/*	File v_maccur.c:
 *		Machine specific part of curses implementation for Macintosh.
 *		Also includes rudimentary replacements for stdio functions.
 *
 * 	Copyright (c) 1994
 *	by Robert Zimmerman
 *
 *  This code may be included in any work, public or private, with the
 *  exception of creating a commercial curses-compatible subroutine
 *  library.  (In other words, use the code all you want, but please don't
 *  rip off the author by reselling this code as your own).
 *
 */

#include <AppleEvents.h>
#include <limits.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include "curses.h"

/****
* Resources expected by this code are:
*	An ALRT and DITL to put up alerts with the function maccur_cpu_share (see macro ALERT_RSRC_ID).
*	A MENU and MBAR to draw the menu (which includes only the apple menu) (see macro MENU_RES_ID).
*
*	Optionally, an ALRT and DITL for the "About x..." menu item (see variable _maccur_about_res_id).
****/

/****
* First some macros for font, screen setup, etc:
****/

#define X_MARGIN 2
#define Y_MARGIN 4
#define Y_PIX_WIN (rowheight_pix * _maccur_lines + Y_MARGIN)
#define X_PIX_WIN (charwid_pix * _maccur_cols + 2 * X_MARGIN)

/****
* ROW/COL_TO_PIX determine pen position to write character at row/col.
* First row/column is 0/0.
****/

#define COL_TO_PIX(col) ((col) * charwid_pix + X_MARGIN)
#define ROW_TO_PIX(row) (((row) + 1) * rowheight_pix)

#define MAX_OUT_STRSEG 132			/* Max number of parts of a single character row (distinguished by writing attrib. */
#define MAX_TYPEAHEAD 32			/* Number of keystrokes that will be buffered. */
#define FLASH_TICKS 8				/* Flash cursor 2x second. */
#define WINDO_TOP_ON_SCREEN 40		/* Default position of window on screen. */
#define WINDO_LEFT_ON_SCREEN 5
#define ALERT_RSRC_ID 128			/* ALRT id for generalize alert. */
#define MENU_RES_ID 128				/* MBAR id for putting up (not much of a) menu. */

#define TST_BIT(i, b) ((i) & (1 << (b)))
#define SET_BIT(b) (1 << (b))

enum {
	MACCUR_RETURN_ON_IDLE_BIT,
	MACCUR_RETURN_ON_CHAR_BIT
};

typedef struct key_evnt_strct {				/* Struct in which to buffer keyboard events. */
	long msg;
	short mod;
} KEY_EVENT;

static KEY_EVENT typebuf[MAX_TYPEAHEAD];	/* Buffer for keyboard events. */

/****
* The following can be modified before curses is initialized
* in order to customize display, etc:
****/

int _maccur_cols = 80;						/* Window will be initialized to this size. */
int _maccur_lines = 25;
char *_maccur_font_name = "Monaco";			/* Font to use (C string). */
int _maccur_font_size = 9;					/* Size of font. */
int _maccur_handle_break_option = MACCUR_EXIT_ON_BREAK;	/* This determines what happens on CMD-. (see enum in macurses.h). */
int _maccur_about_res_id = 128;					/* Resource id alert to display if About... is chosen from apple menu. */
char *_maccur_pgm_name = "Curses";			/* Program name for about and window title. */
int _maccur_fg_sleep_ticks = 1;				/* How much can WaitNextEvent sleep in foreground. */
int _maccur_bg_sleep_ticks = 2;				/* How much can WaitNextEvent sleep in background. */
int _maccur_io_sleep_dvsr = 1;				/* How many of the output calls (maccur_printf, etc) call maccur_cpu_share. */
											/*   (1/_maccur_io_sleep_dvsr of output calls will call maccur_cpu_share). */

static int rowheight_pix;					/* Screen dimensions for each character. */
static int charwid_pix;
static short font_num;						/* Writing font number. */
static short font_res_id;					/* Font resource ID. */
static int show_cursor_flag;				/* TRUE if cursor should be flashed. */
static Rect cursor_rect;					/* Cursor, as a Rect. */
static WindowPtr the_windo;					/* Program window. */
static long last_flash_when;				/* Keep track of when cursor flashed. */
static int in_background_flag;				/* Keep track of program foreground/bkground state. */
static int cursor_inverted;					/* True if cursor is currently inverted. */
static int typeahead_cnt;					/* How many characters are waiting in typebuf. */
static int typeahead_indx;					/* Where does next character go in typebuf. */
static int outchar_indx;					/* Where does next character come out of typebuf. */
static chtype *screen_char_arry;			/* Array holding screen contents. */
static FontInfo fnt_info;					/* Description of writing font. */
static int maccur_about_mitm;				/* 1 if there is an About... item in apple menu. */
static int maccur_has_menu;					/* True if MBAR resource was found and menu put up. */

static int direct_curs_pos_row;				/* Keep track of cursor position when in direct (non-curses) mode. */
static int direct_curs_pos_col;

static int cpu_share_rot_cnt;				/* When this reaches _maccur_io_sleep_dvsr its time to share CPU. */

/********
**
**	maccur_init_toolbox:  Initialize mac stuff.
**
**
********/

static void maccur_init_toolbox(void)
{
	InitGraf(&QD(thePort));
	InitFonts();
	FlushEvents(everyEvent, 0);
	InitWindows();
	InitMenus();
	TEInit();
	InitDialogs(0L);
	InitCursor();
	MaxApplZone();
}

/********
**
**	event_do_nothing:  Do nothing in response to apple events.
**
**
********/

static pascal OSErr event_do_nothing(
const AppleEvent *theAppleEvent,
AppleEvent *reply,
long refCon
) {
	return 0;
}

/********
**
**	quit_app:  Quit application in response to apple event.
**		This is fairly severe -- Should put up a dialogue box?
**
**
********/

static pascal OSErr quit_app(
const AppleEvent *theAppleEvent,
AppleEvent *reply,
long refCon
) {
	ExitToShell();
	return 0;
}

/********
**
**	maccur_init_event: Initialize handlers (such as they are) for
**		required apple events.
**
**
********/

static void maccur_init_event(void)
{
	AEInstallEventHandler(kCoreEventClass, kAEOpenApplication,
					event_do_nothing, 0L, FALSE);
	AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments,
					event_do_nothing, 0L, FALSE);
	AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments,
					event_do_nothing, 0L, FALSE);
	AEInstallEventHandler(kCoreEventClass, kAEQuitApplication,
					quit_app, 0L, FALSE);
}

/********
**
**	maccur_get_font_info:  Search for resouce for desired font via FOND resource.
**		Return dimension of characters for sizing of window.  There must be an
**		easier way to determine the required window size b4 putting the window
**		up (and without putting a fixed size window in resource file).
**
**	Returns:
**		TRUE if font resource found, else FALSE.
**
**
********/

static int maccur_get_font_info(
char *font_name,			/* -R- Name of desired font. */
int font_pts,				/* -R- Size of desired font. */
short *font_num_ptr,		/* -W- Return font number. */
short *font_id_ptr,			/* -W- Return font resource id. */
int *rowheight_pix_ptr,		/* -W- Return height of font. */
int *charwid_pix_ptr		/* -W- Return width of font. */
) {
	ResType res_typ;
	Handle rsrc_ptr;
	FamRec *fnt_fam_ptr;
	FontAssoc *fnt_cnt_ptr;
	AsscEntry *fnt_assc_list_ptr;
	FontRec *fnt_rec_ptr;
	int i;
	char buf[256];

	memcpy(&res_typ, "FOND", sizeof(res_typ));
	if ((rsrc_ptr = GetNamedResource(res_typ, (ConstStr255Param) font_name)) == NULL)
		return FALSE;

	GetResInfo(rsrc_ptr, font_id_ptr, &res_typ, (unsigned char *) buf);
	if (ResError() != noErr) return FALSE;

	fnt_fam_ptr = *((FamRec **)rsrc_ptr);
	fnt_cnt_ptr = (FontAssoc *)(fnt_fam_ptr + 1);
	fnt_assc_list_ptr = (AsscEntry *)(fnt_cnt_ptr + 1);

	*font_num_ptr = fnt_fam_ptr->ffFamID;

	for (i = 0; i < fnt_cnt_ptr->numAssoc + 1; i++)
	{
		if ((fnt_assc_list_ptr + i)->fontSize == font_pts &&
				(fnt_assc_list_ptr + i)->fontStyle == 0)
			break;
	}

	if (i == fnt_cnt_ptr->numAssoc + 1) return FALSE;

	fnt_rec_ptr = NULL;
	if ((fnt_assc_list_ptr + i)->fontID == *font_id_ptr * 128 + font_pts)
	{
		memcpy(&res_typ, "FONT", sizeof(res_typ));
		if ((rsrc_ptr = GetResource(res_typ, *font_id_ptr)) != NULL)
			fnt_rec_ptr = *((FontRec **)rsrc_ptr);
	}

	if (fnt_rec_ptr != NULL)
	{
		memcpy(&res_typ, "NFNT", sizeof(res_typ));
		if ((rsrc_ptr = GetResource(res_typ, *font_id_ptr)) != NULL)
			fnt_rec_ptr = *((FontRec **)rsrc_ptr);
	}

	if (fnt_rec_ptr == NULL) return FALSE;

	*rowheight_pix_ptr = fnt_rec_ptr->fRectHeight;
	*charwid_pix_ptr = fnt_rec_ptr->fRectWidth;

	return TRUE;
}

/********
**
**	maccur_init_window: Size and open curses window.
**
**
********/

static void maccur_init_window(void)
{
	Rect windo_rect;
	ResType font_res;
	FontRec *fnt_rec_ptr;
	Handle rsrc_ptr;
	char windo_titl[128], font_name[32];

	font_name[0] = strlen(_maccur_font_name);							/* Copy font name to local pascal string. */
	strcpy(font_name + 1, _maccur_font_name);

	if (_maccur_font_size <= 0) _maccur_font_size = 9;					/* Make sure font size is reasonable. */

	windo_rect.top = WINDO_TOP_ON_SCREEN;
	windo_rect.left = WINDO_LEFT_ON_SCREEN;
	if (maccur_get_font_info(font_name, _maccur_font_size,				/* Try to find font dimensions before putting up window. */
								&font_num, &font_res_id,
								&rowheight_pix, &charwid_pix))
	{
		windo_rect.bottom = windo_rect.top + Y_PIX_WIN;
		windo_rect.right = windo_rect.left + X_PIX_WIN;
	}
	else
	{
		GetFNum((ConstStr255Param) font_name, &font_num);				/* If can't find them, put it up and then resize it. */
		if (font_num == 0)
		{
			maccur_alert_msg("Could not access font '%s'!",
								_maccur_font_name);
			ExitToShell();
		}
		windo_rect.bottom = windo_rect.top + 10;
		windo_rect.right = windo_rect.left + 10;
	}

	strcpy(windo_titl, (_maccur_pgm_name == NULL ||
								*_maccur_pgm_name == '\0') ?
									"Curses" : _maccur_pgm_name);
	if ((the_windo = NewWindow(nil, &windo_rect,
								CtoPstr(windo_titl), TRUE,
								noGrowDocProc, (WindowPtr)-1L,
								FALSE, (long)nil)) == nil)
	{
		maccur_alert_msg("Could not initialize program window!");
		ExitToShell();
	}

	SetPort(the_windo);

	if (font_num != 0)
		TextFont(font_num);

	TextSize(_maccur_font_size);
	GetFontInfo(&fnt_info);
	if (charwid_pix != fnt_info.widMax ||
			rowheight_pix != fnt_info.ascent + fnt_info.descent + fnt_info.leading)
	{
		charwid_pix = fnt_info.widMax;
		rowheight_pix = fnt_info.ascent + fnt_info.descent + fnt_info.leading;
		SizeWindow(the_windo, X_PIX_WIN, Y_PIX_WIN, TRUE);
	}

	/* Could check if bold-condense is same size as normal to allow/dissallow bold mode? */
	ShowWindow(the_windo);
}

/********
**
**	maccur_init_menus:  Set up menu bar (for now, only the apple menu).
**
**
********/

static void maccur_init_menus(void)
{
	Handle menu_bar_hndl;
	MenuHandle menu_hndl;
	char about_str[128];

	if ((menu_bar_hndl = GetNewMBar(MENU_RES_ID)) != NULL)
	{
		SetMenuBar(menu_bar_hndl);
		menu_hndl = GetMHandle(MENU_RES_ID);
		if (_maccur_about_res_id != 0)
		{
			sprintf(about_str, "About %s...", _maccur_pgm_name);
			SetItem(menu_hndl, 1, CtoPstr(about_str));
			maccur_about_mitm = 1;
		}
		else
		{
			DelMenuItem(menu_hndl, 1);
			maccur_about_mitm = 0;
		}
		AddResMenu(menu_hndl, 'DRVR');

		DrawMenuBar();
		maccur_has_menu = TRUE;
	}
	else maccur_has_menu = FALSE;
}

/********
**
**	maccur_draw_text_line:  Write a single line of text to window.  Input text is
**		type chtype, with attribute info.
**
**
********/

static void maccur_draw_text_line(
chtype *line_ptr,		/* -R- Pointer to text to write. */
int n,					/* -R- Number of characters. */
int row,				/* -R- Start at what row on screen (first row is 0). */
int col					/* -R- Start at what column on screen (first col is 0). */
) {
	int i, first_pass_flag, j, attr, previous_attr, txt_face;
	char out_str[MAX_OUT_STRSEG];

	SetPort(the_windo);

	MoveTo(COL_TO_PIX(col), ROW_TO_PIX(row));

	for (i = 0, first_pass_flag = TRUE; i < n; )						/* Loop until string is written, writing it in segments */
	{																	/*   that all have the same attribute. */
		for (j = 0, attr = (*(line_ptr + i) & A_ATTRIBUTES);
				i < n && j < MAX_OUT_STRSEG && attr == (*(line_ptr + i) & A_ATTRIBUTES);
				i++, j++)
			out_str[j] = (*(line_ptr + i) & A_CHARTEXT);

		if (attr != previous_attr || first_pass_flag)					/* Determine/set attribute for this segment... */
		{
			if (attr & A_REVERSE || attr & A_STANDOUT)					/* Should do standout w/ hilite? */
				TextMode(notSrcCopy);
			else
				TextMode(srcCopy);

			txt_face = normal;
			if (attr & A_UNDERLINE)
				txt_face |= underline;

			if (attr & A_BOLD)											/* It is assument that bold condense is same size as normal. */
				txt_face |= (bold | condense);

			TextFace(txt_face);
			previous_attr = attr;
		}
		DrawText(out_str, 0, j);
		first_pass_flag = FALSE;
	}
}

/********
**
**	maccur_flash_cursor:  Flash cursor.
**
**
********/

static void maccur_flash_cursor(void)
{
	InvertRect(&cursor_rect);
	cursor_inverted = !cursor_inverted;
}

/********
**
**	maccur_unflash_cursor:  Unhighlight cursor if it is highlighted.
**
**
********/

static void maccur_unflash_cursor(void)
{
	if (cursor_inverted) maccur_flash_cursor();
}

/********
**
**	maccur_set_cursor:  Set cursor_rect to reflect current cursor position.
**		Before moving it, un-invert it if necessary (based on cursor_inverted flag).
**
**
********/

static void maccur_set_cursor(
int row,		/* -R- Row for cursor. */
int col			/* -R- Column for cursor. */
) {

	if (cursor_inverted) maccur_flash_cursor();
	show_cursor_flag = TRUE;

	row = MIN(row, _maccur_lines - 1);
	col = MIN(col, _maccur_cols - 1);

	cursor_rect.bottom = ROW_TO_PIX(row) + fnt_info.descent + fnt_info.leading;
	cursor_rect.right = COL_TO_PIX(col + 1);
	cursor_rect.top = cursor_rect.bottom - rowheight_pix;
	cursor_rect.left = COL_TO_PIX(col);

}

/********
**
**	maccur_mouse_down_evnt:  Do something about mouse down event.  All that
**		is handled is the apple menu, dragging the window, and system clicks.
**
**
********/

static void maccur_mouse_down_evnt(
EventRecord *event_ptr		/* -R- The event details. */
) {
	WindowPtr wndo_ptr;
	MenuHandle apl_menu_hndl;
	Str255 nam;
	long menu_choice;
	short wnd_part, menu_id, menu_itm, num;

	wnd_part = FindWindow(event_ptr->where, &wndo_ptr);
	switch (wnd_part)
	{
		case inMenuBar:
			if ((menu_choice = MenuSelect(event_ptr->where)) != 0)
			{
				menu_id = HiWord(menu_choice);
				menu_itm = LoWord(menu_choice);

				if (menu_id == MENU_RES_ID)
				{
					if (menu_itm == maccur_about_mitm)					/* See if this is about... */
					{
						if (NoteAlert(_maccur_about_res_id, NULL) == -1)
							SysBeep(1);
					}
					else
					{
						apl_menu_hndl = GetMHandle(MENU_RES_ID);		/* If not, pass it on. */
						GetItem(apl_menu_hndl, menu_itm, nam);
						num = OpenDeskAcc(nam);
					}
				}
				HiliteMenu(0);
			}
			break;

		case inSysWindow:
			SystemClick(event_ptr, wndo_ptr);
			break;

		case inContent:
			break;

		case inDrag:
			DragWindow(wndo_ptr, event_ptr->where,
								&QD(screenBits).bounds);
			break;
	}
}

/********
**
**	maccur_event_loop: Main event loop.  Argument indicates whether it is to
**		return due to a null event or due to a key stroke.
**
**	Returns:
**		Reason for return (null or keystroke).
**
**
********/

static int maccur_event_loop(
int until_flags
) {
	EventRecord event;
	int i;
	long sleep;
	char c;

	if (TST_BIT(until_flags, MACCUR_RETURN_ON_CHAR_BIT))				/* If waiting for a character, don't need a lot of CPU. */
	{
		if (in_background_flag)											/* Don't need any in background. */
			sleep = LONG_MAX;
		else sleep = FLASH_TICKS;										/* Just need enough to flash cursor in foreground. */
	}
	else																/* If not waiting for a character, try to give up some time. */
	{																	/*   but maybe not a lot. */
		if (in_background_flag)
			sleep = _maccur_bg_sleep_ticks;
		else sleep = _maccur_fg_sleep_ticks;
	}

	while (1)															/* Loop until return condition is met. */
	{
		if (WaitNextEvent(everyEvent, &event, sleep, nil))
		{

#ifdef __MWERKS__
		  if (!SIOUXHandleOneEvent(&event)) {
#endif

			switch (event.what)
			{
				case kHighLevelEvent:
					AEProcessAppleEvent(&event);
					break;

				case mouseDown:
					if (maccur_has_menu)
						maccur_mouse_down_evnt(&event);
					break;

				case keyDown:
				case autoKey:
					if ((event.modifiers & cmdKey) != 0)				/* Ignore all command keys except CMD-. */
					{
						if ((event.message & charCodeMask) == '.')		/* This may exit right away or simulate a control-C interrupt. */
						{
							switch (_maccur_handle_break_option)
							{
								case MACCUR_EXIT_ON_BREAK:
									ExitToShell();
									break;

								case MACCUR_SIGNAL_BREAK:
									raise(SIGINT);
									break;
							}
						}
						else break;
					}

					if (typeahead_cnt >= MAX_TYPEAHEAD)					/* Other keystrokes go into typeahead (if there is room). */
						SysBeep(1);										/* Typeahead buf is circular. */
					else
					{
						typebuf[typeahead_indx % MAX_TYPEAHEAD].msg = event.message;
						typebuf[typeahead_indx % MAX_TYPEAHEAD].mod = event.modifiers;
					}
					typeahead_indx++;
					typeahead_cnt++;

					if (TST_BIT(until_flags, MACCUR_RETURN_ON_CHAR_BIT))/* Return if keystroke was what was wanted. */
						return MACCUR_RETURN_ON_CHAR_BIT;
					break;

				case updateEvt:
					BeginUpdate((WindowPtr)(event.message));

					EraseRect(&cursor_rect);							/* This may be unnecessary??? */
					for (i = 0; i < _maccur_lines; i++)					/* Redraw the screen. */
						maccur_draw_text_line(screen_char_arry + i * _maccur_cols,
											_maccur_cols, i, 0);
					cursor_inverted = FALSE;

					EndUpdate((WindowPtr)(event.message));
					break;

				case osEvt:												/* Switch background/foreground. */
					if ((event.message & suspendResumeMessage) == resumeFlag)
						in_background_flag = FALSE;
					else
					{
						in_background_flag = TRUE;
						maccur_unflash_cursor();						/* Turn cursor off in the background. */
					}
					break;

			}
#ifdef __MWERKS__
		  }
#endif
		}
		else if (TST_BIT(until_flags, MACCUR_RETURN_ON_IDLE_BIT))		/* If just letting CPU run, this is the cue to split. */
			return MACCUR_RETURN_ON_IDLE_BIT;

		if (!in_background_flag && show_cursor_flag &&					/* See if its time to flash the cursor. */
				event.when - last_flash_when > FLASH_TICKS)
		{
			if (event.what != osEvt)
				maccur_flash_cursor();
			last_flash_when = event.when;
		}
	}
}

/****
* The following "keypad map" maps various non-ascii keystrokes to
* their curses KEY_xxx equivalents.  This could be made global so
* that user supplied replacements could be linked in (but who would
* want to go to all that trouble).
****/

#define KEYMAP_BASE 0x33												/* Key number of first key to translate. */
#define KEYMAP_TOP (KEYMAP_BASE + sizeof(dflt_keypad_map)/sizeof(KEYPAD_MAP))
#define KEYMAP_NOCODE 0													/* If this is key translation, return ascii code for that key. */

typedef struct keypad_map_strct {	/* Determine what to do with key/modifier: */
	chtype basic_code;				/* This is translation of unmodified key. */
	chtype shift_code;				/* Translation of key with shift. */
	chtype opt_code;				/* Translation of key with option. */
	chtype shift_opt_code;			/* Translation of key with shift-option. */
} KEYPAD_MAP;

static KEYPAD_MAP dflt_keypad_map[] = {
/* 33 delete */		{KEY_BACKSPACE, KEY_BACKSPACE, KEY_BACKSPACE, KEY_BACKSPACE},
/* 34 */			{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 35 */			{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 36 */			{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 37 */			{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 38 */			{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 39 */			{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 3A */			{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 3B */			{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 3C */			{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 3D */			{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 3E */			{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 3F */			{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 40 */			{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 41 KP-. */		{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 42 rt arrow (Mac +) */	{KEY_RIGHT, KEY_SRIGHT, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 43 KP-* */		{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 44 */			{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 45 KP-+ */		{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 46 left arrow (Mac +) */	{KEY_LEFT, KEY_SLEFT, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 47 clear */		{KEY_CLEAR, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 48 down arrow (Mac +) */	{KEY_DOWN, KEY_NPAGE, KEY_END, KEYMAP_NOCODE},
/* 49 */			{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 4A */			{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 4B KP-/ */		{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 4C enter */		{KEY_ENTER, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 4D up arrow (Mac +) */	{KEY_UP, KEY_PPAGE, KEY_HOME, KEYMAP_NOCODE},
/* 4E KP-- */		{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 4F */			{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 50 */			{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 51 KP-= */		{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 52 KP-0 */		{KEYMAP_NOCODE, KEYMAP_NOCODE, KEY_F(0), KEY_F(10)},
/* 53 KP-1 */		{KEY_C1, KEYMAP_NOCODE, KEY_F(1), KEY_F(11)},
/* 54 KP-2 */		{KEY_DOWN, KEYMAP_NOCODE, KEY_F(2), KEY_F(12)},
/* 55 KP-3 */		{KEY_C3, KEYMAP_NOCODE, KEY_F(3), KEY_F(13)},
/* 56 KP-4 */		{KEY_LEFT, KEYMAP_NOCODE, KEY_F(4), KEY_F(14)},
/* 57 KP-5 */		{KEY_B2, KEYMAP_NOCODE, KEY_F(5), KEY_F(15)},
/* 58 KP-6 */		{KEY_RIGHT, KEYMAP_NOCODE, KEY_F(6), KEY_F(16)},
/* 59 KP-7 */		{KEY_A1, KEYMAP_NOCODE, KEY_F(7), KEY_F(17)},
/* 5A */			{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 5B KP-8 */		{KEY_UP, KEYMAP_NOCODE, KEY_F(8), KEY_F(18)},
/* 5C KP-9 */		{KEY_A3, KEYMAP_NOCODE, KEY_F(9), KEY_F(19)},
/* 5D */			{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 5E */			{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 5F */			{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 60 F5 */			{KEY_F(5), KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 61 F6 */			{KEY_F(6), KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 62 F7 */			{KEY_F(7), KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 63 F3 */			{KEY_F(3), KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 64 F8 */			{KEY_F(8), KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 65 F9 */			{KEY_F(9), KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 66 */			{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 67 F11 */		{KEY_F(11), KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 68 */			{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 69 F13 */		{KEY_F(13), KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 6A */			{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 6B F14 */		{KEY_F(14), KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 6C */			{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 6D F10 */		{KEY_F(10), KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 6E */			{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 6F F12 */		{KEY_F(12), KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 70 */			{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 71 F15 */		{KEY_F(15), KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 72 ins */		{KEY_IC, KEY_IL, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 73 home */		{KEY_HOME, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 74 pgup */		{KEY_PPAGE, KEY_SR, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 75 fwd del */	{KEY_DC, KEY_DL, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 76 F4 */			{KEY_F(4), KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 77 end */		{KEY_END, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 78 F2 */			{KEY_F(2), KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 79 pgdn */		{KEY_NPAGE, KEY_SF, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 7A F1 */			{KEY_F(1), KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 7B left arrow */	{KEY_LEFT, KEY_SLEFT, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 7C rt arrow */	{KEY_RIGHT, KEY_SRIGHT, KEYMAP_NOCODE, KEYMAP_NOCODE},
/* 7D down arrow */	{KEY_DOWN, KEY_NPAGE, KEY_END, KEYMAP_NOCODE},
/* 7E up arrow */	{KEY_UP, KEY_PPAGE, KEY_HOME, KEYMAP_NOCODE},
/* 7F */			{KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE, KEYMAP_NOCODE}
};

/********
**
**	maccur_key_trans:  Return curses key code based on keystroke event.
**		If key has no entry in translation table, or entry is KEYMAP_NOCODE,
**		return ascii value.
**
**	Returns:
**		Character translation of keystroke (type chtype).
**
**
********/

static chtype maccur_key_trans(
KEY_EVENT *ch_evnt_ptr,
int keypad_enabled_flag
) {
	chtype c, c_ascii, key;

	c_ascii = ch_evnt_ptr->msg & charCodeMask;
	key = (ch_evnt_ptr->msg & keyCodeMask) >> 8;

	if (keypad_enabled_flag && key >= KEYMAP_BASE && key < KEYMAP_TOP)
	{
		if ((ch_evnt_ptr->mod & shiftKey) && (ch_evnt_ptr->mod & optionKey))
			c = (dflt_keypad_map + (key - KEYMAP_BASE))->shift_opt_code;
		else if ((ch_evnt_ptr->mod & shiftKey))
			c = (dflt_keypad_map + (key - KEYMAP_BASE))->shift_code;
		else if ((ch_evnt_ptr->mod & optionKey))
			c = (dflt_keypad_map + (key - KEYMAP_BASE))->opt_code;
		else
			c = (dflt_keypad_map + (key - KEYMAP_BASE))->basic_code;

		if (c == KEYMAP_NOCODE) c = c_ascii;
	}
	else
	{
		if (key == 0x4C) c = '\n';										/* Give the keypad enter key a better ascii translation. */
		else c = c_ascii;
	}

	return c;
}

/********
**
**	maccur_flush_typeahead:  Flush the typeahead buffer.
**
**
********/

void maccur_flush_typeahead(
void
) {

	typeahead_indx = typeahead_cnt = outchar_indx = 0;
}

/********
**
**	maccur_init: Entry point for initalization.  Sets LINES and COLS
**		to the size of the window that is opened up.
**
**	Returns:
**		TRUE if initialization is successful, else FALSE.
**
**
********/

bool maccur_init(void)
{
	int i, screen_chars;

	screen_chars = _maccur_cols * _maccur_lines;
	if (screen_char_arry == NULL)										/* Allow for multiple calls to this routine (especially */
	{																	/*   implicit ones for fake stdio). */
		maccur_init_toolbox();
		maccur_init_event();
		maccur_init_window();
		maccur_init_menus();

		LINES = _maccur_lines;
		COLS = _maccur_cols;

		if ((screen_char_arry = malloc(screen_chars * sizeof(chtype))) == NULL)
			return FALSE;
	}

	for (i = 0; i < screen_chars; i++)									/* Blank the screen. */
		*(screen_char_arry + i) = ' ';

	return TRUE;
}

/********
**
**	maccur_refresh: Refresh the screen for a curses window.
**
**	Returns:
**		ERR on error, else OK.
**
**
********/

int maccur_refresh(WINDOW *win, bool outflag)
{
	static int	next_cursor_col = 0;		/* Save cursor position for next window refresh. */
	static int	next_cursor_row = 0;		/*   (in case there are multiple calls before refresh and some have leaveok TRUE). */
	static int window_current_flag;			/* Set to true when window is updated from saved image. */
	static int cursor_enabled_flag;			/* Set to true if at least one window contributing to screen has a cursor. */
	int i, mov_size, maxx, maxy;
	chtype *src_ptr, *dst_ptr;
                                                                        /* Your comment here                                       */

	if (screen_char_arry == NULL) return ERR;							/* Initialization has not been done (bad). */

	if (win)															/* If window was passed, transfer chars from it. */
	{
		if (win->_begy < _maccur_lines && win->_begx < _maccur_cols)
		{
			dst_ptr = screen_char_arry + (win->_begy * _maccur_cols) +
								win->_begx;
			if (win->_flags & _ISPAD)
			{
				maxx = win->_pmap_maxx - win->_pmap_orgx;				/* For pads, get # rows and columns to display. */
				maxy = win->_pmap_maxy - win->_pmap_orgy;
				src_ptr = win->_y +										/* Point to 1st displayed character in pad. */
								((win->_pmap_orgy * win->_xdim) +
								win->_pmap_orgx);
			}
			else
			{
				maxx = win->_maxx;
				maxy = win->_maxy;
				src_ptr = win->_y;
			}
			mov_size = MIN(maxx, _maccur_cols - win->_begx) *
								sizeof(chtype);
			for (i = 0; i < maxy && i + win->_begy < _maccur_lines;		/* Copy text from window to screen image. */
								i++, dst_ptr += _maccur_cols,
									src_ptr += win->_xdim)
				memcpy(dst_ptr, src_ptr, mov_size); 
		}

		if (!win->_leave)												/* If cursor is not left off, */
		{
			next_cursor_row = _CURS_CURSOR_SCREEN_ROW(win);				/* Save its position. */
			next_cursor_col = _CURS_CURSOR_SCREEN_COL(win);
			cursor_enabled_flag = TRUE;									/* It will be enabled on next screen update. */
		}
		else if (window_current_flag)									/* If this is 1st refresh after update and cursor */
			cursor_enabled_flag = FALSE;								/*   disabled, indicate that.  Subsequent refreshes */
		else cursor_enabled_flag = cursor_enabled_flag || FALSE;		/*   or into flag so if cursor is enabled once it is shown. */
		window_current_flag = FALSE;
	}

	if (outflag)														/* If actual screen update is to be done... */
	{
		for (i = 0; i < _maccur_lines; i++)								/* Draw the characters. */
			maccur_draw_text_line(screen_char_arry + i * _maccur_cols,
								_maccur_cols, i, 0);
		if (cursor_enabled_flag)
		{
			cursor_inverted = FALSE;									/* Window was completely re-written, so cursor is gone. */
			maccur_set_cursor(next_cursor_row, next_cursor_col);
		}
		else show_cursor_flag = FALSE;
		window_current_flag = TRUE;
	}

	return OK;
}

/********
**
**	maccur_end:  Exit curses mode.
**
**	Returns:
**		OK.
**
**
********/

bool maccur_end(void)
{
	void maccur_noncurse_mode_scroll(void);

	maccur_noncurse_mode_scroll();										/* Scroll screen up a line and position cursor at bottom. */

	direct_curs_pos_row = _maccur_lines - 1;
	direct_curs_pos_col = 0;

	maccur_set_cursor(direct_curs_pos_row, direct_curs_pos_col);
	return OK;
}

/********
**
**	maccur_beep: Beep or flash the screen.
**
**
********/

void maccur_beep(
int flash_flag		/* -R- TRUE to flash instead of beep. */
) {
	if (flash_flag)
		SysBeep(0);
	else
		SysBeep(1);
}

/********
**
**	maccur_kbinp: Take a single character input.
**
**	Returns:
**		Character read, or ERR if no character ready or error.
**
**
********/

int maccur_kbinp(WINDOW *win, bool raw, bool cbreak)
{

	if (typeahead_cnt == 0 && win->_nodelay)							/* If caller is in a hurry don't make 'em wait */
		return ERR;

	if (typeahead_cnt == 0)												/* If need to wait for input... */
	{
		maccur_set_cursor(_CURS_CURSOR_SCREEN_ROW(win),					/* Set cursor to current input position. */
								_CURS_CURSOR_SCREEN_COL(win));			/*   (this will also un-invert it). */
		if (win->_leave)												/* If cursor is to be left off, set flag to false. */
			show_cursor_flag = FALSE;
		maccur_event_loop(SET_BIT(MACCUR_RETURN_ON_CHAR_BIT));
	}

	if (typeahead_cnt > 0)												/* There should be something to return now, so do it... */
	{
		typeahead_cnt--;
		if (outchar_indx >= MAX_TYPEAHEAD) outchar_indx = 0;
		return maccur_key_trans(typebuf + outchar_indx++, win->_use_keypad);
	}

	return ERR;
}

/********
**
**	maccur_alert_text:  Display a string in an alert box.
**		Trim white space and newlines from beginning and end of string first.
**
**
********/

static void maccur_alert_text(
char *txt
) {
	static char buf[256];
	int len;

	while (isspace(*txt))												/* Strip off leading white space. */
		txt++;
	len = strlen(txt);

	while (len > 0 && isspace(*(txt + len - 1)))						/* Strip off trailing white space. */
		len--;
	if (len > 255) len = 255;

	buf[0] = len;														/* Make it a pascal string. */
	memcpy(buf + 1, txt, len);

	ParamText((ConstStr255Param) buf, NULL, NULL, NULL);

	if (CountResources('ALRT') == 0)
	  return;

	Alert(ALERT_RSRC_ID, NULL);											/* Display as an alert. */
}

/********
**
**	maccur_alert_msg:  Display a string as an alert message, after
**		formatting it with sprintf.
**
**
********/

void maccur_alert_msg(
char *fmt,
...
) {
    va_list args;
    int rtn;

	if (screen_char_arry == NULL)
		maccur_init();

    va_start(args, fmt);
    rtn = vsprintf(_curses_prntw, fmt, args);
    va_end(args);

	if (rtn < 0) strcpy(_curses_prntw, "Error in curses");

	maccur_alert_text(_curses_prntw);
}

/********
**
**	maccur_cpu_share:  Call the event manager so CPU time can be shared.
**
**
********/

void maccur_cpu_share(void)
{
	maccur_event_loop(SET_BIT(MACCUR_RETURN_ON_IDLE_BIT));
}


/*	**************************************************************************	*/
/*									STDIO emulation								*/
/*																				*/
/*	The following routines can be used to emulate standard io calls to stdin	*/
/*	and stdout, placing the output in the curses window.						*/
/*																				*/
/*	**************************************************************************	*/


/********
**
**	maccur_noncurse_mode_scroll: Scroll the screen up a line.
**
**
********/

static void maccur_noncurse_mode_scroll(void)
{
	int i;
	chtype *scrn_ptr;

	memmove(screen_char_arry, screen_char_arry + _maccur_cols,					/* Scroll it up. */
								_maccur_cols * (_maccur_lines - 1) * sizeof(chtype));
	scrn_ptr = screen_char_arry + ((_maccur_lines - 1) * _maccur_cols);
	for (i = 0; i < _maccur_cols; i++)											/* Clear bottom line. */
		*(scrn_ptr + i) = ' ';

	for (i = 0; i < _maccur_lines; i++)
		maccur_draw_text_line(screen_char_arry + i * _maccur_cols,
								_maccur_cols, i, 0);
	cursor_inverted = FALSE;
}

/********
**
**	maccur_noncurse_mode_show_str:  Display a string on the screen.
**		Wrap strings that are too long and expand tabs.
**
**
********/

static void maccur_noncurse_mode_show_str(
char *str,					/* -R- String to display. */
int *row_pos_ptr,			/* -M- Passed as row to start string, updated to row of end of string. */
int *col_pos_ptr,			/* -M- Passed as column to start string, updated to column of end of string. */
int *scroll_cnt_ptr,		/* -W- Return number of lines up the screen was scrolled. */
bool show_str_flag,			/* -R- TRUE to display string, FALSE to just return cursor position at end of string. */
bool add_newline_flag,		/* -R- True if extra newline should be added after string is displayed. */
bool clear_to_eos_flag		/* -R- True to clear all of screen following displayed string. */
) {
	int nchars, char_pos, row_pos, col_pos, i, n_to_show,
		end_col_pos, move_down_flag;
	chtype *scrn_ptr;
	char *out_ptr, *cp;

	if (scroll_cnt_ptr != NULL)
		*scroll_cnt_ptr = 0;
	nchars = strlen(str);
	char_pos = 0;
	row_pos = *row_pos_ptr;
	col_pos = *col_pos_ptr;
	out_ptr = str;

	while (nchars > 0)													/* Loop while there are characters to write out. */
	{
		if (col_pos >= _maccur_cols)
		{
			col_pos = 0;
			row_pos++;
		}

		if (show_str_flag && row_pos >= _maccur_lines)					/* If writing position is past end of screen, scroll. */
		{
			maccur_noncurse_mode_scroll();
			if (scroll_cnt_ptr != NULL) (*scroll_cnt_ptr)++;
			row_pos = _maccur_lines - 1;
		}

		n_to_show = MIN(_maccur_cols - col_pos, nchars);				/* Determine how many chars to put in this line: */
		if ((cp = strchr(out_ptr, '\n')) != NULL)						/*   up to end of line, up to end of string, */
			n_to_show = MIN(n_to_show, cp - out_ptr);					/*   up to next newline, or up to tab. */
		if ((cp = strchr(out_ptr, '\t')) != NULL)
			n_to_show = MIN(n_to_show, cp - out_ptr);

		if (show_str_flag)
		{
			scrn_ptr = screen_char_arry + (row_pos * _maccur_cols) +
								col_pos;
			for (i = 0; i < n_to_show; i++)								/* Copy the characters out. */
				*(scrn_ptr + i) = *(out_ptr + i);
		}
		col_pos += n_to_show;

		move_down_flag = FALSE;
		if (*(out_ptr + n_to_show) == '\n')								/* If newline encountered, blank out rest of line */
		{																/*   and position to beginning of next. */
			if (show_str_flag)
			{
				scrn_ptr = screen_char_arry + (row_pos * _maccur_cols);
				while (col_pos < _maccur_cols)
					*(scrn_ptr + col_pos++) = ' ';
			}
			n_to_show++;												/* Skip over the '\n' */
			move_down_flag = TRUE;
		}
		else if (*(out_ptr + n_to_show) == '\t')						/* For a tab, blank up to the next tab stop. */
		{
			end_col_pos = col_pos + (_curses_tab_wid -					/* Blank fwd until col_pos becomes multiple of tab wid. */
								col_pos % _curses_tab_wid);
			end_col_pos = MIN(end_col_pos, _maccur_cols);				/* But don't wrap over into next line. */
			if (show_str_flag)
			{
				scrn_ptr = screen_char_arry + (row_pos * _maccur_cols);
				while (col_pos < end_col_pos)
					*(scrn_ptr + col_pos++) = ' ';
			}
			col_pos = end_col_pos;
			n_to_show++;												/* Skip over the '\t' */
		}

		if (show_str_flag)
			maccur_draw_text_line(screen_char_arry +					/* Redraw the modified line. */
								row_pos * _maccur_cols,
								_maccur_cols, row_pos, 0);

		if (move_down_flag)
		{
			col_pos = 0;												/* Move to next line. */
			row_pos++;
		}

		if (show_str_flag && row_pos >= _maccur_lines)					/* If writing position is past end of screen, scroll. */
		{
			maccur_noncurse_mode_scroll();
			if (scroll_cnt_ptr != NULL) (*scroll_cnt_ptr)++;
			row_pos = _maccur_lines - 1;
		}

		out_ptr += n_to_show;
		nchars -= n_to_show;
	}

	if (add_newline_flag || clear_to_eos_flag)							/* If extra newline is to be added after string output or */
	{																	/*   or screen is to be cleared up to end of screen. */
		if (show_str_flag)
		{
			int last_row, i_rw;

			if (clear_to_eos_flag)										/* Whether new line or cleareos, will be clearing rest */
				last_row = _maccur_lines;								/*   of current line and possibly others. */
			else last_row = row_pos + 1;

			for (i_rw = row_pos; i_rw < last_row; i_rw++)				/* Do next line or a bunch depending on clear_to_eos_flag. */
			{
				scrn_ptr = screen_char_arry + (i_rw * _maccur_cols);
				for (i = (i_rw == row_pos ? col_pos : 0);				/* Clear the line. */
						i < _maccur_cols; i++)
					*(scrn_ptr + i) = ' ';

				maccur_draw_text_line(screen_char_arry +				/* Redraw the modified line. */
								i_rw * _maccur_cols,
								_maccur_cols, i_rw, 0);
			}
		}

		if (add_newline_flag)											/* If actually echoing a newline. */
		{
			col_pos = 0;
			row_pos++;
			if (show_str_flag && row_pos >= _maccur_lines)				/* Scroll of necessary. */
			{
				maccur_noncurse_mode_scroll();
				if (scroll_cnt_ptr != NULL) (*scroll_cnt_ptr)++;
				row_pos = _maccur_lines - 1;
			}															/* (this automatically clears the last line of screen). */
		}
	}

	*row_pos_ptr = row_pos;
	*col_pos_ptr = col_pos;
}

/********
**
**	maccur_noncurse_mode_getstr:  Input a string, echoing input to screen,
**		allowing editing with left/right arrows, delete key, clear key
**		(or ctrl-u) to clear input, ctrl-e to move to end of input,
**		ctrl-d to return EOF.  Special single character mode does away
**		with most special line editing, and returns with the single char (ala getchar).
**
**	Returns:
**		Number of characters in, or EOF if CTRL-D entered.
**
**
********/

static int maccur_noncurse_mode_getstr(
char *str,					/* -W- Put the string read here. */
int max_chars,				/* -R- Size of above string (or -1 if unknown). */
int single_char_flag		/* -R- True if this is call for a single character. */
) {
	int begin_at, nchars, pos, key, c_ascii, cleareos_flag,
		redisplay_flag, disp_pos, disp_row, disp_col, scroll_cnt,
		rtn_val, string_ended_flag, add_newline_flag, save_char;

	begin_at = (direct_curs_pos_row * _maccur_cols) +					/* Remember initial cursor position. */
								direct_curs_pos_col;

	nchars = pos = rtn_val = 0;
	*str = '\0';
	string_ended_flag = FALSE;
	while (!string_ended_flag)
	{
		redisplay_flag = FALSE;
		cleareos_flag = FALSE;
		add_newline_flag = FALSE;

		if (typeahead_cnt == 0)											/* If need to wait for input... */
		{
			disp_row = begin_at/_maccur_cols;
			disp_col = begin_at % _maccur_cols;
			save_char = *(str + pos);
			*(str + pos) = '\0';
			maccur_noncurse_mode_show_str(str,							/* Measure string to determine cursor position at */
								&disp_row, &disp_col,					/*   input point (there might be tabs). */
								NULL, FALSE, FALSE, FALSE);
			*(str + pos) = save_char;

			if (disp_col >= _maccur_cols)								/* Move cursor to next line if it is past end of a line. */
			{
				disp_col = 0;
				disp_row++;
			}

			if (disp_row >= _maccur_lines)								/* Scroll if cursor off screen. */
			{
				maccur_noncurse_mode_scroll();
				disp_row = _maccur_lines - 1;
				begin_at -= _maccur_cols;
			}

			maccur_set_cursor(disp_row, disp_col);						/* Set cursor to current input position. */
			maccur_event_loop(SET_BIT(MACCUR_RETURN_ON_CHAR_BIT));		/* Get some text. */
			maccur_unflash_cursor();
		}

		if (typeahead_cnt > 0)											/* There should be at least one keystroke available. */
		{
			typeahead_cnt--;
			if (outchar_indx >= MAX_TYPEAHEAD) outchar_indx = 0;
			c_ascii = (typebuf + outchar_indx)->msg & charCodeMask;
			key = ((typebuf + outchar_indx)->msg & keyCodeMask) >> 8;
			outchar_indx++;

			switch (key)												/* See if key has special meaning. */
			{
				case 0x7D:												/* Up and down arrows (Mac plus and II). */
				case 0x7E:												/* All ignored. */
				case 0x4D:
				case 0x48:
					SysBeep(1);
					break;
		
				case 0x7C:												/* Right arrow (Mac II). */
				case 0x42:												/*   (Mac plus). */
					if (pos < nchars)
						pos++;
					else SysBeep(1);

					break;
		
				case 0x7B:												/* Left arrow (Mac II). */
				case 0x46:												/*   (Mac plus). */
					if (pos > 0)
						pos--;
					else SysBeep(1);
		
					break;

				case 0x33:												/* DELETE key. */
					if (pos > 0)
					{
						if (pos < nchars)
							memmove(str + (pos - 1), str + pos, nchars - pos);
						*(str + --nchars) = '\0';
						pos--;
						redisplay_flag = TRUE;
						cleareos_flag = TRUE;
					}
					else SysBeep(1);

					break;

				case 0x47:												/* CLEAR key. */
					if (single_char_flag) SysBeep(1);					/* Don't let getchar user think CLEAR means anything. */
					nchars = pos = 0;
					*str = '\0';
					redisplay_flag = TRUE;
					cleareos_flag = TRUE;
					break;

				case 0x24:												/* RETURN key. */
				case 0x4C:												/* ENTER key. */
					if (single_char_flag)								/* Single character mode will allow \n as a return value. */
					{
						*str = '\n';
						*(str + 1) = '\0';
						nchars = 1;
					}
					else add_newline_flag = TRUE;						/* Don't return newline in string, so set flag to echo it. */
					string_ended_flag = TRUE;
					redisplay_flag = TRUE;								/* Redisplay it to echo newline. */
					rtn_val = pos = nchars;
					break;
		
				default:
					if (c_ascii == 4)									/* Control-D (return EOF). */
					{
						rtn_val = EOF;
						string_ended_flag = TRUE;
					}
					else if (!single_char_flag && c_ascii == 5)			/* Control-E (move to end of input text). */
					{
						pos = nchars;
					}
					else if (!single_char_flag && c_ascii == 21)		/* Control-U (same as CLEAR) */
					{
						nchars = pos = 0;
						*str = '\0';
						redisplay_flag = TRUE;
						cleareos_flag = TRUE;
					}
					else												/* Get here if its just a regular old ascii value. */
					{
						if (max_chars >= 0 && nchars >= max_chars - 1)
							SysBeep(1);
						else
						{
							if (pos < nchars)
								memmove(str + (pos + 1), str + pos, nchars - pos);
							*(str + pos++) = c_ascii;
							*(str + ++nchars) = '\0';
							redisplay_flag = TRUE;
						}
					}
					break;
			}

			if (redisplay_flag)											/* If something has happened necesitating a redraw, do it. */
			{
				disp_pos = MAX(0, pos - 1);								/* Just redraw from current position on. */
				disp_row = begin_at/_maccur_cols;						/* But first measure to find start position. */
				disp_col = begin_at % _maccur_cols;
				save_char = *(str + disp_pos);
				*(str + disp_pos) = '\0';
				maccur_noncurse_mode_show_str(str,
									&disp_row, &disp_col,
									NULL, FALSE, FALSE, FALSE);
				*(str + disp_pos) = save_char;

				maccur_noncurse_mode_show_str(str + disp_pos,
								&disp_row, &disp_col,
								&scroll_cnt, TRUE,
								add_newline_flag, cleareos_flag);
				begin_at -= scroll_cnt * _maccur_cols;
			}

			if (single_char_flag && nchars > 0 && !string_ended_flag)
			{
				string_ended_flag = TRUE;
				rtn_val = nchars;
			}
		}
	}

	direct_curs_pos_row = disp_row;										/* Leave cursor where last update put it. */
	direct_curs_pos_col = disp_col;

	return rtn_val;
}

/********
**
**	maccur_printf:  Equivalent to printf.
**
**	Returns:
**		Results of vsprintf.
**
**
********/

int	maccur_printf(
char *fmt,
...
) {
    va_list args;
    int rtn;

	if (screen_char_arry == NULL)
		maccur_init();

    va_start(args, fmt);
    rtn = vsprintf(_curses_prntw, fmt, args);
    va_end(args);

	if (rtn < 0) return rtn;

	maccur_noncurse_mode_show_str(_curses_prntw, &direct_curs_pos_row,
								&direct_curs_pos_col, NULL, TRUE,
								FALSE, FALSE);

	if (_maccur_io_sleep_dvsr > 0 && ++cpu_share_rot_cnt >= _maccur_io_sleep_dvsr)
	{
		maccur_cpu_share();
		cpu_share_rot_cnt = 0;
	}

	return rtn;
}

/********
**
**	maccur_puts:  Equivalent to puts.
**
**	Returns:
**		0 (success).
**
**
********/

int maccur_puts(
char *str
) {
	if (screen_char_arry == NULL)
		maccur_init();

	maccur_noncurse_mode_show_str(str, &direct_curs_pos_row,
								&direct_curs_pos_col, NULL, TRUE,
								TRUE, FALSE);

	if (_maccur_io_sleep_dvsr > 0 && ++cpu_share_rot_cnt >= _maccur_io_sleep_dvsr)
	{
		maccur_cpu_share();
		cpu_share_rot_cnt = 0;
	}

	return 0;
}

/********
**
**	maccur_putchar:  Equivalent to putchar.
**
**	Returns:
**		Character printed.
**
**
********/

int maccur_putchar(
char c
) {
	char str[2];

	if (screen_char_arry == NULL)
		maccur_init();

	str[0] = c;
	str[1] = '\0';

	maccur_noncurse_mode_show_str(str, &direct_curs_pos_row,
								&direct_curs_pos_col, NULL, TRUE,
								FALSE, FALSE);

	if (_maccur_io_sleep_dvsr > 0 && ++cpu_share_rot_cnt >= _maccur_io_sleep_dvsr)
	{
		maccur_cpu_share();
		cpu_share_rot_cnt = 0;
	}

	return c;
}

/********
**
**	maccur_scanf:  Equivalent to scanf.  Uses THINK C's non-portable _vsscanf.
**
**	Returns:
**		Results of _vsscanf.
**
**
********/

int	maccur_scanf(
char *fmt,
...
) {
    va_list args;
    int rtn;

	if (screen_char_arry == NULL)
		maccur_init();

	maccur_noncurse_mode_getstr(_curses_prntw, _curses_prntw_size, FALSE);

	va_start(args, fmt);
	rtn = _vsscanf(_curses_prntw, fmt, args);
	va_end(args);

	return rtn;
}

/********
**
**	maccur_gets:  Equivalent to gets.
**
**	Returns:
**		Pointer to string argument (success).
**
**
********/

char *maccur_gets(
char *str
) {

	if (screen_char_arry == NULL)
		maccur_init();

	if (maccur_noncurse_mode_getstr(str, -1, FALSE) == EOF)
		return NULL;

	return str;
}

/********
**
**	maccur_getchar:  Equivalent to getchar.
**
**	Returns:
**		Character read or EOF.
**
**
********/

int maccur_getchar(void)
{
	char str[2];
	int rtn;

	if (screen_char_arry == NULL)
		maccur_init();

	if (maccur_noncurse_mode_getstr(str, 2, TRUE) == EOF)
		rtn = EOF;
	else
		rtn = str[0];

	return rtn;
}
