tony@wldrdg.UUCP (06/10/87)
#! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # curses.doc # This archive created: Tue Jun 9 23:02:00 1987 export PATH; PATH=/bin:$PATH if test -f 'curses.doc' then echo shar: will not over-write existing file "'curses.doc'" else cat << \HE_HATES_THESE_CANS > 'curses.doc' The CURSES screen updating and cursor movement package. A collection of C-functions for Atari ST-series computers Version 1.0 R. van 't Veen This document describes a set of C-functions which allow the user easier design of full-screen applications. This package is intended for programs that perform graphics-like operations on terminal screens. It is largely compatible with the UN*X curses(3x) package. This document provides pointers to its use, a little detail about its inner workings and it stresses differences between the UN*X-implementation and the Atari-ST series implementation. Introduction. This package was born out of my desire to make more programs available for UN*X also available on my Atari. A side-effect of this is that it provides easier screen updating than by means of sending escape codes. The package is also quite fast, timing of a sample application yielded a rate of about 90 kilobaud per second. Terminology. Throughout this document the following words will be used frequently : WINDOW An internal representation of what a section of the terminal screen may eventually look like. A window is a rectangular array, which may be as big as the terminal screen or even as small as a single character. TERMINAL Or terminal screen, the packages idea of what the Atari's VT52 emulation looks like. If everything is allright this is what you see. The terminal screen is a special screen. SCREEN A screen is an element of the set of windows that are as large as the terminal screen. One screen, called stdscr, is already provided for. Using the package. To use the package, include the file "curses.h" in your source. This gives you the types and definitions necessary to be able to use it. After compilation one should link with the file containing the compiled curses-source, as well as with the object-file or library giving you access to the system calls, gemdos, bios and xbios. You also should link in a file giving you access to the malloc() and free() calls. ( Note : this is not the GEMDOS Malloc/Mfree pair, but a malloc() and free(), which should be contained in any decent standard C-function library. GEMDOS Malloc/Mfree are very buggy indeed.) There is a difference with UN*X here : the UN*X-curses includes stdio.h and sgtty.h, whereas Atari-curses includes portab.h and stdio.h. This should give you the definitions for BYTE, WORD and LONG. Some compilers supply default startup files that don't supply enough heap space to run curses in, you should adapt these startup files to your liking. Installing the package. Compile the file "curses.c" with your favorite C-compiler, to create an object-file or library, whatever you prefer. Curses.c includes "portab.h" for BYTE, WORD, LONG, and NULL definitions, osbind.h for system-call macros and curses.h for its own definitions. Remember curses.h also includes stdio.h. The code should be portable between various brands of compilers. Forget about warnings like : warning : pointer substraction yields a long result. Not all compilers adhere to Kernighan and Ritchie standards in this respect. The meaning of each of the constants is given in the table below. In another table the used system call macro's are given. +---------------+----------------------------------------------------+ | constant name | meaning | +===============+====================================================+ | BYTE | a type with at least 8 bits in it, usually a char | | WORD | a type with at least 16 bits in it | | LONG | a type with at least 32 bits, used for pointers | | NULL | a value assigned to pointers pointing nowhere | +---------------+----------------------------------------------------+ +---------------+-----------------------------------------------------+ | macro name | meaning | +===============+=====================================================+ | Pterm | gemdos $4c, terminate process and return to caller | | Cconis | gemdos $0b, console character available ? | | Crawcin | gemdos $07, get a raw character from console | | Cnecin | gemdos $08, get a not-so raw character from console | | Bconout | bios $03, output a character to a device | | Bconstat | bios $01, get input device status | | Bconin | bios $02, input a character from a device | | Supexec | xbios $26, execute function in supervisor mode | +---------------+-----------------------------------------------------+ Screen updating. The package defines a data type called WINDOW, which represents what a section of the terminal may eventually look like. It can be used as a scratch-pad onto which you can make changes. Only after calling the function refresh() or wrefresh() an attempt will be made by the package to make things on the screen look like the things you specified in the window. Information contained in the window include not only its contents, but also its size, its starting point and various other information. Two standard window are defined after initialization of the package, namely curscr, which constitutes the package's idea of what the screen looks like, and stdscr, which is a default window to make changes on. It is advised not to address curscr directly, but first to make changes on another window and then to call refresh() or wrefresh() to make curscr look like the other window. Making a change on curscr results in immediate execution of the change, this greatly reduces any chances of optimization and therefore results in a sizeable loss of speed ( unless you know what you're doing, of course ). Naming conventions. All output and input functions can be used with any window. These functions have two variants, one to act on the default screen stdscr, and another to act on any window. An example is the function addch(), which adds a character to stdscr. The variant, which adds a character to a window w is called waddch() and takes w as an argument. This convention is used throughout the package. The routines which do not adhere to this convention always take a window as an argument. Apart from the w-convention some functions also take a mv- prefix. Ordinarily one moves the current coordinates of a window by calling the move or wmove function. Preceding an I/O function by mv first moves the current coordinates and then performs the I/O function. In this way one can write the sequence : move(y,x) ; addch('Q') ; as : mvaddch(y,x,'Q') ; and the sequence : wmove(win,y,x) ; waddch(win,'W') ; may be replaced by : mvwaddch(win,y,x,'W') ; You may have noticed the following other conventions : 1. If a window must be specified as a parameter, it is always the first parameter. 2. Coordinates are specified as (y,x) pairs, where y is in the up/down direction, and x in the letf/right direction, the coordinate (0,0) is in the upper-left corner of the window. Programming with curses. The following sections deal with programming with the library. First of all curses should be initialized first. This is done by a call to initscr(). On return curses will have initialized the screen to a state known by curses, and have initialized curscr and stdscr, whereas prior to the initscr() call, any reference to curscr or stdscr will result in bombs. After calling initscr() you should perform any terminal status changing routines like crmode() or nonl(), although these may be performed anytime when curses is active. After that you can set up the screen status using functions like leaveok() and scrollok() or allocate new windows with newwin() or subwin(), delete them with delwin(). Calling initscr() again will either result in a redraw of the screen or another initialization step when the variables LINES or COLS have changed. This enables you to set the screen size by hand, because normally the variables LINES and COLS contain the true size of the terminal. Calling initscr ni such a way again, results in the deletion and regeneration of curscr and stdscr. The basic functions that allow you to change the contents of a window are addch() and move(). Addch() adds a character to a window and move() moves the current coordinates. Other output functions exist to let you do some more elaborate things, like addstr() to add an entire string, insertln() to insert a line etc.. When you have composed the contents of the window to your liking you can call refresh() to make the portion of the terminal screen overlapped by the window look like the window. When updating one must keep in mind that refresh assumes that parts of the window not changed since the last update of the window are also not changed on the terminal. Use the touchwin() function to let curses think the entire window has changed when using overlapping windows. Calling wrefresh() with curscr as an argument results in a complete redraw of the terminal screen. Because curses has to know what the contents of the physical screen is at all times, you should not do any other screen output, than by curses alone. Or if you really have to, do a redraw afterwards. If you don't, you're bound to end up with some very confused screens. Input is done by the getch() function, which inputs a character from the keyboard and echoes it through addch() if echo is set. When using curses you may notice that, while performing output and other functions, the cursor is switched off, when curses waits for input however the cursor is switched on. Input can be done in either one of three modes : cooked, cbreak and raw. These input modes are primarily introduced to maintain UN*X-compatability, but they do not work the same, so when porting UN*X-programs special care should be taken with any input routines. Moreover, you should not perform any input through the stdio library, as they will surely echo something on the screen. UN*X-curses can handle this because it explicitly sets the echo of the terminal off, whereas Atari-curses only simulates this behaviour. So : any input only through curses. Cooked mode is supposed to be a line-oriented input interface. Using UN*X and curses, cooked input can only be performed, when echo is not set, if it is set cooked mode input turns to cbreak mode for the duration of the input call. Using Atari and curses, the same restrictions apply, with the exception that the following characters have special meanings : +---------------+-------+------------------------------------------+ | character | ASCII | action | +===============+=======+==========================================+ | control-C | $03 | do a Pterm(-1), i.e. exit immediately | | control-H/BS | $08 | do a backspace, not beyond the beginning | | | | of the input-field. | | control-U | $13 | kill the entire input field | +---------------+-------+------------------------------------------+ Cbreak mode is supposed to be a single character oriented interface, with which one can receive any character and have no special meaning attached to it. The only exceptions are job-control characters like control-C ( interrupt ), delete ( kill the process ), control-Z ( suspend the process ) etc. On Atari this is translated in the following : all characters are received and only control-C has special meaning, which is : terminate the process. Raw mode is also a single character interface and has no special processing whatsoever. The same holds for Atari. Apart from the above-mentioned functions one should also note that getch() does not return a character, but a 32-bit value. For UN*X getch() returns an int, with ( apart from errors ) only the lower 7 or 8 bits set. On Atari the same holds true for cooked mode, in cbreak mode the ASCII value is returned in bits 0 to 7 and the scancode of the key in bits 16 to 23, raw mode adds the keyboard shift state in bits 24 to 30. When entering a string through getstr() or wgetstr() only the character portion of the long values are retained. Cleaning up is performed by the endwin() function. It releases the storage acquired for curscr and stdscr, switches the terminal to its normal state and puts the cursor in a reasonably comfortable place, i.e. at the bottom of the screen. Note that any windows acquired through newwin() and subwin() still exist. They should be released from storage prior to calling endwin(). Constants, types and variables defined by curses.h. The include file curses.h makes several constants, types and variables available to the programmer. In this section something will be told about their function and use. The most important thing defined in curses.h is the data type WINDOW. This datatype is used by curses to maintain data for all windows. It has the following definition : #define WINDOW struct _win_st struct _win_st { WORD _cury, _curx ; WORD _maxy, _maxx ; WORD _begy, _begx ; WORD _flags ; bool _clear ; bool _leave ; bool _scroll ; WORD **_y ; WORD *_firstch ; WORD *_lastch ; } ; The internals of this structure are not normally to be accessed by the user. This structure differs in three aspects from the UN*X-curses WINDOW structure. First of all the _y field is char in UN*X-curses, WORD in Atari-curses. This had been done to ensure that all characters in the Atari character set, including all diacritical marks, can be displayed, whereas UN*X-curses will only display characters $20-$7f, or standard ASCII characters. As a matter of act the Atari will display codes $00-$ff, with the exception of tabs, linefeeds and carriage returns. These have a special meaning. Each character can be in standout mode, if so desired. The other changes are in the _firstch, _lastch fields. This reflects the updating algorithms used. The fields have the following meaning : _cury and _curx are the current coordinates of the window, relative to the window's home, _begy and _begx. This beginning is relative to the terminals home position (0,0). _maxy and _maxx are the maximum values allowed for _cury and _curx. _flags is a bit-pattern of several flags describing the structure of the window. _flags can have several values OR'ed into it. _SUBWIN means that the window is a subwindow, which indicates to delwin() that the character space cannot be freed. The _FULLWIN flag indicates that the window is a screen. _STANDOUT means that any added character is in standout-mode. UN*X-curses also defines the _ENDLINE and _SCROLLWIN flags, they are defined in curses.h for compatability purposes, but Atari-curses does nothing with these values. The _clear field in the WINDOW structure is set when the next refresh on the window should generate a clear screen sequence. Setting the clear flag for curscr will always generate a clear screen sequence on any refresh. The _leave field is TRUE if the windows current coordinates are to be set after the last character changed on the terminal after a refresh. The _scroll field is TRUE, if scrolling is allowed. The _y field is a pointer to an array of pointers to arrays of character values. These values are the actual contents of a window. The lower 8 bits of a character value contain the real character value, the upper 8 bits are used by curses internally. The _firstch and _lastch fields are used by the updating algorithms of curses. Unless you know what you are doing you are not supposed to tamper with any of these values, curses supplies a full set of functions to set any values which you want to be set. After initializing curses you can be sure the following variables have been set and initialized to their proper values. Some variables may even be set prior to initializing curses. This is marked by a & in the table. +----------------+------------+-----------------------------------------------+ | variable name | type | description | +================+============+===============================================+ | curscr | WINDOW * | curses idea of what the terminal screen looks | | | | like. | | stdscr | WINDOW * | Default screen for updates. | | Def_term | char * | Default terminal type. UN*X-compatability. | | My_term & | bool | UN*X-compatability, does nothing. | | ttytype | char * | Full name of the terminal | | LINES & | int | Number of ines curses thinks the terminal has | | | | normally 25, but may be changed, see text | | COLS & | int | Number of columns curses thinks the terminal | | | | has, may be changed, see text. | | ERR | int | If this is set, curses had an internal error | | OK | int | If this is set, everything is gone right | +----------------+------------+-----------------------------------------------+ Also the following constants and macros are defined in curses.h : +----------------+------------------------------------------------------------+ | name | description | +================+============================================================+ | reg | storage class register, i.e. register int i => reg int i | | bool | boolean type, values can be TRUE or FALSE | | TRUE | boolean 'true' flag. | | FALSE | boolean 'false' flag. | +----------------+------------------------------------------------------------+ Another difference between Atari-curses and UN*X-curses is that UN*X-curses reads in a lot of variables described in /etc/termcap, no such thing exists for Atari, and is not needed anyway. However these variables are available to the programmer in UN*X-curses, but not in Atari-curses. These include things like strings, which when output to the terminal cause some action to be taken, like the string VB, for a visible bell, or DM, enter delete mode. Care should be taken with UN*X-programs that perform such actions. Description of individual functions. The following is a description of all the individual routines, a lot of them are defined as macros, notably those that take stdscr as the window and functions that can be preceded by "mv". If such is the case, it is stated by a '!' a little further on the line. Output functions. addch(c) ! char c ; mvaddch(y,x,c) ! int y,x ; char c ; mvwaddch(win,y,x,c) ! WINDOW *win ; int y,x ; char c ; waddch(win,c) WINDOW *win ; char c ; Add the character c at the current coordinates of the window w. The following characters cause special processing : a newline ('\n') causes the line to be cleared to the end, and the current coordinates to be changed to the beginning of the next line if newline mapping is on, or to the next line at the same column if newline mapping isn't on. A return ('\r') will move the cursor to the beginning of the line on the window. Tabs ('\t') will be expanded into spaces into the normal tabstop positions of every eight characters. addstr(s) ! char *s ; mvaddstr(y,x,s) ! int y,x ; char *s ; mvwaddstr(win,y,x,s) ! WINDOW *win ; int y,x ; char *s ; waddstr(win,s) WINDOW *win ; char *s ; Add the string s to the window at the current coordinates. Addstr() calls addch repeatedly to add the characters. box(win,vert,hor) WINDOW *win ; char vert, hor ; mvbox(win,y,x,vert,hor) ! WINDOW *win ; int y, x ; char vert, hor ; Draws a box around the window using vert as the vertical drawing character and hort as the horizontal drawing character. clear() ! wclear(win) WINDOW *win ; Reset the window to blanks. If win is a screen this will cause a clear screen sequence to be sent on the next refresh. The current coordinates are also moved to the home-position (0, 0). clearok(scr, boolf) ! WINDOW *scr ; bool boolf Sets the clear-flag for the screen scr to boolf. If boolf is TRUE this will generate a clear screen sequence on the next refresh. If it is FALSE refresh may be stopped from generating a clear screen sequence. clrtobot() ! wclrtobot(win) WINDOW *win ; Clear the window from the current coordinates to the bottom. The current coordinates aren't changed. clrtoeol() ! wclrtoeol(win) WINDOW *win ; Clear the window from the current coordinates to the end of the line. This doesn't change the current coordinates. delch() ! mvdelch(y,x) ! int y, x ; mvwdelch(win,y,x) ! WINDOW *win ; int y, x ; wdelch(win) WINDOW *win ; Delete the character at the current coordinates. Characters on the same line after the deleted character shift to the left and the last character becomes blank. deleteln() ! mvdeleteln(y,x) ! int y, x ; mvwdeleteln(win,y,x) ! WINDOW *win ; int y,x ; wdeleteln(win) WINDOW *win ; Delete the current line, move every line under it one line up and make the bottom line blank. This doesn't change the current coordinates. erase() ! werase(win) WINDOW *win ; Sets the entire window to blanks without setting the clear flag. See clear(). The difference is that erase doesn't cause a clear screen sequence to be generated. insch(c) ! char c ; mvinsch(y,x,c) ! int y, x ; char c ; mvwinsch(win,y,x,c) ! WINDOW *win ; int y, x ; char c ; Insert the character c at the current coordinates. each character after the current character shifts one place to the right and the rightmost character falls off. insertln() ! mvinsertln(y,x) ! int y, x ; mvwinsertln(win,y,x) ! WINDOW *win ; int y, x ; Insert a line above the current line. Every line below will scroll down, and the bottom line will disappear. The inserted line will be blank. The current coordinates aren't chnaged by this function. move(y,x) ! int y, x ; wmove(win,y,x) WINDOW *win ; int y, x ; Change the current coordinates of window win to (y, x). Whenever one of the other output or input functions are preceded by "mv", this function is executed first. overlay(win1,win2) WINDOW *win1, *win2 ; The contents of win1 are placed on win2 at their respective starting coordinates. Those contents of win1, which would not fit in win2, are discarded. Blanks on win1 leave the contents of win2 untouched. overwrite(win1, win2) WINDOW *win1, *win2 ; The contents of win1 are placed on win2 at their respective starting coordinates. Those contents of win1, which would not fit in win2, are discarded. refresh() ! wrefresh(win) WINDOW *win ; Update the terminal screen with the contents of the window. Only those parts covered by the window will be updated. If the window is a screen and its clear flag has been set, then a clear screen sequence will be generated. mvstandend(y,x) ! int y, x ; mvwstandend(win,y,x) ! WINDOW *win ; int y, x ; standend() ! wstandend(win) WINDOW *win ; Stop putting characters onto win in standout mode. mvstandout(y,x) ! int y, x ; mvwstandout(win,y,x) ! WINDOW *win ; int y, x ; standout() ! wstandout(win) WINDOW *win ; Start putting characters onto win in standout mode, i.e. display inverted characters. Input functions. crmode() Set the terminal in cbreak mode. nocrmode() Reset the terminal from cbreak mode into cooked mode. echo() Let the terminal echo characters on input. noecho() Do not allow the terminal to echo characters on input. getch() ! mvgetch(y,x) ! int y,x ; mvwgetch(win,y,x) ! WINDOW *win ; int y, x ; wgetch(win) WINDOW *win ; Get a character from the terminal and echo it on the window if echo is set. See the section on programming with curses for more details about input modes. Remember getch() returns a 32-bit value, and not a char. getstr(s) ! char *s ; mvgetstr(y,x,s) ! int y,x ; char *s ; mvwgetstr(win,y,x,s) ! WINDOW *win ; int y, x ; char *s ; wgetstr(win,s) WINDOW *win ; char *s ; Get a string through the window win and put it in the string s, which is assumed to be large enough to hold the data. To get the input it calls getch() repeatedly. The end of the input is designated by any of the following characters, a carriage return, a linefeed, a EOF or control-D, and any character, which results in a '\0'. This character is stripped of the string and replaced by a null character to indicate the end of the string. raw() Set the terminal in raw input mode. noraw() Set the terminal from raw into cooked input mode. Miscellaneous functions. delwin(win) WINDOW *win ; Deletes the window. All resources ( i.e. storage ) held by the window is freed for future use. As subwindows share their character space with another window their character space is not freed. To delete a window with subwindows, you should first delete the subwindows and then the root window, as any access to a subwindow after deletion of its parent window is a sure route to disaster. endwin() Clean up after curses. You should call this prior to termination of your program. getyx(win,y,x) ! WINDOW *win ; int y,x ; This macro puts the window current coordinates into the variables y and x. Note that you do not supply the address of the variables, as it is a macro. inch() ! mvinch(y,x) ! int y,x ; mvwinch(w,y,x) ! WINDOW *w ; int y, x ; winch(win) ! WINDOW *win ; This macro returns the value of the character at the current coordinates of the window. This does not change the contents of the window. The functions mvinch() and mvwinch() are not supported by standard UN*X-curses, although they probably are by most UN*X-implementations. initscr() Initialize curses. You should call this before any of the other calls. leaveok(win,flag) WINDOW *win ; bool flag ; Sets the _leave field of the window win to the value flag. If flag is TRUE then after an update on the terminal, the window's current coordinates will be set to the coordinates immediately after the last updated character. If flag is FALSE, then the cursor will be moved to the windows current coordinates. This flag is FALSE by default. longname(termbuf,name) char *termbuf, *name ; Introduced for UN*X-compatability, it returns the contents of ttytype in name, regardless of the contents of termbuf. This is not really consistent with UN*X-curses. mvwin(win,y,x) WINDOW *win ; int y, x ; Move the home position of the window win, from its current coordinates to the coordinates specified in y and x. You cannot move a window off the terminal screen, not even by a character. WINDOW *newwin(l,c,by,bx) int l, c, by, bx ; Create a new window with l lines and c columns starting at position (by,bx). If either l or c is zero, then the dimension will be set to ( LINES - by ), ( LINES - bx ). To get a screen, one would say newwin(0,0,0,0). nl() Set curses to newline mapping. This has consequences for adding the character '\n'. See addch() for these consequences. nonl() Set curses to do no newline mapping. This is the default. In UN*X-curses you get the fastest updates when you have no newline mapping, Atari-curses doesn't care about it. scrollok(win,flag) Set the _scroll field of the window win to flag. If flag is FALSE, the terminal is not allowed to scroll, which is also the default. touchwin(win) WINDOW *win ; Force curses to believe that the entire window has changed on the next refresh. This is useful for overlapping windows. WINDOW *subwin(win,l,c,by,bx) Create a new window with a shared character space,as opposed to newwin(), which allocates a new character space. See newwin() for more details about the l, c, by and bx parameters. The new window will share the character space with the window win. The subwindow must be smaller or the same size as the parent window. Changes on either window are reflected on the other window. Moving subwindows with mvwin() will create some pretty weird results, because the mapping of the subwindow to the parent window will tend to get displaced. For instance, if I have a one character subwindow of a screen at coordinates (2,3) and if I move the subwindow to say (4,5), then if I change the character on the subwindow at (4,5), the parent window will have changed at (2,3). I don't know whether this is UN*X-compatible, so take care here. Detail functions. These routines are at the core of UN*X-curses, and can be used on a standalone basis. Atari-curses has a very limited implemntation of this, so if a program uses these, you'd better rewrite the whole thing. Most likely, the results of using these won't be the same on either of the two implementations. mvcur(lasty,lastx,newy,newx) int lasty, lastx, newy, newx ; Move the terminals cursor from (lasty,lastx) to (newy,newx). Don't use it when you use any of the screen routines. scroll(win) WINDOW *win ; Scroll the window upward one line. You don't normally want to do this. Functions not supported by Atari-curses. Several functions are not supported by Atari-curses, but are by UN*X-curses. The names of these functions are given, as well as a reason for not implementing them. mvprintw(), mvwprintw(), printw() and wprintw() Performs a printf() on a window. I didn't want to implement this for two reasons : 1. This introduces compiler dependant code. 2. A large overhead in coding is required. ( I'm very lazy. ) This omission can easily be gotten around with by inline replacement with : sprintf(a_string, format, ... ) ; addstr(a_string) ; mvscanw(), mvwscanw(), scanw() and wscanw() Performs a scanf() on a window. The same reasons apply here as the reasons for printw(). Replace it inline with : getstr(a_string) ; sscanf(a_string, format, ... ) ; uncntrl() Returns a string which is a representation of a character, nice debugging feature for UN*X-curses, but pretty meaningless for Atari-curses as Atari-curses can show any character. gettmode() A function called by UN*X-initscr(), it gets tty stats, which is UN*X-dependant anyway. savetty() and resetty() Save and reset tty characteristics, used by UN*X-curses to initialize and clean up respectively. Not necessary on Atari. setterm(name) Set terminal characteristics to those of the terminal with name name. Pretty useless on Atari. tstp() Function which is used in UN*X-curses to save the current state when a process is put to sleep and to restore it when it is restarted. This is the normal catch for the signal SIGTSTP, which Atari users won't be able to receive anyway. Caveats and bugs. There are differences between Atari-curses and UN*X-curses, especially in the area of input and the more detailed workings of curses. I will probably not change the workings of any input routines, because if I did application programs wouldn't have any control over say function keys, cursor keys, etc. Such keys really make a difference in user friendly programs, so I'd better let a programmer have access to them. The more detailed workings of curses are best left out too, as they concern themselves with multiple types of terminals, where the Atari-curses is only fitted to a single terminaltype, i.e. the Atari's VT52 emulation mode. Another problem is possibly the low-resolution mode of the Atari, I don't have access to a color monitor, so I wasn't able to test it in either medium or low-res. Medium probably works fine, but low-res ?. So, if you don't do anything too fancy, you should be allright. One more thing : when you write portable programs, keep in mind that the Atari-curses is a lot faster than any implementation, which sends data over a terminal line. Comparing speeds will put any program on the Atari seemingly on a 50+ kilobaud line. Also beware of funny characters, i.e. portable programs require character ranges between $20 and $7F, Atari-curses can and will handle a lot more. This warning should especially be heeded by say Germans and French, diacritical marks may screw things up real bad. HE_HATES_THESE_CANS fi # end of overwriting check # End of shell archive exit 0