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

mactty.c

/*    SCCS Id: @(#)mactty.c   3.1   93/03/01                */
/* Copyright (c) Jon W{tte 1993.                            */
/* NetHack may be freely redistributed.  See license for details. */

/*
 * mactty.c
 *
 * This file contains the actual code for the tty library. For a 
 * description, see the file mactty.h, which contains all you
 * need to know to use the library.
 */

#include "mttypriv.h"

/* these declarations are here becaues I can't include macwin.h without including the world */
extern void dprintf(char * , ...);                                            /* dprintf.c */
extern pascal short tty_environment_changed(WindowPtr window);    /* local */

#if PRINTF_TTY
# include <stdio.h>
# include <stdarg.h>
#endif

static void select_onscreen_window ( tty_record * record ) ;
static void select_offscreen_port ( tty_record * record ) ;

#define MEMORY_MARGIN 30000


/*
 * Error code returned when it's probably our fault, or
 * bad parameters.
 */
#define general_failure 1

/*
 * How long lines do we support for input?
 */
#define IB_LIMIT 80

/*
 * Convenience macro for most functions - put last in declaration
 */
#define RECORD(record) \
tty_record * record ; \
      if ( ! window ) { \
            dprintf ( "*** null Window ***" ) ; \
            return general_failure ; \
      } \
      record = ( tty_record * ) GetWRefCon ( window ) ; \
      if ( ! record ) { \
            return general_failure ; \
      }

/*
 * Simple macro for deciding wether we draw at once or delay
 */
#define DRAW_DIRECT ( 0L != ( TA_ALWAYS_REFRESH & record -> \
      attribute [ TTY_ATTRIB_FLAGS ] ) )


/*
 * Module variable used as return value for various calls.
 */
static short s_err = 0 ;

/*
 * Table of special characters. Zero is ALWAYS special; it means
 * end of string and would be MISSED if it was not included here.
 */
static const unsigned char s_cooked_controls [ ] = {
      1,    0,    0,    0,    0,    0,    0,    1,    1,    0,    1,    0,    0,    1,    0,    0,
      0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
      0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
      0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
      0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
      0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
      0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
      0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
      0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
      0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
      0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
      0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
      0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
      0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
      0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
      0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
} ;

static const unsigned char s_raw_controls [ ] = {
      1,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
      0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
      0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
      0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
      0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
      0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
      0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
      0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
      0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
      0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
      0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
      0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
      0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
      0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
      0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
      0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
} ;

static const unsigned char * s_control = s_cooked_controls ;


/*
 * Memory-related error
 */
static short
mem_err ( void ) {
      if ( MemError ( ) ) {
            return MemError ( ) ;
      }
      return general_failure ;
}


/*
 * Make a rectangle empty
 */
static void
empty_rect ( Rect * r ) {

      r -> right = -20000 ;
      r -> left = 20000 ;
      r -> top = 20000 ;
      r -> bottom = -20000 ;
}


/*
 * Union twp rect together
 */
static void
union_rect ( Rect * r1 , Rect * r2 , Rect * dest ) {

      if ( r1 -> left < r2 -> left ) {
            dest -> left = r1 -> left ;
      } else {
            dest -> left = r2 -> left ;
      }

      if ( r1 -> top < r2 -> top ) {
            dest -> top = r1 -> top ;
      } else {
            dest -> top = r2 -> top ;
      }

      if ( r1 -> bottom > r2 -> bottom ) {
            dest -> bottom = r1 -> bottom ;
      } else {
            dest -> bottom = r2 -> bottom ;
      }

      if ( r1 -> right > r2 -> right ) {
            dest -> right = r1 -> right ;
      } else {
            dest -> right = r2 -> right ;
      }
}


/*
 * Dispose a pointer using the set memory-allocator
 */
static short
dispose_ptr ( tty_record * record , void * ptr ) {
pascal short ( * func ) ( WindowPtr window , void * ptr ) =
      ( pascal short ( * ) ( WindowPtr , void * ) )
      record -> attribute [ TTY_FREE_MEMORY_FUNCTION ] ;

      if ( ! ptr ) {
            return noErr ; /* Silently accept disposing nulls */
      }
      if ( func ) {
            s_err = ( * func ) ( record -> its_window , ptr ) ;
      } else {
            DisposePtr ( ptr ) ;
            s_err = MemError ( ) ;
      }
      return s_err ;
}


/*
 * Allocate a pointer using the set memory-allocator
 */
static short
alloc_ptr ( tty_record * record , void * * ptr , long size ) {
pascal short ( * func ) ( WindowPtr window , void * * ptr , long ) =
      ( pascal short ( * ) ( WindowPtr , void * * , long ) )
      record -> attribute [ TTY_ALLOCATE_MEMORY_FUNCTION ] ;

      if ( func ) {
            s_err = ( * func ) ( record -> its_window , ptr , size ) ;
      } else {
            * ptr = NewPtr ( size ) ;
            s_err = * ptr ? noErr : mem_err ( ) ;
      }
      return s_err ;
}


/*
 * Set up a GWorld in the record
 */
static short
allocate_offscreen_world ( tty_record * record ) {
GWorldPtr gw = (GWorldPtr)0 ;
GWorldFlags world_flags = 0 ;
long mem_here , mem_there , other , required_mem ;
Point p = { 0 , 0 } ;
Rect r_screen ;
GDHandle gdh ;

      select_onscreen_window ( record ) ;
      LocalToGlobal ( & p ) ;
      r_screen = record -> its_bits . bounds ;
      OffsetRect ( & r_screen , p . h , p . v ) ;

      gdh = GetMaxDevice ( & r_screen ) ;
      required_mem = ( long ) ( * ( ( * gdh ) -> gdPMap ) ) -> pixelSize *
            ( ( long ) record -> its_bits . bounds . right *
            record -> its_bits . bounds . bottom ) >> 3 ;

      PurgeSpace ( & other , & mem_here ) ;
      if ( other < mem_here + MEMORY_MARGIN ) {
            mem_here = other - MEMORY_MARGIN ;
      }
      dprintf ( "Heap %ld Required %ld" , mem_here , required_mem ) ;
      if ( required_mem > mem_here ) {
            mem_there = required_mem ;
            if ( required_mem > MFMaxMem ( & mem_there ) ) {
                  dprintf ( "No memory" ) ;
                  return memFullErr ;
            }
            world_flags |= useTempMem ;
      }
      s_err = NewGWorld ( & gw , 0 , & r_screen , (CTabHandle) 0 , (GDHandle) 0 , world_flags ) ;
      if ( ! s_err ) {
            record -> offscreen_world = gw ;
            select_offscreen_port ( record ) ;
            SetOrigin ( 0 , 0 ) ;
            select_onscreen_window ( record ) ;
            dprintf ( "New GWorld @ %lx;dm %lx CGrafPtr" , gw , gw ) ;
      }
      return s_err ;
}


/*
 * Done with GWorld, release data
 */
static short
deallocate_gworld ( tty_record * record ) {
      if ( record -> offscreen_world ) {
            DisposeGWorld ( record -> offscreen_world ) ;
            record -> offscreen_world = (GWorldPtr) 0 ;
      }
      return noErr ;
}


/*
 * Save the current port/world in a safe place for later retrieval
 */
static void
save_port ( tty_record * record , void * save ) {
GWorldPtr gw ;
GDHandle gh ;
GrafPtr gp ;

      if ( record -> uses_gworld ) {
            GetGWorld ( & gw , & gh ) ;
            * ( GWorldPtr * ) save = gw ;
      } else {
            GetPort ( & gp ) ;
            * ( GrafPtr * ) save = gp ;
      }
}


/*
 * Restore current port/world after a save
 */
static void
use_port ( tty_record * record , void * port ) {
      if ( record -> uses_gworld ) {
      PixMapHandle pix_map ;

            SetGWorld ( ( GWorldPtr ) port , (GDHandle) 0 ) ;
            if ( port == record -> offscreen_world ) {
                  pix_map = GetGWorldPixMap ( record -> offscreen_world ) ;
                  if ( pix_map ) {
                        LockPixels ( pix_map ) ;
                  }
            } else {
                  pix_map = GetGWorldPixMap ( record -> offscreen_world ) ;
                  if ( pix_map ) {
                        UnlockPixels ( pix_map ) ;
                  }
            }
      } else {
            SetPort ( ( GrafPtr ) port ) ;
      }
}


/*
 * Use offscreen drawing - lock the pixels through use_port
 */
static void
select_offscreen_port ( tty_record * record ) {
      if ( record -> uses_gworld ) {
            use_port ( record , record -> offscreen_world ) ;
      } else {
            use_port ( record , record -> offscreen_port ) ;
      }
}


/*
 * Use the window - unlock pixels
 */
static void
select_onscreen_window ( tty_record * record ) {
      if ( record -> uses_gworld ) {
            use_port ( record , record -> its_window_world ) ;
            SetPort ( record -> its_window ) ;
      } else {
            use_port ( record , record -> its_window ) ;
      }
}


/*
 * Do bits copy depending on if we're using color or not
 */
static void
copy_bits ( tty_record * record , Rect * bounds , short xfer_mode , RgnHandle mask_rgn ) {
RGBColor old_fore , old_back ;
RGBColor rgb_black = { 0 , 0 , 0 } ;
RGBColor rgb_white = { 0xffff , 0xffff , 0xffff } ;

      if ( record -> uses_gworld ) {
      GWorldFlags pix_state = GetPixelsState ( GetGWorldPixMap ( record -> offscreen_world ) ) ;

            LockPixels ( GetGWorldPixMap ( record -> offscreen_world ) ) ;
            GetForeColor ( & old_fore ) ;
            GetBackColor ( & old_back ) ;
            RGBForeColor ( & rgb_black ) ;
            RGBBackColor ( & rgb_white ) ;
            CopyBits ( ( BitMap * ) & ( record -> offscreen_world -> portPixMap ) ,
                  & ( record -> its_window -> portBits ) , bounds , bounds , xfer_mode ,
                  mask_rgn ) ;
            RGBForeColor ( & old_fore ) ;
            RGBBackColor ( & old_back ) ;
            SetPixelsState ( GetGWorldPixMap ( record -> offscreen_world ) , pix_state ) ;
      } else {
            CopyBits ( & ( record -> its_bits ) , & ( record -> its_window -> portBits ) ,
                  bounds , bounds , xfer_mode , mask_rgn ) ;
      }
}


/*
 * Fill an area with the background color
 */
static void
erase_rect ( tty_record * record , Rect * area ) {
#if defined(applec)
# pragma unused(record)
#endif

      EraseRect ( area ) ;
}


/*
 * Get rid of offscreen bitmap
 */
static short
free_bits ( tty_record * record ) {
      if ( record -> uses_gworld ) {
            s_err = deallocate_gworld ( record ) ;
      } else {
            if ( record -> offscreen_port ) {
                  ClosePort ( record -> offscreen_port ) ;
                  s_err = dispose_ptr ( record , record -> offscreen_port ) ;
                  if ( ! s_err ) {
                        record -> offscreen_port = (GrafPtr) 0 ;
                  } else {
                        return s_err ;
                  }
            }
            s_err = dispose_ptr ( record , record -> its_bits . baseAddr ) ;
            if ( ! s_err ) {
                  record -> its_bits . baseAddr = (char *)0 ;
            }
      }
      return s_err ;
}


/*
 * Snatch a window from the resource fork. Create the record.
 * Otherwise, do nothing.
 */
pascal short
create_tty ( WindowPtr * window , short resource_id , Boolean in_color ) {
tty_record * record ;
Boolean was_allocated = !! * window ;

      if ( in_color ) {
            * window = GetNewCWindow ( resource_id , ( Ptr ) * window , ( WindowPtr ) -1L ) ;
      } else {
            * window = GetNewWindow ( resource_id , ( Ptr ) * window , ( WindowPtr ) -1L ) ;
      }
      if ( ! * window ) {
            return mem_err ( ) ;
      }

      record = ( tty_record * ) NewPtrClear ( sizeof ( tty_record ) ) ;
      if ( ! record ) {
            if ( was_allocated ) {
                  CloseWindow ( * window ) ;
            } else {
                  DisposeWindow ( * window ) ;
            }
            return mem_err ( ) ;
      }
      record -> its_window = * window ;
      SetWRefCon ( * window , ( long ) record ) ;
      record -> was_allocated = was_allocated ;
      record -> its_bits . baseAddr = (char *)0 ;
#if TTY_INPUT
      record -> input_buffer = (char *)0 ;
#endif

/*
 * We need to keep the window world around if we switch worlds
 */
      record -> offscreen_world = (GWorldPtr) 0 ;
      record -> uses_gworld = in_color ;
      if ( in_color ) {
      GDHandle gh ;

            SetPort ( * window ) ;
            GetGWorld ( & ( record -> its_window_world ) , & gh ) ;
      } else {
            record -> its_window_world = (GWorldPtr)0 ;
      }

#if CLIP_RECT_ONLY
      empty_rect ( & ( record -> invalid_rect ) ) ;
#else
      record -> invalid_part = NewRgn ( ) ;
      if ( ! record -> invalid_part ) {
      short err = mem_err ( ) ;

            err = destroy_tty ( * window ) ;
            return err ;
      }
#endif

      return noErr ;
}


/*
 * Initialize the struct so it actually works as a tty.
 */
pascal short
init_tty_name ( WindowPtr window , unsigned char * font_name , short font_size ,
      short x_size , short y_size ) {
short font_num = 0 ;

      GetFNum ( font_name , & font_num ) ;
      if ( ! font_num ) {
            return general_failure ;
      }
      return init_tty_number ( window , font_num , font_size , x_size , y_size ) ;
}


pascal short
init_tty_number ( WindowPtr window , short font_number , short font_size ,
      short x_size , short y_size ) {
RECORD ( record ) ;

      record -> font_number = font_number ;
      record -> font_size = font_size ;
      record -> x_size = x_size ;
      record -> y_size = y_size ;

      record -> offscreen_port = (GrafPtr) 0 ;
      record -> attribute [ TTY_ATTRIB_BACKGROUND ] = 0xffffff ; /* White */

#if TTY_INPUT
      record -> input_buffer_len = 0 ;
      record -> input_buffer_limit = IB_LIMIT ;
      s_err = alloc_ptr ( record , & ( record -> input_buffer ) , IB_LIMIT ) ;
      if ( s_err ) {
            return s_err ;
      }
#endif

      return force_tty_coordinate_system_recalc ( window ) ;
}


/*
 * Done with a window - destroy it. Release the memory only if
 * it wasn't allocated when we got it!
 */
pascal short
destroy_tty ( WindowPtr window ) {
Boolean close_flag ;
RECORD ( record ) ;

      s_err = free_bits ( record ) ;
#if TTY_INPUT
      if ( ! s_err ) {
            s_err = dispose_ptr ( record -> input_buffer ) ;
            if ( ! s_err ) {
                  record -> input_buffer = (char *)0 ;
            }
      }
#endif
      if ( ! s_err ) {
            close_flag = record -> was_allocated ;
            DisposePtr ( ( Ptr ) record ) ;
            s_err = MemError ( ) ;
            if ( close_flag ) {
                  CloseWindow ( window ) ;
            } else {
                  DisposeWindow ( window ) ;
            }
      }
      
      return s_err ;
}


/*
 * Use a new font for drawing.
 */
pascal short
set_tty_font_name ( WindowPtr window , unsigned char * font_name ) {
RECORD ( record ) ;

      record -> font_number = 0 ;
      GetFNum ( font_name , & ( record -> font_number ) ) ;

      return ! record -> font_number ;
}


pascal short
set_tty_font_number ( WindowPtr window , short font_number ) {
RECORD ( record ) ;

      record -> font_number = font_number ;

      return noErr ;
}


pascal short
set_tty_font_size ( WindowPtr window , short font_size ) {
RECORD ( record ) ;

      record -> font_size = font_size ;

      return noErr ;
}


static void
do_set_port_font ( tty_record * record ) {

      PenNormal ( ) ;
      TextFont ( record -> font_number ) ;
      TextFace ( 0 ) ;
      TextSize ( record -> font_size ) ;
      if ( 0L != ( record -> attribute [ TTY_ATTRIB_FLAGS ] & TA_OVERSTRIKE ) ) {
            TextMode ( srcOr ) ;
      } else {
            TextMode ( srcCopy ) ;
      }
}


/*
 * Fill in some fields from some other fields that may have changed
 */
static void
calc_font_sizes ( tty_record * record ) {
FontInfo font_info ;

      do_set_port_font ( record ) ;

      GetFontInfo ( & font_info ) ;
      record -> char_width = font_info . widMax ;
      record -> ascent_height = font_info . ascent + font_info . leading ;
      record -> row_height = record -> ascent_height + font_info . descent ;
}


/*
 * Allocate memory for the bitmap holding the tty window
 */
static short
alloc_bits ( tty_record * record ) {
void * old_port ;

      save_port ( record , & old_port ) ;
      SetRect ( & record -> its_bits . bounds , 0 , 0 ,
            record -> char_width * record -> x_size ,
            record -> row_height * record -> y_size ) ;

/*
 * Clear two highest and lowest bit - not a color pixMap, and even in size
 */
      record -> its_bits . rowBytes = ( ( record -> its_bits . bounds . right + 15 )
            >> 3 ) & 0x1ffe ;

      if ( record -> uses_gworld ) {
            s_err = allocate_offscreen_world ( record ) ;
      } else {
            s_err = alloc_ptr ( record , ( void * * ) & ( record -> its_bits . baseAddr ) ,
                  record -> its_bits . rowBytes * record -> its_bits . bounds . bottom ) ;
            if ( ! s_err ) {
                  s_err = alloc_ptr ( record , ( void * * ) & ( record -> offscreen_port ) ,
                        sizeof ( GrafPort ) ) ;
            }
            if ( ! s_err ) {
                  OpenPort ( record -> offscreen_port ) ;
                  SetPort ( record -> offscreen_port ) ;
                  ClipRect ( & ( record -> its_bits . bounds ) ) ;
                  SetPortBits ( & ( record -> its_bits ) ) ;
            }
      }
      use_port ( record , old_port ) ;

      return s_err ;
}


static void
update_offscreen_info ( tty_record * record ) {

      select_offscreen_port ( record ) ;
      do_set_port_font ( record ) ;
      select_onscreen_window ( record ) ;
}


/*
 * Recalculate the window based on new size, font, extent values,
 * and re-allocate the bitmap.
 */
pascal short
force_tty_coordinate_system_recalc ( WindowPtr window ) {
RECORD ( record ) ;

      if ( s_err = free_bits ( record ) ) {
            return s_err ;
      }
      select_onscreen_window ( record ) ;
      calc_font_sizes ( record ) ;

      if ( s_err = alloc_bits ( record ) ) {
/*
 * Catastrophe! We could not allocate memory for the bitmap! Things may go very
 * much downhill from here!
 */
            dprintf ( "alloc_bits returned null in force_tty_coordinate_system_recalc!" ) ;
            return s_err ;
      }

      update_offscreen_info ( record ) ;
      return clear_tty ( window ) ;
}


/*
 * Update TTY according to new color environment for the window
 */
pascal short
tty_environment_changed ( WindowPtr window ) {
Point p = { 0 , 0 } ;
Rect r_screen ;
RECORD ( record ) ;

      if ( record -> uses_gworld ) {
            select_onscreen_window ( record ) ;
            r_screen = record -> its_bits . bounds ;
            LocalToGlobal ( & p )  ;
            OffsetRect ( & r_screen , p . h , p . v ) ;
            UpdateGWorld ( & ( record -> offscreen_world ) , 0 , & r_screen ,
                  (CTabHandle) 0 , (GDHandle) 0 , stretchPix ) ;
            select_offscreen_port ( record ) ;
            SetOrigin ( 0 , 0 ) ;
            select_onscreen_window ( record ) ;
      }
      return s_err ;
}


/*
 * Read a lot of interesting and useful information from the current tty
 */
pascal short
get_tty_metrics ( WindowPtr window , short * x_size , short * y_size ,
      short * x_size_pixels , short * y_size_pixels , short * font_number ,
      short * font_size , short * char_width , short * row_height ) {
RECORD ( record ) ;

/*
 * First, test that we actually have something to draw to...
 */
      if ( ( ( (char *)0 == record -> its_bits . baseAddr ) && ! record -> uses_gworld ) ||
            ( ( (GWorldPtr)0 == record -> offscreen_world ) && record -> uses_gworld ) ) {
            return general_failure ;
      }

      * x_size = record -> x_size ;
      * y_size = record -> y_size ;
      * x_size_pixels = record -> its_bits . bounds . right ;
      * y_size_pixels = record -> its_bits . bounds . bottom ;
      * font_number = record -> font_number ;
      * font_size = record -> font_size ;
      * char_width = record -> char_width ;
      * row_height = record -> row_height ;

      return noErr ;
}


/*
 * Map a position on the map to screen coordinates
 */
static void
pos_rect ( tty_record * record , Rect * r , short x_pos , short y_pos ,
      short x_end , short y_end ) {
 
      SetRect ( r , x_pos * ( record -> char_width ) , y_pos * ( record -> row_height ) ,
            ( 1 + x_end ) * ( record -> char_width ) , ( 1 + y_end ) *
            ( record -> row_height ) ) ;
}


static void
accumulate_rect ( tty_record * record , Rect * rect ) {
#if CLIP_RECT_ONLY
      union_rect ( rect , & ( record -> invalid_rect ) , & ( record -> invalid_rect ) ) ;
#else
RgnHandle rh = NewRgn ( ) ;

      RectRgn ( rh , rect ) ;
      UnionRgn ( record -> invalid_part , rh , record -> invalid_part ) ;
      DisposeRgn ( rh ) ;
#endif
}


/*
 * Invert the specified position
 */
static void
curs_pos ( tty_record * record , short x_pos , short y_pos , short to_state ) {
Rect r ;

      if ( record -> curs_state == to_state ) {
            return ;
      }
      record -> curs_state = to_state ;
      pos_rect ( record , & r , x_pos , y_pos , x_pos , y_pos ) ;

      if ( DRAW_DIRECT ) {
      void * old_port ;

            save_port ( record , & old_port ) ;
            select_onscreen_window ( record ) ;
            InvertRect ( & r ) ;
            use_port ( record , old_port ) ;
      } else {
            accumulate_rect ( record , & r ) ;
      }
}


/*
 * Move the cursor (both as displayed and where drawing goes)
 * HOWEVER: The cursor is NOT stored in the bitmap!
 */
pascal short
move_tty_cursor ( WindowPtr window , short x_pos , short y_pos ) {
RECORD ( record ) ;

      select_onscreen_window ( record ) ;
      if ( record -> x_curs == x_pos && record -> y_curs == y_pos ) {
            return noErr ;
      }
      if ( record -> x_size <= x_pos || x_pos < 0 ||
            record -> y_size <= y_pos || y_pos < 0 ) {
            return general_failure ;
      }
      curs_pos ( record , record -> x_curs , record -> y_curs , 0 ) ;
      record -> x_curs = x_pos ;
      record -> y_curs = y_pos ;
      curs_pos ( record , x_pos , y_pos , 1 ) ;

      return noErr ;
}


/*
 * Get the current cursor position. Note that the cursor may not be
 * displayed there yet; it depends on wether you've called update_tty()
 * or have the window in TA_ALWAYS_REFRESH mode.
 */
pascal short
get_tty_cursor ( WindowPtr window , short * x_pos , short * y_pos ) {
RECORD ( record ) ;

      * x_pos = record -> x_curs ;
      * y_pos = record -> y_curs ;

      return noErr ;
}


/*
 * Update the screen to match the current bitmap, after adding stuff
 * with add_tty_char etc.
 */
pascal short
update_tty ( WindowPtr window ) {
Rect r ;
RECORD ( record ) ;

#if CLIP_RECT_ONLY
      if ( record -> invalid_rect . right <= record -> invalid_rect . left ||
            record -> invalid_rect . bottom <= record -> invalid_rect . top ) {
            return noErr ;
      }
      r = record -> invalid_rect ;
#else
      if ( EmptyRgn ( record -> invalid_part ) ) {
            return noErr ;
      }
      r = ( * ( record -> invalid_part ) ) -> rgnBBox ;
#endif
      select_onscreen_window ( record ) ;
#if CLIP_RECT_ONLY
      copy_bits ( record , & r , srcCopy , (RgnHandle) 0 ) ;
      empty_rect ( & ( record -> invalid_rect ) ) ;
#else
      copy_bits ( record , & r , srcCopy , (RgnHandle) 0 ) ;
      SetEmptyRgn ( record -> invalid_part ) ;
#endif
      if ( record -> curs_state ) {

            pos_rect ( record , & r , record -> x_curs , record -> y_curs ,
                  record -> x_curs , record -> y_curs ) ;
            InvertRect ( & r ) ;
      }

      return noErr ;
}


/*
 * Add a single character. It is drawn directly if the correct flag is set,
 * else deferred to the next update event or call of update_tty()
 */
pascal short
add_tty_char ( WindowPtr window , short character ) {
char s [ 2 ] ;

      s [ 0 ] = character ;
      s [ 1 ] = 0 ;
      return add_tty_string ( window , s ) ;
}


/*
 * Low level add to screen
 */
static void
do_add_string ( tty_record * record , char * str , short len ) {
Rect r ;
register int x_pos , count = len ;

      if ( len < 1 ) {
            return ;
      }
      select_offscreen_port ( record ) ;

      if ( 0L != ( record -> attribute [ TTY_ATTRIB_FLAGS ] & TA_MOVE_EACH_CHAR ) ) {
            x_pos = record -> x_curs ;
            while ( count -- ) {
                  MoveTo ( x_pos * record -> char_width , record -> y_curs *
                        record -> row_height + record -> ascent_height ) ;
                  DrawChar ( * ( str ++ ) ) ;
            }
      } else {
            MoveTo ( record -> x_curs * record -> char_width , record -> y_curs *
                  record -> row_height + record -> ascent_height ) ;
            DrawText ( str , 0 , len ) ;
      }

      pos_rect ( record , & r , record -> x_curs , record -> y_curs ,
            record -> x_curs + len - 1 , record -> y_curs ) ;
      if ( DRAW_DIRECT ) {
            select_onscreen_window ( record ) ;
            copy_bits ( record , & r , srcCopy , (RgnHandle)0 ) ;
      } else {
            accumulate_rect ( record , & r ) ;
            select_onscreen_window ( record ) ;
      }
}


/*
 * Low-level cursor handling routine
 */
static void
do_add_cursor ( tty_record * record , short x_pos ) {

      record -> x_curs = x_pos ;
      if ( record -> x_curs >= record -> x_size ) {
            if ( 0L != ( record -> attribute [ TTY_ATTRIB_FLAGS ] & TA_WRAP_AROUND ) ) {
                  record -> y_curs ++ ;
                  record -> x_curs = 0 ;
                  if ( record -> y_curs >= record -> y_size ) {
                        if ( 0L != ( record -> attribute [ TTY_ATTRIB_FLAGS ] &
                              TA_INHIBIT_VERT_SCROLL ) ) {
                              record -> y_curs = record -> y_size ;
                        } else {
                              scroll_tty ( record -> its_window , 0 , 1 + record -> y_curs -
                                    record -> y_size ) ;
                        }
                  }
            } else {
                  record -> x_curs = record -> x_size ;
            }
      }
}


/*
 * Beep
 */
static void
do_tty_beep ( tty_record * record ) {
      if ( record -> attribute [ TTY_BEEP_FUNCTION ] ) {
      pascal void ( * tty_beep ) ( WindowPtr ) = ( pascal void ( * ) ( WindowPtr ) )
            record -> attribute [ TTY_BEEP_FUNCTION ] ;
            ( * tty_beep ) ( record -> its_window ) ;
      } else {
            SysBeep ( 20 ) ;
      }
}


/*
 * Do control character
 */
static void
do_control ( tty_record * record , short character ) {
static int recurse = 0 ;

/*
 * Check recursion because nl_add_cr and cr_add_nl may both be set and call each other
 */
      recurse ++ ;
      if ( recurse > 2 ) {
            return ;
      }
      switch ( character ) {
      case 10 :
            record -> y_curs ++ ;
            if ( record -> y_curs >= record -> y_size ) {
                  scroll_tty ( record -> its_window , 0 , 1 + record -> y_curs -
                        record -> y_size ) ;
            }
            if ( 0L != ( record -> attribute [ TTY_ATTRIB_CURSOR ] & TA_NL_ADD_CR ) ) {
                  do_control ( record , 13 ) ;
            }
            break ;
      case 13 :
            record -> x_curs = 0 ;
            if ( 0L != ( record -> attribute [ TTY_ATTRIB_CURSOR ] & TA_CR_ADD_NL ) ) {
                  do_control ( record , 10 ) ;
            }
            break ;
      case 7 :
            do_tty_beep ( record ) ;
            break ;
      case 8 :
            if ( record -> x_curs > 0 ) {
                  record -> x_curs -- ;
            }
            break ;
      default :
            break ;
      }
      recurse -- ;
}


/*
 * Add a null-terminated string of characters
 */
pascal short
add_tty_string ( WindowPtr window , const char * string ) {
register const unsigned char * start_c ;
register const unsigned char * the_c ;
register short max_x , pos_x ;
RECORD ( record ) ;

      select_onscreen_window ( record ) ;
      curs_pos ( record , record -> x_curs , record -> y_curs , 0 ) ;

      the_c = ( const unsigned char * ) string ;
      max_x = record -> x_size ;
      while ( 1 ) {
            start_c = the_c ;
            pos_x = record -> x_curs ;
            if ( ( 0L == ( record -> attribute [ TTY_ATTRIB_FLAGS ] & TA_WRAP_AROUND ) ) &&
                  pos_x >= max_x ) { /* Optimize away drawing across border without wrap */
                  break ;
            }
            while ( pos_x < max_x && ! s_control [ * the_c ] ) {
                  the_c ++ ;
                  pos_x ++ ;
            }
            do_add_string ( record , ( char * ) start_c , the_c - start_c ) ;
            do_add_cursor ( record , pos_x ) ;
            if ( ! * the_c ) {
                  break ;
            }
            if ( s_control [ * the_c ] ) {
                  do_control ( record , * the_c ) ;
                  the_c ++ ;
            }
      }
      select_onscreen_window ( record ) ;
      curs_pos ( record , record -> x_curs , record -> y_curs , 1 ) ;

      ShowWindow ( window ) ;

      return noErr ;
}


/*
 * Do a c-style printf - the result shouldn't be too long...
 */
short
printf_tty ( WindowPtr window , const char * fmt , ... ) {
static char buf [ 256 ] ;
va_list list ;

      va_start ( list , fmt ) ;
      vsprintf ( buf , fmt , list ) ;
      va_end ( list ) ;

      return add_tty_string ( window , buf ) ;
}


/*
 * Read or change attributes for the tty. Note that some attribs may
 * very well clear and reallocate the bitmap when changed, whereas
 * others (color, highlight, ...) are guaranteed not to.
 */
pascal short
get_tty_attrib ( WindowPtr window , tty_attrib attrib , long * value ) {
RECORD ( record ) ;

      if ( attrib < 0 || attrib >= TTY_NUMBER_ATTRIBUTES ) {
            return general_failure ;
      }
      * value = record -> attribute [ attrib ] ;

      return noErr ;
}


pascal short
set_tty_attrib ( WindowPtr window , tty_attrib attrib , long value ) {
long old_value ;
RGBColor rgb_color ;
RECORD ( record ) ;

      if ( attrib < 0 || attrib >= TTY_NUMBER_ATTRIBUTES ) {
            return general_failure ;
      }
      old_value = record -> attribute [ attrib ] ;
      if ( old_value == value ) {
            return noErr ;
      }
      record -> attribute [ attrib ] = value ;
      /*
       * Presently, no attributes generate a new bitmap.
       */
      switch ( attrib ) {
      case TTY_ATTRIB_CURSOR :
/*
 * Check if we should change tables
 */
            if ( 0L != ( value & TA_RAW_OUTPUT ) ) {
                  s_control = s_raw_controls ;
            } else {
                  s_control = s_cooked_controls ;
            }
            break ;
      case TTY_ATTRIB_FLAGS :
/*
 * Check if we should flush the output going from cached to draw-direct
 */
            if ( 0L != ( value & TA_ALWAYS_REFRESH ) ) {
                  update_tty ( window ) ;
            }
            break ;
      case TTY_ATTRIB_FOREGROUND :
/*
 * Set foreground color
 */
            TA_TO_RGB ( value , rgb_color ) ;
            select_offscreen_port ( record ) ;
            RGBForeColor ( & rgb_color ) ;
            select_onscreen_window ( record ) ;
            RGBForeColor ( & rgb_color ) ;
            break ;
      case TTY_ATTRIB_BACKGROUND :
/*
 * Set background color
 */
            TA_TO_RGB ( value , rgb_color ) ;
            select_offscreen_port ( record ) ;
            RGBBackColor ( & rgb_color ) ;
            select_onscreen_window ( record ) ;
            RGBBackColor ( & rgb_color ) ;
            break ;
      default :
            break ;
      }
      return noErr ;
}


/*
 * Scroll the window. Positive is up/left. scroll_tty ( window , 0 , 1 ) is a line feed.
 * Scroll flushes the accumulated update area by calling update_tty().
 */
pascal short
scroll_tty ( WindowPtr window , short delta_x , short delta_y ) {
RgnHandle rgn ;
RECORD ( record ) ;

      select_onscreen_window ( record ) ;
      s_err = update_tty ( window ) ;

      rgn = NewRgn ( ) ;

      select_offscreen_port ( record ) ;
      ScrollRect ( & ( record -> its_bits . bounds ) , - delta_x * record -> char_width ,
            - delta_y * record -> row_height , rgn ) ;
      EraseRgn ( rgn ) ;
      SetEmptyRgn ( rgn ) ;

      select_onscreen_window ( record ) ;
      ScrollRect ( & ( record -> its_bits . bounds ) , - delta_x * record -> char_width ,
            - delta_y * record -> row_height , rgn ) ;
      EraseRgn ( rgn ) ;
      DisposeRgn ( rgn ) ;

      record -> y_curs -= delta_y ;
      record -> x_curs -= delta_x ;

      return noErr ;
}


/*
 * Clear the screen. Immediate.
 */
pascal short
clear_tty ( WindowPtr window ) {
RECORD ( record ) ;

      select_offscreen_port ( record ) ;
      erase_rect ( record , & ( record -> its_bits . bounds ) ) ;
      select_onscreen_window ( record ) ;
      curs_pos ( record , record -> x_curs , record -> y_curs , 0 ) ;
      erase_rect ( record , & ( record -> its_bits . bounds ) ) ;
#if CLIP_RECT_ONLY
      empty_rect ( & ( record -> invalid_rect ) ) ;
#else
      SetEmptyRgn ( record -> invalid_part ) ;
#endif
      curs_pos ( record , record -> x_curs , record -> y_curs , 1 ) ;

      return noErr ;
}


/*
 * Resize the area - clears and reallocates the bitmap.
 */
pascal short
resize_tty_area ( WindowPtr window , short x_size , short y_size ) {
RECORD ( record ) ;

      record -> x_size = x_size ;
      record -> y_size = y_size ;

      return force_tty_coordinate_system_recalc ( window ) ;
}


/*
 * Echo to the user if echo mode on
 */
static void
do_add_input_character ( tty_record * record , unsigned char character ) {
      if ( 0L != ( record -> attribute [ TTY_ATTRIB_CURSOR ] & TA_ECHO_INPUT ) ) {
            add_tty_char ( record -> its_window , character ) ;
      }
}


#if TTY_INPUT
/*
 * Add a key in the queue.
 */
static short
do_add_key ( tty_record * record , long message ) {
      if ( record -> input_buffer_len >= record -> input_buffer_limit ) {
            do_tty_beep ( record ) ;
            return general_failure ;
      } else {
/*
 * If input is cooked, we should fix up this here line to allow line editting...
 */
            record -> input_buffer [ record -> input_buffer_len ++ ] = message & 0xff ;
            do_add_input_character ( record , message & 0xff ) ;
            return noErr ;
      }
}
#endif


/*
 * Add a key in the queue.
 */
static short
do_cmd_key ( tty_record * record , EventRecord * event ) {
      pascal void ( * callback ) ( EventRecord * event , WindowPtr window ) =
            ( pascal void ( * ) ( EventRecord * , WindowPtr ) )
            record -> attribute [ TTY_COMMAND_KEY_CALLBACK ] ;

      if ( callback ) {
            ( * callback ) ( event , record -> its_window ) ;
            return noErr ;
      }
      return general_failure ;
}


/*
 * Handle a tty event:
 * Updates, pertaining to our window.
 * Key downs, entered into the queue (if we are frontmost)
 */
pascal short
handle_tty_event ( WindowPtr window , EventRecord * event ) {
RECORD ( record ) ;

      update_tty ( window ) ;

      if ( 0L != ( record -> attribute [ TTY_ATTRIB_CURSOR ] & TA_BLINKING_CURSOR ) ) {
            if ( event -> when > record -> last_cursor + GetCaretTime ( ) ) {
                  curs_pos ( record , record -> x_curs , record -> y_curs ,
                        ! record -> curs_state ) ;
                  record -> last_cursor = event -> when ;
            }
      }

      switch ( event -> what ) {
      case updateEvt :
            if ( event -> message == ( long ) window ) {
                  BeginUpdate ( window ) ;
                  
                  /* Why do we have to erase the same area we are about to
                        do a copy_bits to?
                        
                  erase_rect ( record , & ( record -> its_bits . bounds ) ) ;
                  
                  */
                  
                  tty_environment_changed ( window ) ;
                  s_err = image_tty ( window ) ;
                  EndUpdate ( window ) ;
                  return s_err ;
            }
            break ;
#if TTY_INPUT
      case keyDown :
      case autoKey :
            if ( FrontWindow ( ) == window ) {
                  if ( event -> modifiers & cmdKey ) {
                        return do_cmd_key ( record , event ) ;
                  } else {
                        return do_add_key ( record , event -> message ) ;
                  }
            }
            break ;
#endif
      deafult :
            break ;
      }

      return general_failure ;
}


/*
 * Draw an image of the tty - used for update events and can be called
 * for screen dumps.
 */
pascal short
image_tty ( WindowPtr window ) {
Rect r ;
RECORD ( record ) ;

      select_onscreen_window ( record ) ;
      copy_bits ( record , & ( record -> its_bits . bounds ) , srcCopy , (RgnHandle) 0 ) ;
      if ( record -> curs_state ) {

            pos_rect ( record , & r , record -> x_curs , record -> y_curs ,
                  record -> x_curs , record -> y_curs ) ;
            InvertRect ( & r ) ;
      }

      return noErr ;
}


#if TTY_INPUT

/*
 * Read a character depending on the input mode
 */
pascal short
getchar_tty ( WindowPtr window , short * character ) {
RECORD ( record ) ;

      if ( 0L != ( record -> attribute [ TTY_ATTRIB_CURSOR ] & TA_RAW_INPUT ) ) {
            while ( ! record -> input_buffer_len ) {
            EventRecord er ;

                  WaitNextEvent ( -1 , & er , GetCaretTime ( ) , (char *)0 ) ;
                  if ( handle_tty_event ( window , & er ) ) {
                        switch ( er . what ) {
                        default :
                              break ;
                        }
                  }
            }
            * character = 
      } else {

      }
}

#endif /* TTY_INPUT */

#if EXTENDED_SUPPORT
/*
 * Delete or insert operations used by many terminals can bottleneck through
 * here. Note that the order of executin for row/colum insertions is NOT
 * specified. Negative values for num_ mean delete, zero means no effect.
 */
pascal short
mangle_tty_rows_columns ( WindowPtr window , short from_row , short num_rows ,
      short from_column , short num_columns ) {
Rect r ;
RgnHandle rh = NewRgn ( ) ;
RECORD ( record ) ;

      update_tty ( window ) ; /* Always make sure screen is OK */
      curs_pos ( record , record -> x_curs , record -> y_curs , 0 ) ;

      if ( num_rows ) {
            pos_rect ( record , & r , 0 , from_row , record -> x_size - 1 ,
                  record -> y_size - 1 ) ;
            select_offscreen_port ( record ) ;
            ScrollRect ( & r , 0 , num_rows * record -> row_height , rh ) ;
            EraseRgn ( rh ) ;
            SetEmptyRgn ( rh ) ;
            select_onscreen_window ( record ) ;
            ScrollRect ( & r , 0 , num_rows * record -> row_height , rh ) ;
            EraseRgn ( rh ) ;
            SetEmptyRgn ( rh ) ;
      }
      if ( num_columns ) {
            pos_rect ( record , & r , from_column , 0 , record -> x_size - 1 ,
                  record -> y_size - 1 ) ;
            select_offscreen_port ( record ) ;
            ScrollRect ( & r , num_columns * record -> char_width , 0 , rh ) ;
            EraseRgn ( rh ) ;
            SetEmptyRgn ( rh ) ;
            select_onscreen_window ( record ) ;
            ScrollRect ( & r , num_columns * record -> char_width , 0 , rh ) ;
            EraseRgn ( rh ) ;
            SetEmptyRgn ( rh ) ;
      }
      DisposeRgn ( rh ) ;
      if ( record -> x_curs >= from_column ) {
            record -> x_curs += num_columns ;
      }
      if ( record -> y_curs >= from_row ) {
            record -> y_curs += num_rows ;
      }
      curs_pos ( record , record -> x_curs , record -> y_curs , 1 ) ;

      return noErr ;
}


/*
 * Clear an area
 */
pascal short
clear_tty_window ( WindowPtr window , short from_x , short from_y ,
      short to_x , short to_y ) {
Rect r ;
RECORD ( record ) ;

      if ( from_x > to_x || from_y > to_y ) {
            return general_failure ;
      }
      pos_rect ( record , & r , from_x , from_y , to_x , to_y ) ;
      select_offscreen_port ( record ) ;
      erase_rect ( record , & r ) ;
      accumulate_rect ( record , & r ) ;
      if ( DRAW_DIRECT ) {
            update_tty ( window ) ;
      } else
            select_onscreen_window ( record ) ;
}


/*
 * Frame an area in an aesthetically pleasing way.
 */
pascal short
frame_tty_window ( WindowPtr window , short from_x , short from_y ,
      short to_x , short to_y , short frame_fatness ) {
Rect r ;
RECORD ( record ) ;

      if ( from_x > to_x || from_y > to_y ) {
            return general_failure ;
      }
      pos_rect ( record , & r , from_x , from_y , to_x , to_y ) ;
      select_offscreen_port ( record ) ;
      PenSize ( frame_fatness , frame_fatness ) ;
      FrameRect ( & r ) ;
      PenNormal ( ) ;
      accumulate_rect ( record , & r ) ;
      if ( DRAW_DIRECT ) {
            update_tty ( window ) ;
      } else
            select_onscreen_window ( record ) ;
}


/*
 * Highlighting a specific part of the tty window
 */
pascal short
invert_tty_window ( WindowPtr window , short from_x , short from_y ,
      short to_x , short to_y ) {
Rect r ;
RECORD ( record ) ;

      if ( from_x > to_x || from_y > to_y ) {
            return general_failure ;
      }
      pos_rect ( record , & r , from_x , from_y , to_x , to_y ) ;
      select_offscreen_port ( record ) ;
      InvertRect ( & r ) ;
      accumulate_rect ( record , & r ) ;
      if ( DRAW_DIRECT ) {
            update_tty ( window ) ;
      } else
            select_onscreen_window ( record ) ;
}


static void
canonical_rect ( Rect * r , short x1 , short y1 , short x2 , short y2 ) {
      if ( x1 < x2 ) {
            if ( y1 < y2 ) {
                  SetRect ( r , x1 , x2 , y1 , y2 ) ;
            } else {
                  SetRect ( r , x1 , x2 , y2 , y1 ) ;
            }
      } else {
            if ( y1 < y2 ) {
                  SetRect ( r , x2 , x1 , y1 , y2 ) ;
            } else {
                  SetRect ( r , x2 , x1 , y2 , y1 ) ;
            }
      }
}


/*
 * Line drawing - very device dependent
 */
pascal short
draw_tty_line ( WindowPtr window , short from_x , short from_y ,
      short to_x , short to_y ) {
Rect r ;
RECORD ( record ) ;

      select_offscreen_port ( record ) ;
      MoveTo ( from_x , from_y ) ;
      LineTo ( to_x , to_y ) ;
      canonical_rect ( & r , from_x , from_y , to_x , to_y ) ;
      accumulate_rect ( record , & r ) ;
      if ( DRAW_DIRECT ) {
            update_tty ( window ) ;
      } else
            select_onscreen_window ( record ) ;
}


#endif /* EXTENDED_SUPPORT */

Generated by  Doxygen 1.6.0   Back to index