/* sqUnixDirectFB.c -- support for Unix and DirectFB.
 * 
 *   Copyright (C) 1996 1997 1998 1999 2000 2001 Ian Piumarta and individual
 *      authors/contributors listed elsewhere in this file.
 *   All rights reserved.
 *   
 *   This file is part of Unix Squeak.
 * 
 *   This file 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.
 *   
 *   You may use and/or distribute this file ONLY as part of Squeak, under
 *   the terms of the Squeak License as described in `LICENSE' in the base of
 *   this distribution, subject to the following restrictions:
 * 
 *   1. The origin of this software must not be misrepresented; you must not
 *      claim that you wrote the original software.  If you use this software
 *      in a product, an acknowledgment to the original author(s) (and any
 *      other contributors mentioned herein) in the product documentation
 *      would be appreciated but is not required.
 * 
 *   2. This notice may not be removed or altered in any source distribution.
 * 
 *   Using or modifying this file for use in any context other than Squeak
 *   changes these copyright conditions.  Read the file `COPYING' in the base
 *   of the distribution before proceeding with any such use.
 * 
 *   You are STRONGLY DISCOURAGED from distributing a modified version of
 *   this file under its original name without permission.  If you must
 *   change it, rename it first.
 */

/* Author: Ian Piumarta <ian.piumarta@inria.fr>
 *
 * Support for displays deeper than 8 bits contributed by: Kazuki YASUMATSU
 *	<kyasu@crl.fujixerox.co.jp> <Kazuki.Yasumatsu@fujixerox.co.jp>
 *
 * Support for cursor and keypad editing keys based on code contributed by:
 *	Stefan Matthias Aust <sma@kiel.netsurf.de>
 *
 * Support for intelligent visual class selection contributed by:
 *	Bill Cattey <wdc@MIT.EDU>
 *
 * Support for European accented characters in selections, and
 * Support for displays shallower than 8 bits contributed by:
 *	Bert Freudenberg <bert@isg.cs.uni-magdeburg.de>
 *
 * Support for 24bpp TrueColour X display devices contributed by:
 *	Tim Rowledge <tim@sumeru.stanford.edu>
 *
 * Support for OSProcess plugin contributed by:
 *	Dave Lewis <lewis@mail.msen.com> Mon Oct 18 20:36:54 EDT 1999
 *
 * Support for browser plugins contributed by:
 *	Bert Freudenberg <bert@isg.cs.uni-magdeburg.de>
 *
 * BUGS: this file is too long; it should be split into two (Unix vs. X11).
 *	 icon stuff should be removed (window manager's responsibility).
 *	 RCS stuff has been removed.
 */

#include "sq.h"
#include "sqaio.h"

# if 0
/* Nope, no need for these at all. TPR */
#include "FilePlugin.h"			/* sqFileInit() */
#include "JoystickTabletPlugin.h"	/* joystickInit() */
# endif

/* OS_TYPE may be set in configure.in and passed via the Makefile */

#ifndef OS_TYPE
# ifdef UNIX
#   define OS_TYPE "unix"
# else
#  define OS_TYPE "unknown"
# endif
#endif

#include <stdio.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/utsname.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>

#ifdef HAVE_SYS_SELECT_H
# include <sys/select.h>
#endif

#ifndef HEADLESS
# include <directfb.h>
#endif

#if defined(HAVE_LIBDL)
# undef USE_INTERNAL_IMAGE
# include <dlfcn.h>
# if !defined(NDEBUG)
#   define NDEBUG
# endif
# include <assert.h>
# include "zipio.h"
#endif

/*** Variables -- Imported from Virtual Machine ***/
extern unsigned char *memory;
extern int interruptKeycode;
extern int interruptPending;
extern int interruptCheckCounter;
extern int savedWindowSize;

/*** Variables -- image and path names ***/
#define IMAGE_NAME_SIZE MAXPATHLEN

static char shortImageName[MAXPATHLEN+1];	/* image name */
char        imageName[MAXPATHLEN+1];		/* full path to image */
static char vmPath[MAXPATHLEN+1];		/* full path to image directory */
static char vmName[MAXPATHLEN+1];		/* full path to vm */

#define DefaultHeapSize		50	/* megabytes */

int initialHeapSize= DefaultHeapSize * 1024 * 1024;

/*** Variables -- globals for access from pluggable primitives ***/
int    argCnt= 0;
char **argVec= 0;
char **envVec= 0;

/* Pointers to arguments for VM.  Used by getAttributeIntoLength(). */
int    vmArgCnt= 0;
char **vmArgVec= 0;

/* Pointers to arguments for Squeak.  Used by getAttributeIntoLength(). */
int    squeakArgCnt= 0;
char **squeakArgVec= 0;

#if defined(USE_INTERNAL_IMAGE)
unsigned char *internalImage=    0;	/* non-zero means use internal image */
unsigned char *internalImagePtr= 0;	/* non-zero means plain image */
z_stream      *internalZStream=  0;	/* non-zero means gzipped image */
#endif

#undef USE_ITIMER			/* severely confuses GNU's profil() */

#ifdef USE_ITIMER
unsigned int	lowResMSecs= 0;
#define	LOW_RES_TICK_MSECS	20	/* 1/50 second resolution */
#endif

#ifndef HEADLESS
IDirectFB               *directFB = 0;
IDirectFBSurface        *displaySurface = 0;
IDirectFBSurface        *cursorSurface = 0;
int cursorX = 0;
int cursorY = 0;
IDirectFBSurface        *cachedSurface = 0;
int                      cachedBitmap = 0;
int                      cachedWidth = 0;
int                      cachedHeight = 0;
int                      cachedDepth = 0;
IDirectFBEventBuffer    *deviceEventBuffer = 0;
int mouseX = 0;
int mouseY = 0;
int dirtyL = 0;
int dirtyT = 0;
int dirtyR = 0;
int dirtyB = 0;
int rawMouseX = 0;
int rawMouseY = 0;
int calibrateXA = 1;
int calibrateXB = 0;
int calibrateXC = 0;
int calibrateXD = 1;
int calibrateXE = 0;
int calibrateYA = 0;
int calibrateYB = 1;
int calibrateYC = 0;
int calibrateYD = 1;
int calibrateYE = 0;
int		 scrW= 0;               /* the size of the whole screen */
int		 scrH= 0;

int		 stBitsPerPixel = 0;
int		 stBytesPerPixel = 0;
unsigned int	 stColors[256];
int		 stRNMask, stGNMask, stBNMask;
int		 stRShift, stGShift, stBShift;
int		 headless= 0;
int	 mouseNeedsCalibration = 0;
int      rotateScreen = 0;
int      noEvents = 0;
#else
/* some vars need setting for headless */
int		 noEvents= 1;		/* 1 to disable new event handling */
/* XXX is there a reason to turn off event-based input?  what's it matter?
   feel free to replace this query with an explanation :)  -lex */

#endif  /* !HEADLESS */

/* events */

int inputEventSemaIndex= 0;
#define IEB_SIZE	 64

sqInputEvent inputEventBuffer[IEB_SIZE];
int iebIn=  0;	/* next IEB location to write */
int iebOut= 0;	/* next IEB location to read  */

int		 asmAlign= 1;
int		 noJitter= 1;
int		 withSpy= 0;
int		 noTitle= 0;
struct timeval	 startUpTime;

#ifndef HEADLESS

/*** Variables -- Event Recording ***/
#define KEYBUF_SIZE 64

int keyBuf[KEYBUF_SIZE];	/* circular buffer */
int keyBufGet= 0;		/* index of next item of keyBuf to read */
int keyBufPut= 0;		/* index of next item of keyBuf to write */
int keyBufOverflows= 0;		/* number of characters dropped */


unsigned stButtons= 0;          /* mouse buttons currently pressed
				   down; the mask bits are described
				   in sq.h as RedButtonBit, etc.  */
unsigned modifiers;             /* the modifiers curretnly pressed down;
				   the mask bits are described in sq.h
				   as CtrlKeyBit, etc. */
unsigned buttonState= 0;        /* the combination of the stButtons
				   and modifiers in the format Squeak
				   expects:

				   buttonState == (stButtons | (modifiers << 3)
				*/


#endif   /* !HEADLESS */



/*** Functions ***/

#ifdef ioMSecs
# undef ioMSecs
#endif

void RecordFullPathForVmName(char *localVmName);
void RecordFullPathForImageName(char *localImageName);
void SetUpTimers(void);
void usage(void);
void imageNotFound(char *imageName);
void ParseArguments(int argc, char **argv, int);
void segv(int ignored);
void keyBufAppend(int keystate);

#ifndef HEADLESS
void SetColorEntry(int index, int red, int green, int blue);
void SetUpColors(void);

# define declareCopyFunction(NAME) \
  void NAME (int *fromImageData, int *toImageData, \
             int width, int height, int pitch, \
             int affectedL, int affectedT, int affectedR, int affectedB)

declareCopyFunction(copyImage8To8);
declareCopyFunction(copyImage8To16);
declareCopyFunction(copyImage8To32);

declareCopyFunction(copyImage16To8);
declareCopyFunction(copyImage16To16);
declareCopyFunction(copyImage16To32);

declareCopyFunction(copyImage32To8);
declareCopyFunction(copyImage32To16);
declareCopyFunction(copyImage32To32);

# undef declareCopyFunction

void SetUpCharmap();
static int translateCode(DFBInputDeviceKeyIdentifier keycode);
static void recordMouseEvent(DFBInputEvent *theEvent);
static void recordKeystrokeEvent(DFBInputEvent *theEvent);
static void recordModifierButtons(void);

int ioMSecs(void);
#endif /*!HEADLESS*/

int strtobkm(const char *str);


time_t convertToSqueakTime(time_t);	/* unix epoch -> Squeak epoch */


/*** VM Home Directory Path ***/

int vmPathSize(void)
{
  return strlen(vmPath);
}

int vmPathGetLength(int sqVMPathIndex, int length)
{
  char *stVMPath= (char *)sqVMPathIndex;
  int count, i;

  count= strlen(vmPath);
  count= (length < count) ? length : count;

  /* copy the file name into the Squeak string */
  for (i= 0; i < count; i++)
    stVMPath[i]= vmPath[i];

  return count;
}

#ifndef HEADLESS

/* Conversion table from DirectFB to Squeak (reversible) */

unsigned char DirectFB_to_Squeak[256] =
{
  0,   1,   2,   3,   4,   5,   6,   7,       /*   0 -   7 */
  8,   9,   13,  11,  12,  10,  14,  15,      /*   8 -  15 */
  16,  17,  18,  19,  20,  21,  22,  23,      /*  16 -  23 */
  24,  25,  26,  27,  28,  29,  30,  31,      /*  24 -  31 */
  32,  33,  34,  35,  36,  37,  38,  39,      /*  32 -  39 */
  40,  41,  42,  43,  44,  45,  46,  47,      /*  40 -  47 */
  48,  49,  50,  51,  52,  53,  54,  55,      /*  48 -  55 */
  56,  57,  58,  59,  60,  61,  62,  63,      /*  56 -  63 */
  64,  65,  66,  67,  68,  69,  70,  71,      /*  64 -  71 */
  72,  73,  74,  75,  76,  77,  78,  79,      /*  72 -  79 */
  80,  81,  82,  83,  84,  85,  86,  87,      /*  80 -  87 */
  88,  89,  90,  91,  92,  93,  94,  95,      /*  88 -  95 */
  96,  97,  98,  99, 100, 101, 102, 103,      /*  96 - 103 */
  104, 105, 106, 107, 108, 109, 110, 111,     /* 104 - 111 */
  112, 113, 114, 115, 116, 117, 118, 119,     /* 112 - 119 */
  120, 121, 122, 123, 124, 125, 126, 127,     /* 120 - 127 */
  196, 197, 165, 201, 209, 247, 220, 225,     /* 128 - 135 */
  224, 226, 228, 227, 198, 176, 170, 248,     /* 136 - 143 */
  213, 206, 195, 207, 211, 212, 210, 219,     /* 144 - 151 */
  218, 221, 246, 245, 250, 249, 251, 252,     /* 152 - 159 */
  160, 193, 162, 163, 223, 180, 182, 164,     /* 160 - 167 */
  172, 169, 187, 199, 194, 173, 168, 255,     /* 168 - 175 */
  161, 177, 178, 179, 171, 181, 166, 183,     /* 176 - 183 */
  184, 185, 188, 200, 186, 189, 202, 192,     /* 184 - 191 */
  203, 231, 229, 204, 128, 129, 174, 130,     /* 192 - 199 */
  233, 131, 230, 232, 237, 234, 235, 236,     /* 200 - 207 */
  208, 132, 241, 238, 239, 205, 133, 215,     /* 208 - 215 */
  175, 244, 242, 243, 134, 217, 222, 167,     /* 216 - 223 */
  136, 135, 137, 139, 138, 140, 190, 141,     /* 224 - 231 */
  143, 142, 144, 145, 147, 146, 148, 149,     /* 232 - 239 */
  240, 150, 152, 151, 153, 155, 154, 214,     /* 240 - 247 */
  191, 157, 156, 158, 159, 253, 254, 216,     /* 248 - 255 */
};

unsigned char Squeak_to_DirectFB[256];

void SetUpCharmap()
{
  int i;
  for(i=0; i<256; i++)
    Squeak_to_DirectFB[DirectFB_to_Squeak[i]]= i;
}

/*** DirectFB-related Functions ***/

/* macro for a safe call to DirectFB functions */
#define DFBCHECK(call)                                                    \
     {                                                                    \
          DFBResult error = call;                                         \
          if (error != DFB_OK) {                                          \
               fprintf (stderr, "%s <%d>:\n\t", __FILE__, __LINE__);      \
               DirectFBErrorFatal (# call, error);                        \
          }                                                               \
     }

static void calibrateCorner (int cornerX, int cornerY, int * headX, int * headY)
{
  DFBEvent event;
  int arrowX, arrowY;

  displaySurface -> SetColor (displaySurface, 0xFF, 0xFF, 0xFF, 0xFF);
  displaySurface -> FillRectangle (displaySurface, 0, 0, scrW, scrH);
  displaySurface -> SetColor (displaySurface, 0, 0, 0, 0xFF);
#if 0
  displaySurface -> DrawString (displaySurface,
                                "Drag your mouse along the arrow.", -1,
                                0, scrH - scrH / 24, DSTF_CENTER);
#endif

  arrowX = cornerX ? - scrW / 8 : scrW / 8;
  arrowY = cornerY ? - scrH / 8 : scrH / 8;
  cornerX *= scrW;
  cornerY *= scrH;

  displaySurface -> DrawLine (displaySurface,
                              cornerX, cornerY,
                              cornerX + arrowX, cornerY + arrowY);
  displaySurface -> FillTriangle (displaySurface,
                                  cornerX, cornerY,
                                  cornerX + arrowX / 4, cornerY,
                                  cornerX, cornerY + arrowY / 4);

  * headX = 0;
  * headY = 0;
  
  for (;;)
  {
    deviceEventBuffer -> WaitForEvent (deviceEventBuffer);
    while (deviceEventBuffer -> GetEvent (deviceEventBuffer, & event) == DFB_OK)
    if (event.clazz == DFEC_INPUT)
    switch (event.input.type)
    {
    case DIET_BUTTONRELEASE:
      return;

    case DIET_AXISMOTION:
      if (event.input.axis == DIAI_X)
      {
        if (event.input.flags & DIEF_AXISREL)
          * headX += event.input.axisrel;
        else
          * headX = event.input.axisabs;
      }
      else
      if (event.input.axis == DIAI_Y)
      {
        if (event.input.flags & DIEF_AXISREL)
          * headY += event.input.axisrel;
        else
          * headY = event.input.axisabs;
      }
      break;
    }
  }
}

static void calibrateMouse (void)
{
  int top, left, bottom, right, x, y;
#if 0
  IDirectFBFont *font;
  DFBFontDescription description;

  description.flags = DFDESC_HEIGHT;
  description.height = scrH / 24;

  DFBCHECK (directFB -> CreateFont (directFB, FONT, & description, & font));
  DFBCHECK (displaySurface -> SetFont (displaySurface, font));
#endif

  calibrateCorner (0, 0, & left, & top);
  calibrateCorner (1, 0, & x, & y);
  calibrateCorner (1, 1, & right, & bottom);

#if 0
  font -> Release (font);
#endif

  if (left < right)
  {
     calibrateXA = 1;
     calibrateXB = 0;
     calibrateXC = 0;
     calibrateXD = right - left + 1;
     calibrateXE = - left;
  }
  else
  {
     calibrateXA = - 1;
     calibrateXB = 0;
     calibrateXC = 1;
     calibrateXD = left - right + 1;
     calibrateXE = right;
  }

  calibrateXE = - left;

  if (top < bottom)
  {
     calibrateYA = 0;
     calibrateYB = 1;
     calibrateYC = 0;
     calibrateYD = bottom - top + 1;
     calibrateYE = - top;
  }
  else
  {
     calibrateYA = 0;
     calibrateYB = - 1;
     calibrateYC = 1;
     calibrateYD = top - bottom + 1;
     calibrateYE = bottom;
  }

  if (abs (x - left) < abs (right - left) / 2)
  {
     int oldXA = calibrateXA,
         oldXB = calibrateXB,
         oldXC = calibrateXC,
         oldXD = calibrateXD,
	 oldXE = calibrateXE;

     calibrateXA = calibrateYA;
     calibrateXB = calibrateYB;
     calibrateXC = calibrateYC;
     calibrateXD = calibrateYD;
     calibrateXE = calibrateYE;
     
     calibrateYA = oldXA;
     calibrateYB = oldXB;
     calibrateYC = oldXC;
     calibrateYD = oldXD;
     calibrateYE = oldXE;
  }
}

static DFBEnumerationResult selectVideoMode (unsigned int width, unsigned int height, unsigned int bpp, void *data)
{
  switch (bpp)
  { 
  case 8:
  case 15:
  case 16:
  case 32:
    break;
    
  default:
    return DFENUM_OK;
  }

  if ((bpp > stBitsPerPixel &&
        (bpp != 16 || stBitsPerPixel != 15)) ||
      (bpp == stBitsPerPixel &&
        (width > scrW || height > scrH)) ||
      (bpp == 15 && stBitsPerPixel == 16))
  {
    scrW = width;
    scrH = height;
    stBitsPerPixel = bpp;
  }
 
  return DFENUM_OK;
}

int openDisplay(int argc, char **argv)
{
  if (!headless && !directFB) {
    SetUpCharmap();
    DFBCHECK (DirectFBInit (& argc, & argv));
    DFBCHECK (DirectFBCreate (& directFB));
    DFBCHECK (directFB -> CreateEventBuffer (directFB, DICAPS_KEYS | DICAPS_BUTTONS, & deviceEventBuffer));
    DFBCHECK (directFB -> SetCooperativeLevel (directFB, DFSCL_FULLSCREEN));
    DFBCHECK (directFB -> EnumVideoModes (directFB, (void *) selectVideoMode, 0));
    DFBCHECK (directFB -> SetVideoMode (directFB, scrW, scrH, stBitsPerPixel));

    {
      DFBSurfaceDescription description;
      DFBRegion clipRegion;
      memset (& description, 0, sizeof (DFBSurfaceDescription));

      description.flags = DSDESC_CAPS;
      description.caps = DSCAPS_PRIMARY | DSCAPS_VIDEOONLY;

      DFBCHECK (directFB -> CreateSurface (directFB, & description, & displaySurface));

      clipRegion.x1 = 0;
      clipRegion.y1 = 0;
      clipRegion.x2 = scrW;
      clipRegion.y2 = scrH;

      displaySurface -> SetClip (displaySurface, & clipRegion);
    }

    if (mouseNeedsCalibration)
      calibrateMouse ();

    SetUpColors();
  }
  return 0;
}

int closeDisplay()
{
  if (directFB) {
    if (cursorSurface)
      cursorSurface -> Release (cursorSurface);
    displaySurface -> Release (displaySurface);
    deviceEventBuffer -> Release (deviceEventBuffer);
    directFB -> Release (directFB);
    directFB = 0;
  }
  return 0;
}

#endif /*!HEADLESS*/


/*** event handling ***/


/* set asynchronous input event semaphore
 */
int ioSetInputSemaphore(int semaIndex)
{
  if ((semaIndex == 0) || (noEvents == 1))
    {
      success(false);
    }
  else
    {
      inputEventSemaIndex= semaIndex;
    }
  return true;
}


#define iebEmptyP()	(iebIn == iebOut)
#define iebAdvance(P)	(P= ((P + 1) % IEB_SIZE))


/* retrieve the next input event from the OS
 */
int ioGetNextEvent(sqInputEvent *evt)
{
  if (iebEmptyP()) ioProcessEvents();
  if (iebEmptyP()) return false;
  *evt= inputEventBuffer[iebOut];
  iebAdvance(iebOut);
  return true;
}

#ifndef HEADLESS
static sqInputEvent *allocateInputEvent(int eventType)
{
  sqInputEvent *evt= &inputEventBuffer[iebIn];
  iebAdvance(iebIn);
  if (iebEmptyP())
    {
      /* overrun: discard oldest event */
      iebAdvance(iebOut);
    }
  evt->type= eventType;
  evt->timeStamp= ioMSecs();
  return evt;
}

#define allocateMouseEvent() ( \
  (sqMouseEvent *)allocateInputEvent(EventTypeMouse) \
)

#define allocateKeyboardEvent() ( \
  (sqKeyboardEvent *)allocateInputEvent(EventTypeKeyboard) \
)

static void signalInputEvent() 
{
  if(inputEventSemaIndex > 0)
    signalSemaphoreWithIndex(inputEventSemaIndex);
}

/* a modified copy of fullDisplayUpdate() that redraws
   only the damaged parts of the window according to each
   expose event on the queue.
   Note: if the format of Form or Bitmap changes, or if
   the special object index of Display is changed, this
   version of the code WILL FAIL!  Otherwise it is to be
   preferred.
*/
static void redrawDisplay(int l, int r, int t, int b)
{
  extern int lengthOf(int);
  extern int displayObject(void);
# define longAt(i) (*((int *) (i)))
  int displayObj= displayObject();
  if ((((((unsigned)(longAt(displayObj))) >> 8) & 15) <= 4) &&
      ((lengthOf(displayObj)) >= 4))
    {
      int dispBits= longAt((displayObj + 4) + (0 * 4));
      int w= fetchIntegerofObject(1, displayObj);
      int h= fetchIntegerofObject(2, displayObj);
      int d= fetchIntegerofObject(3, displayObj);
      int dispBitsIndex= dispBits + 4;
      ioShowDisplay(dispBitsIndex, w, h, d, l, r, t, b);
    }
# undef longAt
}

/* record a mouse event.  This handles button presses, button releases, and motion notify. */
static void recordMouseEvent(DFBInputEvent *theEvent) 
{
  if (theEvent -> type == DIET_AXISMOTION)
  {
    if (dirtyL == dirtyR)
    {
       dirtyL = mouseX - cursorX;
       dirtyR = mouseX - cursorX + 16;
       dirtyT = mouseY - cursorY;
       dirtyB = mouseY - cursorY + 16;
    }

    if (theEvent -> axis == DIAI_X)
    {
      if (theEvent -> flags & DIEF_AXISREL)
	rawMouseX += theEvent -> axisrel;
      else
	rawMouseX = theEvent -> axisabs;
    }
    else
    if (theEvent -> axis == DIAI_Y)
    {
      if (theEvent -> flags & DIEF_AXISREL)
	rawMouseY += theEvent -> axisrel;
      else
        rawMouseY = theEvent -> axisabs;
    }

    mouseX = calibrateXC * (scrW - 1) + (rawMouseX * calibrateXA + rawMouseY * calibrateXB + calibrateXE) * scrW / calibrateXD;
    mouseY = calibrateYC * (scrH - 1) + (rawMouseX * calibrateYA + rawMouseY * calibrateYB + calibrateYE) * scrH / calibrateYD;
   
    if (mouseX < 0)
      mouseX = 0;
    else
    if (mouseX >= scrW)
      mouseX = scrW - 1;
    
    if (mouseY < 0)
      mouseY = 0;
    else
    if (mouseY >= scrH)
      mouseY = scrH - 1;
  }
  else
  {
    int button;

    switch (theEvent -> button)
    {
    case DIBI_LEFT:
      button = RedButtonBit;
      break;

    case DIBI_MIDDLE:
      button = YellowButtonBit;
      break;

    case DIBI_RIGHT:
      button = BlueButtonBit;
      break;
    }

    if (theEvent -> type == DIET_BUTTONPRESS)
      stButtons |= button;
    else
      stButtons &= ~ button;
  }

  recordModifierButtons ();

  if(inputEventSemaIndex != 0) {
    sqMouseEvent *evt= allocateMouseEvent();
    if (rotateScreen)
    {
      evt->x = mouseY;
      evt->y = scrW - mouseX - 1;
    }
    else
    {
      evt->x = mouseX;
      evt->y = mouseY;
    }
    evt->buttons= stButtons;
    evt->modifiers= modifiers;
    evt->reserved1= 0;
    evt->reserved2= 0;
    
    signalInputEvent();
  }
}


/* record a keystroke event, whether it is a release or a press */
static void recordKeystrokeEvent(DFBInputEvent *theEvent) 
{
  int charCode= 0;       /* the character code for the key, in Squeak's encoding */
  int keystate= 0;       /* the combined character code and modifiers */
 
  if((theEvent -> flags & DIEF_MODIFIERS) && (theEvent -> flags & DIEF_LOCKS))
  {
    modifiers = 0;
    if (theEvent -> modifiers & DIMK_SHIFT) modifiers |= ShiftKeyBit;
    if (theEvent -> locks & DILS_CAPS) modifiers |= ShiftKeyBit;
    if (theEvent -> modifiers & DIMK_CTRL) modifiers |= CtrlKeyBit;
    if (theEvent -> modifiers & DIMK_ALT) modifiers |= CommandKeyBit;
  }

  recordModifierButtons ();

  charCode = translateCode (theEvent -> keycode);
  if (charCode < 0)
  {
     if (!theEvent -> key_ascii)
       return;

     charCode = theEvent -> key_ascii;
  }

  /* Squeak uses the Macintosh 8-bit character encoding */
  if (charCode >= 128)
    charCode= DirectFB_to_Squeak[charCode];

  keystate= charCode | (modifiers << 8);

  if (keystate == interruptKeycode)
    {
      /* Treat the interrupt key specially; don't just pass it into the image */
      if(theEvent->type==DIET_KEYPRESS) {
	interruptPending= true;
	interruptCheckCounter= 0;
      }
      
      return;
    }

  keyBufAppend(keystate);


  if(inputEventSemaIndex != 0)     {
    int codes[2];
    int numCodes;
    int i;

    if(theEvent->type==DIET_KEYPRESS) {
      codes[0] = EventKeyDown;
      codes[1] = EventKeyChar;
      numCodes=2;
    }
    else {
      codes[0] = EventKeyUp;
      numCodes=1;
    }

    for(i=0; i<numCodes; i++) {
      sqKeyboardEvent *evt= allocateKeyboardEvent();
    
      evt->charCode= charCode;
      evt->pressCode= codes[i];
      evt->modifiers= modifiers;
      evt->reserved1=
	evt->reserved2=
	evt->reserved3= 0;
    }
  }
  signalInputEvent();
}


void SetColorEntry(int index, int red, int green, int blue)
{
  unsigned int r, g, b;

  if (index >= 256)
    return;

  r= red;
  g= green;
  b= blue;

  stColors[index]= (((r>>(16-stRNMask))<<stRShift) |
                	((g>>(16-stGNMask))<<stGShift) |
			((b>>(16-stBNMask))<<stBShift));
}

void SetUpColors(void)
{
  DFBSurfacePixelFormat pixelFormat;

  displaySurface -> GetPixelFormat (displaySurface, & pixelFormat);
 
  switch (pixelFormat)
  {
  case DSPF_RGB332:
    stRNMask = 3;
    stRShift = 5;
    stGNMask = 3;
    stGShift = 2;
    stBNMask = 2;
    stBShift = 0;
    break;

  case DSPF_RGB15:
  case DSPF_RGB16:
    stRNMask = 5;
    if (pixelFormat == DSPF_RGB15)
    {
      stRShift = 10;
      stGNMask = 5;
    }
    else
    {
      stRShift = 11;
      stGNMask = 6;
    }
    stGShift = 5;
    stBNMask = 5;
    stBShift = 0;
    break;

  case DSPF_RGB24:
  case DSPF_RGB32:
  case DSPF_ARGB:
    stRNMask = 8;
    stRShift = 16;
    stGNMask = 8;
    stGShift = 8;
    stBNMask = 8;
    stBShift = 0;
    break;

  default:
    fprintf (stderr, "Display surface has unsupported pixel format.\n");
    exit (1);
    return;
  }

  stBytesPerPixel = DFB_BYTES_PER_PIXEL (pixelFormat);

  /* 1-bit colors (monochrome) */
  SetColorEntry(0, 65535, 65535, 65535);	/* white or transparent */
  SetColorEntry(1,     0,     0,     0);	/* black */

	/* additional colors for 2-bit color */
  SetColorEntry(2, 65535, 65535, 65535);	/* opaque white */
  SetColorEntry(3, 32768, 32768, 32768);	/* 1/2 gray */

	/* additional colors for 4-bit color */
  SetColorEntry( 4, 65535,     0,     0);	/* red */
  SetColorEntry( 5,     0, 65535,     0);	/* green */
  SetColorEntry( 6,     0,     0, 65535);	/* blue */
  SetColorEntry( 7,     0, 65535, 65535);	/* cyan */
  SetColorEntry( 8, 65535, 65535,     0);	/* yellow */
  SetColorEntry( 9, 65535,     0, 65535);	/* magenta */
  SetColorEntry(10,  8192,  8192,  8192);	/* 1/8 gray */
  SetColorEntry(11, 16384, 16384, 16384);	/* 2/8 gray */
  SetColorEntry(12, 24576, 24576, 24576);	/* 3/8 gray */
  SetColorEntry(13, 40959, 40959, 40959);	/* 5/8 gray */
  SetColorEntry(14, 49151, 49151, 49151);	/* 6/8 gray */
  SetColorEntry(15, 57343, 57343, 57343);	/* 7/8 gray */

	/* additional colors for 8-bit color */
	/* 24 more shades of gray (does not repeat 1/8th increments) */
  SetColorEntry(16,  2048,  2048,  2048);	/*  1/32 gray */
  SetColorEntry(17,  4096,  4096,  4096);	/*  2/32 gray */
  SetColorEntry(18,  6144,  6144,  6144);	/*  3/32 gray */
  SetColorEntry(19, 10240, 10240, 10240);	/*  5/32 gray */
  SetColorEntry(20, 12288, 12288, 12288);	/*  6/32 gray */
  SetColorEntry(21, 14336, 14336, 14336);	/*  7/32 gray */
  SetColorEntry(22, 18432, 18432, 18432);	/*  9/32 gray */
  SetColorEntry(23, 20480, 20480, 20480);	/* 10/32 gray */
  SetColorEntry(24, 22528, 22528, 22528);	/* 11/32 gray */
  SetColorEntry(25, 26624, 26624, 26624);	/* 13/32 gray */
  SetColorEntry(26, 28672, 28672, 28672);	/* 14/32 gray */
  SetColorEntry(27, 30720, 30720, 30720);	/* 15/32 gray */
  SetColorEntry(28, 34815, 34815, 34815);	/* 17/32 gray */
  SetColorEntry(29, 36863, 36863, 36863);	/* 18/32 gray */
  SetColorEntry(30, 38911, 38911, 38911);	/* 19/32 gray */
  SetColorEntry(31, 43007, 43007, 43007);	/* 21/32 gray */
  SetColorEntry(32, 45055, 45055, 45055);	/* 22/32 gray */
  SetColorEntry(33, 47103, 47103, 47103);	/* 23/32 gray */
  SetColorEntry(34, 51199, 51199, 51199);	/* 25/32 gray */
  SetColorEntry(35, 53247, 53247, 53247);	/* 26/32 gray */
  SetColorEntry(36, 55295, 55295, 55295);	/* 27/32 gray */
  SetColorEntry(37, 59391, 59391, 59391);	/* 29/32 gray */
  SetColorEntry(38, 61439, 61439, 61439);	/* 30/32 gray */
  SetColorEntry(39, 63487, 63487, 63487);	/* 31/32 gray */

  /* The remainder of color table defines a color cube with six steps
     for each primary color. Note that the corners of this cube repeat
     previous colors, but simplifies the mapping between RGB colors and
     color map indices. This color cube spans indices 40 through 255.
     */
  {
    int r, g, b;

    for (r= 0; r < 6; r++)
      for (g= 0; g < 6; g++)
	for (b= 0; b < 6; b++)
	  {
	    int i= 40 + ((36 * r) + (6 * b) + g);
	    if (i > 255) error("index out of range in color table compuation");
	    SetColorEntry(i, (r * 65535) / 5, (g * 65535) / 5, (b * 65535) / 5);
	  }
  }
}

/*** Event Recording Functions ***/

/* translate special keys; return -1 if the key isn't recognized */
static int translateCode(DFBInputDeviceKeyIdentifier keyCode)
{
  switch (keyCode)
    {
    case DIKC_LEFT:	return 28;
    case DIKC_UP:	return 30;
    case DIKC_RIGHT:	return 29;
    case DIKC_DOWN:	return 31;
    case DIKC_INSERT:	return  5;
    case DIKC_PAGEUP:	return 11;
    case DIKC_PAGEDOWN:	return 12;
    case DIKC_HOME:	return  1;
    case DIKC_END:	return  4;
    case DIKC_BACKSPACE:  return  8;
    case DIKC_DELETE:   return 127;
     
    default:		return -1;
    }
  /*NOTREACHED*/
}


void keyBufAppend(int keystate)
{
  /* bug: this should be rewritten to cope with nConv > 1 */
  keyBuf[keyBufPut]= keystate;
  keyBufPut= (keyBufPut + 1) % KEYBUF_SIZE;
  if (keyBufGet == keyBufPut)
    {
      /* buffer overflow; drop the last character */
      keyBufGet= (keyBufGet + 1) % KEYBUF_SIZE;
      keyBufOverflows++;
    }
}

static void recordModifierButtons(void)
{
  /* remap the red button, if Control or Meta is pressed */
  if ((stButtons == RedButtonBit) && (modifiers & CtrlKeyBit))
  {
    /* ctrl-red is mapped to the yellow button */
    stButtons= YellowButtonBit;
    modifiers &= ~CtrlKeyBit;
  }
    
  if ((stButtons == RedButtonBit) && (modifiers & CommandKeyBit)) {
    /* meta-red is mapped to the blue button */
    stButtons= BlueButtonBit;
    modifiers &= ~CommandKeyBit;
  }

  /* combine the button state in the format Squeak likes */
  buttonState= (modifiers << 3) | (stButtons & 0x7);
}


#endif /*!HEADLESS*/


/*** I/O Primitives ***/


int ioFormPrint(int bitsAddr, int width, int height, int depth,
		double hScale, double vScale, int landscapeFlag)
{
#ifdef HEADLESS
  return false;
#else
  FILE *ppm;
  float scale;  /* scale to use with pnmtops */
  char printCommand[1000];
  unsigned int *form32;  /* the form data, in  32 bits */
  

  /* convert the form to 32 bits */
  form32 = malloc(width * height * 4);
  if(form32 == NULL) {
    fprintf(stderr, "not enough memory\n");
    return false;
  }
  switch(depth) {
  case 8:
    copyImage8To32((int *) bitsAddr, (int *) form32,
		   width, height, width,
		   1, 1, width, height);
    break;

  case 16:
    copyImage16To32((int *) bitsAddr, (int *) form32,
		    width, height, width,
		    1, 1, width, height);
    break;
  case 32:
    copyImage32To32((int *) bitsAddr, (int *) form32,
		    width, height, width,
		    1, 1, width, height);
    break;
  default:
    fprintf(stderr, "error in formPrint: unhandled form depth %d\n", depth);
    free(form32);
    return false;
  }
  
  /* pick a scale. unfortunately, pnmtops requires the same scale
     horizontally and vertically */
  if(hScale < vScale)
    scale= hScale;
  else
    scale= vScale;


  /* assemble the command for printing.  pnmtops is part of "netpbm",
     a widely-available set of tools that eases image manipulation */ 
  snprintf(printCommand, sizeof(printCommand),
	   "pnmtops -scale %f %s | lpr",
	   scale,
	   (landscapeFlag ? "-turn" : "-noturn"));


  
  /* open a pipe to print through */
  if ((ppm= popen(printCommand, "w")) == 0) {
    free(form32);
    return false;
  }
  
  
  /* print the PPM magic number */
  fprintf(ppm, "P3\n%d %d 255\n", width, height);

  /* write the pixmap */
  {
    int y;
    for (y= 0; y < height; ++y) {
      int x;
      for (x= 0; x < width; ++x) {
	int pixel, red, green, blue;

	pixel= form32[y*width + x];
	red=   (pixel >> 16) & 0xFF;
	green= (pixel >> 8)  & 0xFF;
	blue=  (pixel >> 0)  & 0xFF;

	fprintf(ppm, "%d %d %d\n", red, green, blue);
      }
    }
  }
    
      
  free(form32);
  pclose(ppm);
  return true;
#endif  /* HEADLESS */
}


int ioBeep(void)
{
  return 0;
}


int ioGetButtonState(void)
{
  ioProcessEvents();  /* process all pending events */
#ifndef HEADLESS
  return buttonState;
#else
  return 0;
#endif
}

int ioGetKeystroke(void)
{
#ifndef HEADLESS
  int keystate;
#endif

  ioProcessEvents();  /* process all pending events */
#ifdef HEADLESS
  return -1;  /* keystroke buffer is empty */
#else
  if (keyBufGet == keyBufPut)
    return -1;  /* keystroke buffer is empty */

  keystate= keyBuf[keyBufGet];
  keyBufGet= (keyBufGet + 1) % KEYBUF_SIZE;

  return keystate;
#endif
}


int ioLowResMSecs(void)
{
#ifdef USE_ITIMER
  return lowResMSecs;
#else
  return ioMSecs();
#endif
}


int ioMSecs(void)
{
  struct timeval now;
  gettimeofday(&now, 0);
  if ((now.tv_usec-= startUpTime.tv_usec) < 0) {
    now.tv_usec+= 1000000;
    now.tv_sec-= 1;
  }
  now.tv_sec-= startUpTime.tv_sec;
  return (now.tv_usec / 1000 + now.tv_sec * 1000);
}

int ioMicroMSecs(void)
{
  /* return the highest available resolution of the millisecond clock */
  return ioMSecs();	/* this already to the nearest millisecond */
}

int ioRelinquishProcessorForMicroseconds(int microSeconds)
{
  aioPoll(microSeconds);
  return microSeconds;
}

int ioMousePoint(void)
{
  ioProcessEvents();  /* process all pending events */
  /* x is high 16 bits; y is low 16 bits */
#ifndef HEADLESS
  if (rotateScreen)
    return (mouseY << 16) | (scrW - mouseX - 1);
  else
    return (mouseX << 16) | mouseY;
#else
  return 0;
#endif
}

int ioPeekKeystroke(void)
{
#ifdef HEADLESS
  ioProcessEvents();  /* process all pending events */
  return -1;  /* keystroke buffer is empty */
#else
  int keystate;

  ioProcessEvents();  /* process all pending events */

  if (keyBufGet == keyBufPut)
    return -1;  /* keystroke buffer is empty */

  keystate= keyBuf[keyBufGet];
  return keystate;
#endif
}

int ioProcessEvents(void)
{
#ifndef HEADLESS
  DFBEvent event;

  dirtyL = 0;
  dirtyT = 0;
  dirtyR = 0;
  dirtyB = 0;

  while (deviceEventBuffer -> GetEvent (deviceEventBuffer, & event) == DFB_OK)
  {
    if (event.clazz == DFEC_INPUT)
    switch (event.input.type)
    {
    case DIET_KEYPRESS:
    case DIET_KEYRELEASE:
      recordKeystrokeEvent (& event.input);
      break;

    case DIET_BUTTONPRESS:
    case DIET_BUTTONRELEASE:
    case DIET_AXISMOTION:
      recordMouseEvent (& event.input);
      break;
    }
  }

  if (dirtyL != dirtyR)
  {
    if (rotateScreen)
      redrawDisplay (dirtyT, dirtyB, scrW - dirtyR, scrW - dirtyL);
    else
      redrawDisplay (dirtyL, dirtyR, dirtyT, dirtyB);
  }

#endif
  /* This code used to delay before processing events, but that
     behavior is pretty well taken care of if you are in morphic.
     Plus, aioPoll() is just a select() call, anyway -- it should be
     pretty fast if there are no events.  */
  aioPoll(0);
  return 0;
}


/* returns the depth of the Squeak window */
int ioScreenDepth(void)
{
#ifdef HEADLESS
  return 1;
#else
  if (!directFB)
    return 1;

  return stBytesPerPixel * 8;
#endif
}


/* returns most recently known size of the Squeak window */
/* the return is encoded as:
      (width << 16) | height
*/
int ioScreenSize(void)
{
#ifndef HEADLESS
  if (!headless && directFB)
  {
    if (rotateScreen)
      return (scrH << 16) | scrW;
    else
      return (scrW << 16) | scrH;
  }
#endif
  return (64 << 16) | 64;
}


time_t convertToSqueakTime(time_t unixTime)
{
#ifdef HAVE_TM_GMTOFF
  unixTime+= localtime(&unixTime)->tm_gmtoff;
#else
# ifdef HAVE_TIMEZONE
  unixTime+= ((daylight) * 60*60) - timezone;
# else
#  error: cannot determine timezone correction
# endif
#endif
  /* Squeak epoch is Jan 1, 1901.  Unix epoch is Jan 1, 1970: 17 leap years
     and 52 non-leap years later than Squeak. */
  return unixTime + ((52*365UL + 17*366UL) * 24*60*60UL);
}


/* returns the local wall clock time */
int ioSeconds(void)
{
  return convertToSqueakTime(time(0));
}

int ioSetCursorWithMask(int cursorBitsIndex, int cursorMaskIndex,
			int offsetX, int offsetY)
{
#ifndef HEADLESS
  unsigned int * buffer;
  int pitch, line;
  
  if (cursorMaskIndex == null)
    cursorMaskIndex = cursorBitsIndex;

  if (! cursorSurface)
  {
    DFBSurfaceDescription description;

    memset (& description, 0, sizeof (DFBSurfaceDescription));

    description.flags = DSDESC_WIDTH | DSDESC_HEIGHT | DSDESC_PIXELFORMAT;
    description.width = 16;
    description.height = 16;
    description.pixelformat = DSPF_ARGB;
    
    DFBCHECK (directFB -> CreateSurface (directFB, & description, & cursorSurface));
  }

  cursorX = rotateScreen ? 16 - offsetY : offsetX;
  cursorY = rotateScreen ? offsetX : offsetY;

  DFBCHECK (cursorSurface -> Lock (cursorSurface, DSLF_WRITE, (void **) & buffer, & pitch));
  
  pitch /= 4;

  if (rotateScreen)
    buffer += 15;

  for (line = 0; line < 16; ++ line)
  {
     unsigned short dataLine = (unsigned) checkedLongAt (cursorBitsIndex + line * 4) >> 16,
                    maskLine = (unsigned) checkedLongAt (cursorMaskIndex + line * 4) >> 16;
     int offset;

     for (offset = 0; offset < 16; ++ offset)
     {
       * buffer = ((dataLine & (1 << 15)) ? 0 : 0xFFFFFF) | ((maskLine & (1 << 15)) ? 0xFF << 24 : 0);

       if (rotateScreen)
	 buffer += pitch;
       else
         ++ buffer;

       dataLine <<= 1;
       maskLine <<= 1;
     }
    
     if (rotateScreen)
       buffer -= pitch * 16 + 1;
     else
       buffer += pitch - 16;
  }

  cursorSurface -> Unlock (cursorSurface);

#endif
  return 0;
}


int ioSetCursor(int cursorBitsIndex, int offsetX, int offsetY)
{
  /* Deprecated: forward to new version with explicit mask. */
  ioSetCursorWithMask(cursorBitsIndex, null, offsetX, offsetY);
  return 0;
}

int ioSetFullScreen(int fs)
{
  return 0;
}



int ioForceDisplayUpdate(void)
{
  return 0;
}


int ioShowDisplay(int dispBitsIndex, int width, int height, int depth,
		  int affectedL, int affectedR, int affectedT, int affectedB)
{
#ifndef HEADLESS
  int *buffer, pitch;

  if (!directFB)
    return 0;

  /* Sanitize the input values */
  if (affectedR > width) affectedR = width;
  if (affectedL > width) affectedL = width;
  if (affectedB > height) affectedB = height;
  if (affectedT > height) affectedT = height;
  
  /* quit, if its a trivial region */
  if(affectedL >= affectedR || affectedT >= affectedB)
    return 1;

  if (!(depth == 8 || depth == 16 || depth == 32))
    {
      fprintf(stderr, "depth %d is not supported\n", depth);
      exit(1);
      return 0;
    }

#ifdef WORDS_BIGENDIAN
  if (!rotateScreen && (depth == 16 || depth == 32))
#else
  if (!rotateScreen && depth == 32)
#endif
  {
      DFBRectangle blitArea;

      if (cachedBitmap != dispBitsIndex || 
          cachedWidth != width ||
          cachedHeight != height ||
          cachedDepth != depth)
      {
        DFBSurfaceDescription description;

        cachedBitmap = dispBitsIndex;
        cachedWidth = width;
        cachedHeight = height;
        cachedDepth = depth;

        if (cachedSurface)
          cachedSurface -> Release (cachedSurface);

        memset (& description, 0, sizeof (DFBSurfaceDescription));
	
        description.flags = DSDESC_CAPS | DSDESC_WIDTH | DSDESC_HEIGHT | DSDESC_PIXELFORMAT | DSDESC_PREALLOCATED;
        description.caps = DSCAPS_SYSTEMONLY;
        description.width = width;
        description.height = height;
	
        if (depth == 16)
          description.pixelformat = DSPF_RGB15;
	else
	  description.pixelformat = DSPF_RGB32;
        
        description.preallocated [0].data = (void *) cachedBitmap;
        description.preallocated [0].pitch = width * depth / 8;

        DFBCHECK (directFB -> CreateSurface (directFB, & description, & cachedSurface));
      }
      
      blitArea.x = affectedL;
      blitArea.y = affectedT;
      blitArea.w = affectedR - affectedL;
      blitArea.h = affectedB - affectedT;

      displaySurface -> Blit (displaySurface, cachedSurface, & blitArea, affectedL, affectedT);
      
      displaySurface -> SetColor (displaySurface, 0xFF, 0xFF, 0xFF, 0xFF);
      displaySurface -> SetBlittingFlags (displaySurface, DSBLIT_BLEND_ALPHACHANNEL | DSBLIT_COLORIZE);
      displaySurface -> Blit (displaySurface, cursorSurface, NULL, mouseX - cursorX, mouseY - cursorY);
      displaySurface -> SetBlittingFlags (displaySurface, DSBLIT_NOFX); 
    
      return 0;
  }
 
  if (displaySurface -> Lock (displaySurface,
                              rotateScreen ? DSLF_READ | DSLF_WRITE : DSLF_WRITE,
                  (void **) & buffer, & pitch) != DFB_OK)
    return 0;

  pitch /= stBytesPerPixel;

  if (depth == 8)
  {
    if (stBytesPerPixel == 1)
    {
      copyImage8To8((int *)dispBitsIndex, buffer,
                    width, height, pitch,
                    affectedL, affectedT, affectedR, affectedB);
    }
    else
    if (stBytesPerPixel == 2)
    {
      copyImage8To16((int *)dispBitsIndex, buffer,
		     width, height, pitch,
		     affectedL, affectedT, affectedR, affectedB);
    } 
    else /* stBytesPerPixel == 4 */
    {
      copyImage8To32((int *)dispBitsIndex, buffer,
		     width, height, pitch,
		     affectedL, affectedT, affectedR, affectedB);
    }
  }
  else if (depth == 16)
  {
    if (stBytesPerPixel == 1)
    {
      copyImage16To8((int *)dispBitsIndex, buffer,
		     width, height, pitch,
		     affectedL, affectedT, affectedR, affectedB);
    }
    else 
    if (stBytesPerPixel == 2)
    {
      copyImage16To16((int *)dispBitsIndex, buffer,
                      width, height, pitch,
                      affectedL, affectedT, affectedR, affectedB);
    }
    else /* stBytesPerPixel == 4 */
    {
      copyImage16To32((int *)dispBitsIndex, buffer,
		      width, height, pitch,
		      affectedL, affectedT, affectedR, affectedB);
    }
  }
  else /* depth == 32 */
  {
    if (stBytesPerPixel == 1)
    {
      copyImage32To8((int *)dispBitsIndex, buffer,
		     width, height, pitch,
		     affectedL, affectedT, affectedR, affectedB);
    }
    else 
    if (stBytesPerPixel == 2)
    {
      copyImage32To16((int *)dispBitsIndex, buffer,
                      width, height, pitch,
                      affectedL, affectedT, affectedR, affectedB);
    }
    else /* stBytesPerPixel == 4 */
    {
      copyImage32To32((int *)dispBitsIndex, buffer,
		      width, height, pitch,
        	      affectedL, affectedT, affectedR, affectedB);
    }
  }
  
  displaySurface -> Unlock (displaySurface);
  
  displaySurface -> SetColor (displaySurface, 0xFF, 0xFF, 0xFF, 0xFF);
  displaySurface -> SetBlittingFlags (displaySurface, DSBLIT_BLEND_ALPHACHANNEL | DSBLIT_COLORIZE);
  displaySurface -> Blit (displaySurface, cursorSurface, NULL, 
 		          mouseX - cursorX,
		          mouseY - cursorY);
  displaySurface -> SetBlittingFlags (displaySurface, DSBLIT_NOFX);
#endif /* !HEADLESS */
  return 0;
}


int ioHasDisplayDepth(int i)
{
  switch (i)
    {
    case 8:
    case 16:
    case 32:
      return true;
    }
  return false;
}


int ioSetDisplayMode(int width, int height, int depth, int fullscreenFlag)
{
  fprintf(stderr, "ioSetDisplayMode(%d, %d, %d, %d)\n",
	  width, height, depth, fullscreenFlag);
  return 0;
}


#ifndef HEADLESS

#define pixelsPerLine(width, depth)      (((width) + (32 / (depth) - 1)) & ~ (32 / (depth) - 1))
#define pixelsPerLineRD(width, depth)    ((width) & ~ (32 / (depth) - 1))

#ifdef WORDS_BIGENDIAN
#define PIXEL_INDEX(offset, size) (offset)
#else
#define PIXEL_INDEX(offset, size) ((size) - (offset) - 1)
#endif

#define DEFINE_COPY_IMAGE(name, fromType, fromBpp, toType, toBpp, maskSize) \
void name (int *fromImageData, int *toImageData, int width, int height, int toPitch, \
           int affectedL, int affectedT, int affectedR, int affectedB) \
{ \
  int fromPitch = pixelsPerLine (width, fromBpp); \
  fromType * fromLine = (fromType *) fromImageData + fromPitch * affectedT + pixelsPerLineRD (affectedL, fromBpp), \
           * limit = (fromType *) fromImageData + fromPitch * affectedT + pixelsPerLine (affectedR, fromBpp), \
           * from; \
  toType * toLine, \
         * to; \
  const int rshift = stRNMask - maskSize + stRShift, \
            gshift = stGNMask - maskSize + stGShift, \
            bshift = stBNMask - maskSize + stBShift; \
  int line; \
  if (rotateScreen) \
  { \
    toLine = (toType *) toImageData + toPitch * pixelsPerLineRD (affectedL, fromBpp) + (height - affectedT); \
    for (line = affectedT; line < affectedB; ++ line) \
    { \
      from = fromLine; \
      to = -- toLine; \
      while (from < limit) \
      { \
         ROTATE_PIXEL \
      } \
      limit += fromPitch; \
      fromLine += fromPitch; \
    } \
  } \
  else \
  { \
    toLine = (toType *) toImageData + toPitch * affectedT + pixelsPerLineRD (affectedL, fromBpp); \
    for (line = affectedT; line < affectedB; ++ line) \
    { \
      from = fromLine; \
      to = toLine; \
      while (from < limit) \
      { \
         COPY_PIXEL \
      } \
      limit += fromPitch; \
      fromLine += fromPitch; \
      toLine += toPitch; \
    } \
  } \
}

#define ROTATE_PIXEL \
  * to = stColors [from [PIXEL_INDEX (0, 4)]]; \
  to += toPitch; \
  * to = stColors [from [PIXEL_INDEX (1, 4)]]; \
  to += toPitch; \
  * to = stColors [from [PIXEL_INDEX (2, 4)]]; \
  to += toPitch; \
  * to = stColors [from [PIXEL_INDEX (3, 4)]]; \
  to += toPitch; \
  from += 4;
#define COPY_PIXEL \
  to [0] = stColors [from [PIXEL_INDEX (0, 4)]]; \
  to [1] = stColors [from [PIXEL_INDEX (1, 4)]]; \
  to [2] = stColors [from [PIXEL_INDEX (2, 4)]]; \
  to [3] = stColors [from [PIXEL_INDEX (3, 4)]]; \
  from += 4; \
  to += 4;  
DEFINE_COPY_IMAGE (copyImage8To8, unsigned char, 8, unsigned char, 8, 3)
DEFINE_COPY_IMAGE (copyImage8To16, unsigned char, 8, unsigned short, 16, 3)
DEFINE_COPY_IMAGE (copyImage8To32, unsigned char, 8, unsigned int, 32, 3)
#undef COPY_PIXEL
#undef ROTATE_PIXEL

#define ROTATE_PIXEL \
  * to = MAP_PIXEL (from [PIXEL_INDEX (0, 2)]); \
  to += toPitch; \
  * to = MAP_PIXEL (from [PIXEL_INDEX (1, 2)]); \
  to += toPitch; \
  from += 2;
#define COPY_PIXEL \
  to [1] = MAP_PIXEL (from [PIXEL_INDEX (0, 2)]); \
  to [0] = MAP_PIXEL (from [PIXEL_INDEX (1, 2)]); \
  from += 2; \
  to += 2;
#define MAP_PIXEL(w) \
  ((((w) >> (10+(5-3))) & 0x7) << 5) | \
  ((((w) >> (5+(5-3)))  & 0x7) << 2) | \
   (((w) >> (0+(5-2)))  & 0x3) 
DEFINE_COPY_IMAGE (copyImage16To8, unsigned short, 16, unsigned char, 8, 3)
#undef MAP_PIXEL
#define MAP_PIXEL(w) \
  ((((w) >> 10) & 0x1f) << rshift) | \
  ((((w) >> 5)  & 0x1f) << gshift) | \
   (((w) & 0x1f) << bshift)
DEFINE_COPY_IMAGE (copyImage16To16, unsigned short, 16, unsigned short, 16, 5)
DEFINE_COPY_IMAGE (copyImage16To32, unsigned short, 16, unsigned int, 32, 5)
#undef MAP_PIXEL
#undef COPY_PIXEL
#undef ROTATE_PIXEL

#define ROTATE_PIXEL \
  * to = MAP_PIXEL (* from); \
  to += toPitch; \
  ++ from;
#define COPY_PIXEL \
  * to = MAP_PIXEL (* from); \
  ++ from; \
  ++ to;
#define MAP_PIXEL(w) \
  ((((w) >> (16+(8-3))) & 0x7) << 5) | \
  ((((w) >> ( 8+(8-3))) & 0x7) << 2) | \
   (((w) >> ( 0+(8-2))) & 0x3)
DEFINE_COPY_IMAGE (copyImage32To8, unsigned int, 32, unsigned char, 8, 3)
#undef MAP_PIXEL
#define MAP_PIXEL(w) \
  ((((w) >> 19) & 0x1f) << rshift) | \
  ((((w) >> 11) & 0x1f) << gshift) | \
  ((((w) >>  3) & 0x1f) << bshift)
DEFINE_COPY_IMAGE (copyImage32To16, unsigned int, 32, unsigned short, 16, 5)
#undef MAP_PIXEL
#define MAP_PIXEL(w) \
  ((((w) >> 16) & 0xff) << rshift) | \
  ((((w) >> 8) & 0xff) << gshift) | \
   (((w) & 0xff) << bshift)
DEFINE_COPY_IMAGE (copyImage32To32, unsigned int, 32, unsigned int, 32, 8)
#undef MAP_PIXEL
#undef COPY_PIXEL
#undef ROTATE_PIXEL

#endif

/*** power management ***/


int ioDisablePowerManager(int disableIfNonZero)
{
  return true;
}

/*** VM & Image File Naming ***/


/* copy src filename to target, if src is not an absolute filename,
 * prepend the cwd to make target absolute */
void pathCopyAbs(char *target, const char *src, size_t targetSize)
{
  if (src[0] == '/')
  {
    strcpy(target, src);
  }
  else
  {
    getcwd(target, targetSize);
    strcat(target, "/");
    strcat(target, src);
  }
}

void RecordFullPathForVmName(char *localVmName)
{
  /* get canonical path to vm */
  if (realpath(localVmName, vmPath) == 0)
    pathCopyAbs(vmPath, localVmName, sizeof(vmPath));

  /* truncate vmPath to dirname */
  {
    int i= 0;
    for (i= strlen(vmPath); i >= 0; i--)
      if ('/' == vmPath[i])
	{
	  vmPath[i+1]= '\0';
	  return;
	}
  }
}

void RecordFullPathForImageName(char *localImageName)
{
  struct stat s;
  /* get canonical path to image */
  if ((stat(localImageName, &s) == -1) || (realpath(localImageName, imageName) == 0))
    pathCopyAbs(imageName, localImageName, sizeof(imageName));
}

int imageNameSize(void)
{
  return strlen(imageName);
}

int imageNameGetLength(int sqImageNameIndex, int length)
{
  char *sqImageName= (char *)sqImageNameIndex;
  int count, i;

  count= strlen(imageName);
  count= (length < count) ? length : count;

  /* copy the file name into the Squeak string */
  for (i= 0; i < count; i++)
    sqImageName[i]= imageName[i];

  return count;
}

int imageNamePutLength(int sqImageNameIndex, int length)
{
  char *sqImageName= (char *)sqImageNameIndex;
  int count, i;

  count= (IMAGE_NAME_SIZE < length) ? IMAGE_NAME_SIZE : length;

  /* copy the file name into a null-terminated C string */
  for (i= 0; i < count; i++) imageName[i]= sqImageName[i];
  imageName[count]= 0;

  return count;
}


/*** Timer support ***/


#ifdef USE_ITIMER
void sigalrm(int signum)
{
  lowResMSecs+= LOW_RES_TICK_MSECS;
}
#endif


void SetUpTimers(void)
{
  /* set up the micro/millisecond clock */
  gettimeofday(&startUpTime, 0);
#ifdef USE_ITIMER
  /* set up the low-res (50th second) millisecond clock */
  /* WARNING: all system calls must check for EINTR!!! */
  {
    struct sigaction sa;
    sigset_t ss1, ss2;
    sigemptyset(&ss1);
    sigprocmask(SIG_BLOCK, &ss1, &ss2);
    sa.sa_handler= sigalrm;
    sa.sa_mask= ss2;
#ifdef SA_RESTART
    sa.sa_flags= SA_RESTART;
#else
    sa.sa_flags= 0;	/* assume that we have default BSD behaviour */
#endif
#ifdef __linux__
    sa.sa_restorer= 0;
#endif
    sigaction(SIGALRM, &sa, 0);
  }
  {
    struct itimerval iv;
    iv.it_interval.tv_sec= 0;
    iv.it_interval.tv_usec= LOW_RES_TICK_MSECS * 1000;
    iv.it_value.tv_sec= 0;
    iv.it_value.tv_usec= LOW_RES_TICK_MSECS;
    setitimer(ITIMER_REAL, &iv, 0);
  }
#endif
}



/*** selection handling ***/



int clipboardSize(void)
{
  return 0;
}

int clipboardWriteFromAt(int count, int byteArrayIndex, int startIndex)
{
  return 0;
}

int clipboardReadIntoAt(int count, int byteArrayIndex, int startIndex)
{
  return 0;
}



/*** Profiling ***/



int clearProfile(void) { return 0; }
int dumpProfile(void) { return 0; }
int startProfiling(void) { return 0; }
int stopProfiling(void) { return 0; }



/*** Access to system attributes and command-line arguments ***/



static char *getAttribute(int id)
{
  if (id < 0) {
    /* VM argument */
    if (-id  < vmArgCnt)
      return vmArgVec[-id];
  } else switch (id) {
    case 0: 
      return vmArgVec[0];
    case 1:
      return imageName;
    case 1001:
      /* basic OS type: "unix", "win32", "mac", ... */
      return OS_TYPE;
    case 1002:
      /* specific OS type: "solaris2.5" on unix, "win95" on win32 */
      return VM_HOST_OS;
    case 1003:
      /* processor architecture: "68k", "x86", "PowerPC", ...  */
      return VM_HOST_CPU;
    case 1004:
      return  (char *)interpreterVersion;
    default:
      if ((id-2) < squeakArgCnt)
	return squeakArgVec[id-2];
  }
  success(false);
  return "";
}

int attributeSize(int id)
{
  return strlen(getAttribute(id));
}

int getAttributeIntoLength(int id, int byteArrayIndex, int length)
{
  if (length > 0)
    strncpy((char *)byteArrayIndex, getAttribute(id), length);
  return 0;
}



/*** Command line ***/



static char *progName= 0;

void usage()
{
  printf("Usage: %s [<option>...] [<imageName> [<argument>...]]\n", progName);
  printf("       %s [<option>...] -- [<argument>...]\n\n", progName);
  printf("<option> can be:\n");
  printf("  -align <n>           align functions at n-bytes (jit)\n");
#ifndef HEADLESS
  printf("  -headless            run in headless (no window) mode\n");
  printf("  -calibrate		 calibrate the mouse\n");
  printf("  -rotate		 rotate the screen\n");
  printf("  -noevents            disable event-driven input support\n");
#endif
  printf("  -help                print this help message, then exit\n");
  printf("  -jit                 enable the dynamic (runtime) compiler\n");
  printf("  -memory <size>[mk]   set initial memory size (default: %dm)\n",
	 DefaultHeapSize);
  printf("  -spy                 enable the system spy (jit)\n");
  printf("  -version             print version information, then exit\n");
#if 0  /* on second thought, it makes absolutely no sense for someone
	  to use -args_in_header from the command line.  This is left
	  here for documentation, but it doesn't really need to be
	  printed out by -help */
  printf("  -args_in_header      read NUL-separated arguments from image header\n");
#endif
  printf("\nNotes:\n");
  printf("  <imageName> defaults to `squeak.image'.\n");
  printf("  <argument>s are ignored, but are processed by the Squeak image.\n");
  printf("  The first <argument> normally names a Squeak `script' to execute.\n");
  printf("  Precede <arguments> by `--' to use default image.\n");
  exit(1);
}


void imageNotFound(char *imageName)
{
  /* image file is not found */
  fprintf(stderr,"\
Could not open the Squeak image file `%s'.

There are three ways to open a Squeak image file.  You can:
  1. Put copies of the default image and changes files in this directory.
  2. Put the name of the image file on the command line when you
     run squeak (use the `-help' option for more information).
  3. Set the environment variable SQUEAK_IMAGE to the name of the image
     that you want to use by default.

For more information, type: `man squeak' (without the quote characters).
", imageName);
  exit(1);
}


void versionInfo(void)
{
  extern int vm_serial;
  extern char *vm_date, *cc_version, *ux_version;
  
  fprintf(stderr, "%s %s #%d",
	  VM_HOST, SQ_VERSION, vm_serial);
  
    
  fprintf(stderr, " ["
	  AUDIO_INTERFACE " audio"
#if defined(USE_MEMORY_MMAP)
	  ", memory-mmap"
#endif
	  "] ");
  

  fprintf(stderr, "%s %s\n",  vm_date, cc_version);
  fprintf(stderr,"%s\n", ux_version);


  fprintf(stderr, "default plugin location: %s/%s*.so\n",
	  SQ_LIBDIR, SQ_MODULE_PREFIX);
  
  exit(0);
}


void outOfMemory(void)
{
  fprintf(stderr, "out of memory\n");
  exit(1);
}


int strtobkm(const char *str)
{
  char *suffix;
  int value= strtol(str, &suffix, 10);
  switch (*suffix)
    {
    case 'k': case 'K':
      value*= 1024;
      break;
    case 'm': case 'M':
      value*= 1024*1024;
      break;
    }
  return value;
}


void ParseEnvironment(void)
{
  char *ev= 0;

  if ((ev= getenv("SQUEAK_ALIGN")))	asmAlign= atoi(ev);
  if (asmAlign <  1) asmAlign= 1;
  if (asmAlign > 16) asmAlign= 16;

  if ((ev= getenv("SQUEAK_IMAGE")))	strcpy(shortImageName, ev);
  else					strcpy(shortImageName,
					       "squeak.image");
  if ((ev= getenv("SQUEAK_MEMORY")))	initialHeapSize= strtobkm(ev);
#ifndef HEADLESS
  if (getenv("SQUEAK_NOJIT"))		noJitter= 1;
  if (getenv("SQUEAK_JIT"))		noJitter= 0;
  if (getenv("SQUEAK_SPY"))		withSpy= 1;
  if (getenv("SQUEAK_NOEVENTS"))        noEvents = 1;
  if (getenv("SQUEAK_ROTATE"))      rotateScreen= 1;
  if (getenv("SQUEAK_CALIBRATE"))   mouseNeedsCalibration= 1;
#endif
}


/* defined below */
static void readHeaderOptions(const char *image_filename);


void ParseArguments(int argc, char *argv[], int parsing_header_args)
{
# define skipArg()	(--argc, argv++)
# define saveArg()	(vmArgVec[vmArgCnt++]= *skipArg())
  int args_in_header=0;   /* whether to read arguments from the image header */
  

  /* vm name */
  if(vmArgCnt == 0)
    saveArg();
  

  while ((argc > 0) && (**argv == '-'))	/* more options to parse */
    {
      char *arg= *argv;
      if (!strcmp(arg, "--"))		/* escape from option processing */
	break;

      saveArg();

      if      (!strcmp(arg, "-help"))		usage();
#    ifndef HEADLESS
      else if (!strcmp(arg, "-headless"))	headless= 1;
      else if (!strcmp(arg, "-calibrate"))	mouseNeedsCalibration= 1;
      else if (!strcmp(arg, "-rotate"))		rotateScreen= 1;
      else if (!strcmp(arg, "-noevents"))	noEvents= 1;
#    endif
      else if (!strcmp(arg, "-nojit"))		noJitter= 1;
      else if (!strcmp(arg, "-jit"))		noJitter= 0;
      else if (!strcmp(arg, "-spy"))		withSpy= 1;
      else if (!strcmp(arg, "-version"))	versionInfo();
      else if (!strcmp(arg, "-args_in_header")) {
	args_in_header=1;
	if(argc > 0) {
	  /* record the image name */
	  strcpy(shortImageName, *skipArg());
	}
	/* read the arguments */
	if(!parsing_header_args)   /* but don't recurse */
	  readHeaderOptions(shortImageName);
      }
      else
	{
	  /* option requires an argument */
	  if      (arg == 0)			usage();
	  else if (!strcmp(arg, "-align"))
	    {
	      asmAlign= atoi(saveArg());
	      if (asmAlign <  1) asmAlign= 1;
	      if (asmAlign > 16) asmAlign= 16;
	    }
	  else if (!strcmp(arg, "-memory")) {
	    const char *arg = saveArg();
	    if (arg)
	      initialHeapSize= strtobkm(arg);
	  }
	  else
	    usage();
	} /* option with argument */
    } /* while(more options) */

  if (argc > 0)
    {
      if (!strcmp(*argv, "--"))
	skipArg();
      else
	{
	  /* look for an image name */
	  if(! args_in_header) {  /* if args_in_header, then the image name
				     will have already been read  */
	    if(parsing_header_args)
	      skipArg();
	    else {
	      strcpy(shortImageName, saveArg());
	      if (0 == strstr(shortImageName, ".image"))
		strcat(shortImageName, ".image");
	    }
	  }
	}
    }
  
  /* save remaining arguments as Squeak arguments */
  while (argc > 0)
    squeakArgVec[squeakArgCnt++]= *skipArg();

  
# undef saveArg
# undef skipArg
}


/* read options from the header of the specified image */
#define HEADER_SIZE 512
static void readHeaderOptions(const char *image_filename) 
{
  int fd;
  char *header;
  int header_argc=0;
  char *header_argv[512];
  int header_pos;   /* position in the header as we read from it */

  
  /* allocate the header on the heap, so that strings within it can be
     stored by ParseArguments later */
  header = malloc(HEADER_SIZE+1);
  if(header == NULL) {
    error("Out of Memory\n");
  }
  

  /* read the header */
  fd = open(image_filename, O_RDONLY);
  if(fd < 0) return;
  if(read(fd, header, HEADER_SIZE) < HEADER_SIZE) {
    free(header);
    close(fd);
    return;
  }
  header[HEADER_SIZE] = '\0';  /* make sure header is a valid C string */
  close(fd);
  
  

  if(header[0] != '#' || header[1] != '!') {
    /* not a Unix image file */
    free(header);
    return;
  }
  
  
  /* find the newline which marks the beginning of the options */
  header_pos = 0;
  while(header_pos < HEADER_SIZE && header[header_pos] != '\n')
    header_pos += 1;
  header_pos += 1;   /* skip the newline */

  /* read in options, one at a time; we are finished when either the
     header is exhausted, or a NUL character is seen at the beginning
     of an option */
  while(header_pos < HEADER_SIZE && header[header_pos] != '\0') {
    /* record one option */
    header_argv[header_argc] = &header[header_pos];
    header_argc += 1;

    /* find the begining of the next option */
    while(header_pos < HEADER_SIZE && header[header_pos] != '\0')
      header_pos += 1;

    /* skip over the '\0' */
    header_pos += 1;
  }


  /* got all options -- process them */
  ParseArguments(header_argc, header_argv, 1);
}




/*** internal image ***/



#if defined(USE_INTERNAL_IMAGE)

  /*-- EXPERIMENTAL -- read [and inflate] an internal image file --*/

int sqImageFileClose(FILE *f)
  {
    int err= 0;
    if (f != 0)
      return fclose(f);
    assert(internalImage != 0);
    if (internalZStream != 0)
      {
	printf("decompressed %ld bytes\n", ztell(internalZStream));
	err= zclose(internalZStream);
      }
    internalImage= 0;
    internalImagePtr= 0;
    internalZStream= 0;
    return err;
  }

  int sqImageFilePosition(FILE *f)
  {
    if (f != 0)
      return ftell(f);
    assert(internalImage != 0);
    if (internalZStream != 0)
      return ztell(internalZStream);
    return internalImagePtr - internalImage;
  }

  int sqImageFileRead(void *ptr, size_t sz, size_t count, FILE *f)
  {
    if (f != 0)
      return fread(ptr, sz, count, f);
    assert(internalImage != 0);
    if (internalZStream != 0)
      return zread(ptr, sz, count, internalZStream);
    memcpy(ptr, (void *)internalImagePtr, sz * count);
    internalImagePtr+= sz*count;
    return sz*count;
  }

  int sqImageFileSeek(FILE *f, long pos)
  {
    if (f != 0)
      return fseek(f, pos, SEEK_SET);
    assert(internalImage != 0);
    if (internalZStream != 0)
      return zseek(internalZStream, pos, SEEK_SET);
    internalImagePtr= internalImage + pos;
    return 0;
  }

/* get a value for RTLD_NOW, with increasing levels of desperation... */

#if !defined(RTLD_NOW)
# if defined(DL_NOW)
#   define RTLD_NOW DL_NOW
# elif defined(RTLD_LAZY)
#   define RTLD_NOW RTLD_LAZY
# elif defined(DL_LAZY)
#   define RTLD_NOW DL_LAZY
# else
#   warning: defining RTLD_NOW as 1
#   define RTLD_NOW 1
# endif
#endif
 
  int openInternal(void)
  {
    unsigned char *internalImageEnd= 0;
    void *handle= dlopen(0, RTLD_NOW);
    if (handle == 0)
      {
	fprintf(stderr, "dlopen: %s\n", dlerror());
	exit(1);
      }
    /* non-zero means use in-memory file operations */
    internalImage= (unsigned char *)dlsym(handle, "__squeak_image_start");
    if (internalImage != 0)
      {
	internalImageEnd= (unsigned char *)dlsym(handle, "__squeak_image_end");
	internalImagePtr= internalImage;
	printf("reading internal image at 0x%08x + %d\n",
	       (unsigned)internalImage,
	       (int)(internalImageEnd - internalImage));
	strcpy(shortImageName, "internal.image");
      }
    else
      {
	internalImage= (unsigned char *)dlsym(handle, "__squeak_image_gz_start");
	if (internalImage == 0)
	  return 0;
	internalImageEnd= (unsigned char *)dlsym(handle, "__squeak_image_gz_end");
	{
	  char name[64], comment[64];
	  if (0 == gzstat(internalImage,
			  name, sizeof(name), comment, sizeof(comment)))
	    {
	      fprintf(stderr, "internal image: %s\n", z_error);
	      exit(1);
	    }
	  printf("decompressing %s at 0x%08x + %d\n",
		 ((*name == '\0') ? "internal image" : name),
		 (unsigned)internalImage,
		 (int)(internalImageEnd - internalImage));
	  if (*name == '\0')
	    strcpy(shortImageName, "internal.image");
	  else
	    strcpy(shortImageName, name);
	  if (*comment != '\0')
	    printf("%s\n", comment);
	}
	/* non-zero means inflate on-the-fly */
	internalZStream= gzopen(internalImage, internalImageEnd - internalImage);
	if (internalZStream == 0)
	  {
	    fprintf(stderr, "zopen: %s\n", z_error);
	    exit(1);
	  }
      }
    dlclose(handle);
    return internalImage != 0;
  }

#endif /* USE_INTERNAL_IMAGE */


/*** Segmentation fault handler ***/


#include <signal.h>

void segv(int ignore)
{
  error("Segmentation fault");
}

#ifdef __alpha__
/* headers for setsysinfo (see below) */
# include <sys/sysinfo.h>
# include <sys/proc.h>
#endif



/*** ...we came in? ***/

int main(int argc, char **argv, char **envp)
{
  /* check the interpreter's size assumptions for basic data types */
  if (sizeof(int)    != 4) error("This C platform's integers are not 32 bits.");
  if (sizeof(double) != 8) error("This C platform's floats are not 64 bits.");
  if (sizeof(time_t) != 4) error("This C platform's time_t's are not 32 bits.");

  /* Make parameters global for access from pluggable primitives */
  argCnt= argc;
  argVec= argv;
  envVec= envp;

  /* Allocate arrays to store copies of pointers to command line
     arguments.  Used by getAttributeIntoLength(). */
  if ((vmArgVec= calloc(argc + 1, sizeof(char *))) == 0)
    outOfMemory();

  if ((squeakArgVec= calloc(argc + 1, sizeof(char *))) == 0)
    outOfMemory();

  signal(SIGSEGV, segv);

  /* initialisation */

# if defined(__alpha__)
  /* disable printing of unaligned access exceptions */
  {
    int buf[2]= { SSIN_UACPROC, UAC_NOPRINT };
    if (setsysinfo(SSI_NVPAIRS, buf, 1, 0, 0, 0) < 0)
      {
	perror("setsysinfo(UAC_NOPRINT)");
      }
  }
# endif

  progName= argv[0];
#ifndef HEADLESS
  if (!strcmp(progName, "headlessSqueak"))
    headless= 1;
#endif
  ParseEnvironment();
  ParseArguments(argc, argv, 0);
  SetUpTimers();
  aioInitialize();
  

# if !defined(HEADLESS) && defined(USE_XSHM)
#   ifdef AT_EXIT
      AT_EXIT(shmExit);
      {
	AT_EXIT((void(*)(void))ioShutdownAllModules);
      }
#   else
#     warning: cannot free display bitmap on exit!
#     warning: cannot shut down module system on exit!
#   endif
# endif

# if 0
 /* TPR - do not call these here; the module mechanism will call them at the right time automagically */
  sqFileInit();
  joystickInit();
# endif

# if defined(HAVE_TZSET)
  tzset();	/* should _not_ be necessary! */
# endif

  if (!realpath(argv[0], vmName))
    vmName[0]= 0; /* full VM name */

  /* read the image file and allocate memory for Squeak heap */
  {
    FILE *f= 0;

#   if defined(USE_INTERNAL_IMAGE) /* EXPERIMENTAL */
    if (openInternal())
      {
	RecordFullPathForImageName(shortImageName);
      }
    else
#   endif
      {
	if (0 == (f= fopen(shortImageName, "r")))
	  imageNotFound(shortImageName);
	else
	  RecordFullPathForImageName(shortImageName); /* full image path */
      }
    RecordFullPathForVmName(argv[0]); /* full vm path */
    readImageFromFileHeapSize(f, initialHeapSize);
    sqImageFileClose(f);
  }

#ifndef HEADLESS
  /* open the Squeak window. */
  openDisplay(argc, argv);
#endif

#if defined(HAVE_LIBDL) && defined(J_ENABLED) && !defined(J_PROFILE)
  {
    /* first try to find an internal dynamic compiler... */
    int handle= ioLoadModule(0);
    int comp= ioFindExternalFunctionIn("j_interpret", handle);
    /* ...and if that fails... */
    if ((comp == 0) && !noJitter)
      {
	/* ...try to find an external one */
	handle= ioLoadModule("SqueakCompiler");
	if (handle != 0)
	  comp= ioFindExternalFunctionIn("j_interpret", handle);
      }
    if (comp)
      {
	((void(*)(void))comp)();
	return 0;
      }
    else
      printf("could not find j_interpret\n");
  }
#endif /*HAVE_LIBDL*/

  /* run Squeak */
#ifndef J_PROFILE
  interpret();
#else
  j_interpret();
#endif

  return 0;
}


int ioExit(void)
{
#ifndef HEADLESS
  closeDisplay();
#endif

  /*** Isn't this where... ***/

  exit(0);
}
