Copyright (c) Hyperion Entertainment and contributors.

Console Device

From AmigaOS Documentation Wiki
Jump to navigation Jump to search

Console Device

The console device provides the text-oriented interface for Intuition windows. It acts like an enhanced ASCII terminal obeying many of the standard ANSI sequences as well as special sequences unique to the Amiga. The console device also provides a copy-and-paste facility and an internal character map to redraw a window when it is resized.

Console Device Commands and Functions

Command Command Operation
CD_ASKDEFAULTKEYMAP Get the current default keymap.
CD_ASKKEYMAP Get the current key map structure for this console.
CD_ASKKEYMAP_POINTERS Get pointers to the current key map structures for this console. (V51.19)
CD_GETATTRS Get attributes for this console. (V53)
CD_SETATTRS Set attributes for this console. (V53)
CD_SETDEFAULTKEYMAP Set the current default keymap.
CD_SETKEYMAP Set the current key map structure for this console.
CD_SETKEYMAP_POINTERS Set the current key map structures for the console. (V51.19)
CMD_CLEAR Remove any reports waiting to satisfy read requests from the console input buffer.
CMD_READ Read the next input, generally from the keyboard. The form of this input is as an ANSI byte stream.
CMD_WRITE Write a text record to the display interpreting any ANSI control characters in the record.

Console Device Functions:

CDInputHandler() Handle an input event for the console device.
RawKeyConvert() Decode raw input classes and convert input events of type IECLASS_RAWKEY to ANSI bytes based on the keymap in use.

Device Interface

The console device operates like the other Amiga devices. To use it, you must first open the console device, then send I/O requests to it, and then close it when finished. See Exec Device I/O for general information on device usage.

The I/O request used by the console device is called IOStdReq.

struct IOStdReq
{
    struct  Message io_Message;
    struct  Device  *io_Device;     /* device node pointer  */
    struct  Unit    *io_Unit;       /* unit (driver private)*/
    UWORD   io_Command;             /* device command */
    UBYTE   io_Flags;
    BYTE    io_Error;               /* error or warning num */
    ULONG   io_Actual;              /* actual number of bytes transferred */
    ULONG   io_Length;              /* requested number bytes transferred*/
    APTR    io_Data;                /* points to data area */
    ULONG   io_Offset;              /* offset for block structured devices */
};

See the include file exec/io.h for the complete structure definition.

Console Device Units

The console device provides four units, three that require a console window and one that does not. The unit type is specified when you open the device. See the Opening the Console Device section below for more details.

The CONU_STANDARD unit (0) is generally used with a SMART_REFRESH window. This unit has the least amount of overhead (e.g., memory usage and rendering time), and is highly compatible with all versions of the operating system.

There are two variations of character mapped console units. Both must be used with SIMPLE_REFRESH windows and both have the ability to automatically redraw a console window when resized or revealed.

A character mapped console can be opened which allows the user to drag-select text with the mouse and Copy the highlighted area. The copied text can then be Pasted into other console windows or other windows which support reading data from the clipboard device.

Character mapped console units have more overhead than standard consoles (e.g., rendering times and memory usage).

The CONU_LIBRARY unit (-1) does not require a console window. It is designed to be primarily used with the console device functions and also with the console device commands that do not require a console window.

The Amiga uses the ECMA-94 Latin 1 International 8-bit character set.

Opening the Console Device

Four primary steps are required to open the console device:

  1. Create a message port using AllocSysObject(ASOT_PORT). Reply messages from the device must be directed to a message port.
  2. Create an I/O request structure of type IOStdReq. The IOStdReq structure is created by the AllocSysObject(ASOT_IOREQUEST) function. AllocSysObject() will initialize your I/O request to point to your reply port.
  3. Open an Intuition window and set a pointer to it in the io_Data field of the IOStdReq and the size of the window in the io_Length field. This is the window to which the console will be attached. The window must be SIMPLE_REFRESH for use with the CONU_CHARMAP and CONU_SNIPMAP units.
  4. Open the console device. Call OpenDevice() passing it the I/O request and the type of console unit set in the unit and flags fields. Console unit types and flag values are listed below.

Console device units:

CONU_LIBRARY
Return the device library vector pointer used for calling console device functions. No console is opened.
CONU_STANDARD
Open a standard console.
CONU_CHARMAP
Open a console with a character map.
CONU_SNIPMAP
Open a console with a character map and copy-and-paste support.
CONU_HISTORY (V53)
Similar to CONU_SNIPMAP with content history and tabbed windows.

See the include file devices/conunit.h for the unit definitions and the SDK for an explanation of each unit.

No Changes Required
CONU_STANDARD has a numeric value of zero to insure compatibility with pre-V36 applications. CONU_LIBRARY has a numeric value of negative one and is also compatible with pre-V36 applications.

Console device flags:

CONFLAG_DEFAULT
The console device will redraw the window when it is resized.
CONFLAG_NODRAW_ON_NEWSIZE
The console device will not redraw the window when it is resized
CONFLAG_CREATE_WINDOW and CONFLAG_TABBED_WINDOW (V53)
These bits define the combinations of window source, tabs and sharing. These are only applicable when using CONUNIT_HISTORY or greater.
CONFLAG_SYNC_WRITES (V53)
Forces a task issuing a Write request to wait until the request is complete before returning to the caller.

The character map units are the only units which use the flags parameter to set how the character map is used. CONU_STANDARD units ignore the flags parameter. See the console.doc autodoc for the most up to date information.

See the include file devices/conunit.h for the flag definitions and the SDK for an explanation of the flags.

struct NewWindow nw =
{
    10, 10,                    /* starting position (left,top) */
    620,180,                   /* width, height */
    -1,-1,                     /* detailpen, blockpen */
    CLOSEWINDOW,               /* flags for idcmp */
    WINDOWDEPTH|WINDOWSIZING|
    WINDOWDRAG|WINDOWCLOSE|
    SIMPLE_REFRESH|ACTIVATE,    /* window flags */
    NULL,                      /* no user gadgets */
    NULL,                      /* no user checkmark */
    "Console Test",            /* title */
    NULL,                      /* pointer to window screen */
    NULL,                      /* pointer to super bitmap */
    100,45,                    /* min width, height */
    640,200,                   /* max width, height */
    WBENCHSCREEN               /* open on workbench screen */
};
 
    /* Create reply port console */
struct MsgPort *ConsoleMP = IExec->AllocSysObjectTags(ASOT_PORT,
     ASOPORT_Name, "RKM.Console",
     TAG_END);
if (ConsoleMP == NULL)
    cleanexit("Can't create write port\n", RETURN_FAIL);
 
    /* Create message block for device I/O */
struct IOStdReq *ConsIO = IExec->AllocSysObjectTags(ASOT_IOREQUEST,
    ASOIOR_Size, sizeof(struct IOStdReq),
    ASOIOR_ReplyPort, ConsoleMP,
    TAG_END);
if (ConsIO == NULL)
    cleanexit("Can't create IORequest\n", RETURN_FAIL);
 
    /* Open a window --- we assume intuition.library is already open */
struct Window *win = IIntuition->OpenWindow(&nw);
if (win == NULL)
    cleanexit("Can't open window\n",RETURN_FAIL);
 
    /* Set window pointer and size in I/O request */
ConsIO->io_Data = (APTR) win;
ConsIO->io_Length = sizeof(struct Window);
 
    /* Open the console device */
if (error = IExec->OpenDevice("console.device", CONU_CHARMAP, ConsIO, CONFLAG_DEFAULT))
    cleanexit("Can't open console.device\n",RETURN_FAIL);

Closing the Console Device

Each OpenDevice() must eventually be matched by a call to CloseDevice().

All I/O requests must be complete before CloseDevice(). If any requests are still pending, abort them with AbortIO().

if (!(IExec->CheckIO(ConsIO)))
    IExec->AbortIO(ConsIO);      /* Ask device to abort any pending requests */
 
IExec->WaitIO(ConsIO);           /* Wait for abort, then clean up */
IExec->CloseDevice(ConsIO);      /* Close console device */

About Console I/O

The console device may be thought of as a kind of terminal. You send character streams to the console device; you also receive them from the console device. These streams may be characters, control sequences or a combination of the two.

Console I/O is closely associated with the Amiga Intuition interface; a console must be tied to a window that is already opened. From the Window data structure, the console device determines how many characters it can display on a line and how many lines of text it can display in a window without clipping at any edge.

You can open the console device many times, if you wish. The result of each open call is a new console unit. AmigaDOS and Intuition see to it that only one window is currently active and its console, if any, is the only one (with a few exceptions) that receives notification of input events, such as keystrokes. Later in this article you will see that other Intuition events can be sensed by the console device as well.

Introducing...
For this entire article the characters <CSI> represent the control sequence introducer. For output you may use either the two-character sequence <Esc>[ (0x1B 0x5B) or the one-byte value 0x9B. For input you will receive 0x9B unless the sequence has been typed by the user.

Exec Functions and the Console Device

The various Exec functions such as DoIO(), SendIO(), AbortIO() and CheckIO() operate normally. The only caveats are that CMD_WRITE may cause your application to wait internally, even with SendIO(), and a task using CMD_READ to wait on a response from a console is at the user’s mercy. If the user never reselects that window, and the console response provides the only wake-up call, that task will sleep forever.

General Console Screen Output

Console character screen output (as compared to console command sequence transmission) outputs all standard printable characters (character values hex 20 through 7E and A0 through FF) normally.

Many control characters such as Backspace (0x08) and Return (0x0D) are translated into their exact ANSI equivalent actions. The Line feed character (0x0A) is a bit different in that it can be translated into a Return/Line feed action. The net effect is that the cursor moves to the first column of the next line whenever an <LF> is displayed. This option is set via the mode control sequences discussed under “Control Sequences for Window Output.”

Console Keyboard Input

If you read from the console device, the keyboard inputs are preprocessed for you and you will get ASCII characters, such as “B.” Most normal text-gathering programs will read from the console device in this manner. Some programs may also ask to receive raw events in their console stream. Keypresses are converted to ASCII characters or CSI sequences via the keymap associated with the unit.

Writing to the Console Device

You write to the console device by passing an I/O request to the device with a pointer to the write buffer set in io_Data, the number of bytes in the buffer set in io_Length and CMD_WRITE set in io_Command.

CONST_STRPTR outstring= "Make it so";
 
ConsIO->io_Data    = outstring;
ConsIO->io_Length  = IUtility->Strlen(outstring);
ConsIO->io_Command = CMD_WRITE;
IExec->DoIO(ConsIO);

You may also send NULL-terminated strings to the console device in the same manner except that io_Length must be set to -1.

ConsIO->io_Data    = "\033[3mOh boy.";
ConsIO->io_Length  = -1;
ConsIO->io_Command = CMD_WRITE;
IExec->DoIO(ConsIO);

The fragment above will output the string “Oh boy.” in italics. Keep in mind that setting the text rendition to italics will remain in effect until you specifically instruct the console device to change it to another text style.

Synchronized Writes

Synchronized writes were the only option for Console Device before version 53.38. Returning from the Write call means that the data has been transferred from the caller's buffer and the IORequest structure contents are no longer required. The data may not yet have been written to the display and any effects caused by the data (e.g. change of cursor position) may not yet have started or been completed.

If the Console was opened in legacy mode (no TABBED or HISTORY options), then the SYNCWRITES option is forced for greater compatibility with older applications. Otherwise, the Console is opened with synchronized writes disabled. This enables the application to continue to do work while the Console works in parallel.

You can still force the Write operation to block until completed by opening the Console with the CONFLAG_SYNC_WRITES set or (for a Shell) including the option SYNCWRITES in the command line.

If you do not want to wait for all Write requests, you can set the IOF_WAIT_WRITE bit in the io_Flags byte before calling CMD_WRITE. This flag bit has the same effect as opening the console with the CONFLAG_SYNC_WRITES set, but its scope is limited to the current IORequest.

Control Sequences for Window Output

The following table lists functions that the console device supports, along with the character stream that you must send to the console to produce the effect. For more information on the control sequences, consult the console.doc in the SDK. The table uses the second form of <CSI>, that is, the hex value 0x9B, to minimize the number of characters to be transmitted to produce a function.

A couple of notes about the table. If an item is enclosed in square brackets, it is optional and may be omitted. For example, for Insert [N] Characters the value for N is shown as optional. The console device responds to such optional items by treating the value of N as 1 if it is not specified. The value of N or M is always a decimal number, having one or more ASCII digits to express its value.

ANSI Console Control Sequences
Console Command Comment Sequence of Characters (in Hexadecimal Form)
Bell Flash the display—do an Intuition DisplayBeep() 07
Backspace Move left one column 08
Horizontal Tab Move right one tab stop 09
Line Feed Move down one text line as specified by the mode function 0A
Vertical Tab Move up one text line 0B
Form Feed Clear the console’s window 0C
Carriage Return Move to first column 0D
Shift In Undo Shift Out OE
Shift Out Set MSB of each character before displaying 0F
Esc Escape can be part of the control sequence introducer 1B
Index Move the active position down one line 84
Next Line Go to the beginning of the next line 85
Horizontal Tabulation Set a tab at the active cursor position 88
Reverse Index Move the active position up one line 8D
CSI Control sequence introducer 9B
Reset to Initial State 1B 63
Insert [N] Characters Insert one or more spaces, shifting the remainder of the line to the right 9B [N] 40
Cursor Up [N] Character Positions (default = 1) 9B [N] 41
Cursor Down [N] Character Positions (default = 1) 9B [N] 42
Cursor Forward [N] Character Positions (default = 1) 9B [N] 43
Cursor Backward [N] Character Positions (default = 1) 9B [N] 44
Cursor Position Where N is row, M is column, and semicolon (hex 3B) must be present as a separator, or if row is left out, so the console device can tell that the number after the semicolon actually represents the column number 9B [N] [3B M] 48
Cursor Horizontal Tabulation move cursor forward to Nth tab position 9B [N] 49
Cursor Next Line [N] (to column 1) 9B [N] 45
Cursor Preceding Line [N] (to column 1) 9B [N] 46
Erase in Display (only to end of display) 9B 4A
Erase in Line (only to end of line) 9B 4B
Insert Line Above the line containing the cursor 9B 4C
Delete Line Remove current line, move all lines up one position to fill gap, blank bottom line 9B 4D
Delete Character [N] That cursor is sitting on and to the right if [N] is specified 9B [N] 50
Scroll up [N] Lines Remove line(s) from top of window, move all other lines up, blanks [N] bottom lines 9B [N] 53
Scroll down [N] Lines Remove line(s) from bottom of window, move all other lines down, blanks [N] top lines 9B [N] 54
Cursor Tabulation Control where N=0 set tab, N=2 clear tab, N=5 clear all tabs 9B [N] 57
Cursor Backward Tabulation move cursor backward to Nth tab position 9B [N] 5A
Set Line Feed Mode Cause Line Feed to respond as Return-Line Feed 9B 32 30 68
Reset Newline Mode Cause Line Feed to respond only as Line Feed 9B 32 30 6C
Device Status Report Cause console device to insert a Cursor Position Report into your read stream; see “Reading from the Console Device” for more information 9B 36 6E
Select Graphic Rendition Select text style (K), character color (L), character cell color (M), background color (N). See note below. 9B K 3B 3L 3B 4M 3B >N 6D

For Select Graphic Rendition, any number of parameters, in any order, are valid. They are separated by semicolons. The parameters follow:

0 Plain text
1 Boldface
2 faint (secondary color)
3 Italic
4 Underscore
5 Set slow character blink (V53.5)
6 Set fast character blink (V53.5)
7 Reversed character/cell colors
8 Concealed mode
22 Normal color, not bold (V36)
23 Italic off (V36)
24 Underscore off (V36)
25 Slow blink off (V53.5)
26 Fast blink off (V53.5)
27 Reversed off (V36)
28 Concealed off (V36)
29 Strike-through off (V53.5)
30–37 System colors 0–7 for character color.
39 Reset to default character color
Transmitted as two ASCII characters.
40–47 System colors 0–7 for character cell color.
39 Reset to default character color
Transmitted as two ASCII characters
>0–7 System colors 0–7 for background color. (V36)

You must specify the “>” in order for this to be recognized and it must be the last parameter.

For example, to select bold face, with color 3 as the character color, and color 0 as the character cell color and the background color, send the hex sequence:

9B 31 3B 33 33 3B 34 30 3B 3E 30 6D

representing the ASCII sequence:

<CSI>1;33;40;>0m

where <CSI> is the control sequence introducer, here used as the single character value 0x9B.

Go Easy On The Eyes
In most cases, the character cell color and the background color should be the same.

Set Graphic Rendition Implementation Notes

The sequences in the next table are not ANSI standard sequences, they are private Amiga sequences. In these command descriptions, length, width, and offset are comprised of one or more ASCII digits, defining a decimal value.

Amiga Console Control Sequences
Console Command Comment Sequence of Characters (in Hexadecimal Form)
Enable Scroll (this is the default) 9B 3E 31 68
Disable Scroll 9B 3E 31 6C
Autowrap On (the default) 9B 3F 37 68
Autowrap Off 9B 3F 37 6C
Set Page Length In character raster lines, causes console to recalculate, using current font, how many text lines will fit on the page 9B <length> 74
Set Line Length In character positions, using current font, how many characters should be placed on each line 9B <width> 75
Set Left Offset In raster columns, how far from the left of the window should the text begin 9B <offset> 78
Set Top Offset In raster lines, how far from the top of the window’s RastPort should the topmost line of the character begin 9B <offset> 79
Set Raw Events Set the raw input events that will trigger an Input Event Report. see the “Selecting Raw Input Events” section below for more details. 9B <events> 7B
Input Event Report Returned by the console device in response to a raw event set by the Set Raw Event sequence. See the “Input Event Reports” section below for more details. 9B <parameters> 7C
Reset Raw Events Reset the raw events set by the Set Raw Event sequence. see the “Selecting Raw Input Events” section below. 9B <events> 7D
Special Key Report Returned by the console device whenever Help, or one of the function keys or arrow keys is pressed. Some sequences do not end with 7E 9B <keyvalue> 7E
Set Cursor Rendition Make the cursor visible or invisible; turning off the cursor increases text output speed 9B N 20 70
Invisible = 9B 30 20 70
Visible = 9B 20 70
Window Status Request Ask the console device to tell you the current bounds of the window, in upper and lower row and column character positions. User may have resized or repositioned it. See “Window Bounds Report” below. 9B 30 20 71
Window Bounds Report Returned by the console device in response to a Window Status Request sequence 9B 31 3B 31 3B <bottom margin> 3B <right margin> 72
Right ‘Amiga V’ Press Returned by the console device when the user presses Right-Amiga-V. See the “Copy and Paste Support” section below for more details. 9B 30 20 76
Give Back What You Take
The console device normally handles the Set Page Length, Set Line Length, Set Left Offset, and Set Top Offset functions automatically. To allow it to do so again after setting your own values, send the functions without a parameter.

Example Console Control Sequences

Move cursor right by 1 <CSI>C or
<CSI>1C
9B 43
9B 31 43
Move cursor right by 20 <CSI>20C 9B 32 30 43
Move cursor to upper-left corner (Home Position) <CSI>H or
<CSI>1;1H or
<CSI>;1H or
<CSI>1;H
9B 48
9B 31 3B 31 48
9B 3B 31 48
9B 31 3B 48
Move cursor to the fourth column of the first line of the window <CSI>1;4H or
<CSI>;4H
9B 31 3B 34 48
9B 3B 34 48
Clear the window <FF> or Ctrl-L (clear window) or
<CSI>H<CSI>J (home and clear to end of window)
0C
9B 48 9B 4A

Reading from the Console Device

Reading input from the console device returns an ANSI 3.64 standard byte stream. This stream may contain normal characters and/or raw input event information. You may also request other raw input events using the Set Raw Events and Reset Raw Events control sequences discussed below. See “Selection of Raw Input Events.”

Generally, console reads are performed asynchronously so that your program can respond to other events and other user input (such as menu selections) when the user is not typing on the keyboard. To perform asynchronous I/O, an I/O request is sent to the console using the SendIO() function (rather than a synchronous DoIO() which would wait until the read request returned with a character).

You read from the console device by passing an I/O request to the device with a pointer to the read buffer set in io_Data, the number of bytes in the buffer set in io_Length and CMD_READ set in io_Command.

#define READ_BUFFER_SIZE 25
char ConsoleReadBuffer[READ_BUFFER_SIZE];
 
ConsIO->io_Data    = (APTR)ConsoleReadBuffer;
ConsIO->io_Length  = READ_BUFFER_SIZE;
ConsIO->io_Command = CMD_READ;
IExec->SendIO(ConsIO);
You May Get Less Than You Bargained For
A request for more than one character may be satisfied by the receipt of only one character. If you request more than one character, you will have to examine the io_Actual field of the request when it returns to determine how many characters you have actually received.

After sending the read request, your program can wait on a combination of signal bits including that of the reply port you created. The following fragment demonstrates waiting on both a queued console read request, and Window IDCMP messages:

uint32 conreadsig = 1 << ConsoleMP->mp_SigBit;
uint32 windowsig = 1 << win->UserPort->mp_SigBit;
 
/* A character, or an IDCMP msg, or both will wake us up */
ULONG signals = IExec->Wait(conreadsig | windowsig);
 
if (signals & conreadsig)
    {
    /* Then check for a character */
    };
 
if (signals & windowsig)
    {
    /* Then check window messages */
    };

Information About the Input Stream

For the most part, keys whose keycaps are labeled with ANSI-standard characters will ordinarily be translated into their ASCII-equivalent character by the console device through the use of its keymap. Keymap information can be found in Keymap Library.

For keys other than those with normal ASCII equivalents, an escape sequence is generated and inserted into your input stream. For example, in the default state (no raw input events selected) the function, arrow and special keys (reserved for 101 key keyboards) will cause the sequences shown in the next table to be inserted in the input stream.

Special Key Report Sequences
Key Unshifted Sends Shifted Sends Comment
F1 <CSI>0 <CSI>10
F2 <CSI>1 <CSI>11
F3 <CSI>2 <CSI>12
F4 <CSI>3 <CSI>13
F5 <CSI>4 <CSI>14
F6 <CSI>5 <CSI>15
F7 <CSI>6 <CSI>16
F8 <CSI>7 <CSI>17
F9 <CSI>8 <CSI>18
F10 <CSI>9 <CSI>19
F11 <CSI>20 <CSI>30 (101 key keyboard)
F12 <CSI>21 <CSI>31 (101 key keyboard)
Help <CSI>? <CSI>? (same sequence for both)
Insert <CSI>40 <CSI>50 (101 key keyboard)
Page Up <CSI>41 <CSI>51 (101 key keyboard)
Page Down <CSI>42 <CSI>52 (101 key keyboard)
Pause/Break <CSI>43 <CSI>53 (101 key keyboard)
Home <CSI>44 <CSI>54 (101 key keyboard)
End <CSI>45 <CSI>55 (101 key keyboard)
Cursor Up <CSI>A <CSI>T
Cursor Down <CSI>B <CSI>S
Cursor Left <CSI>D <CSI> A (notice the space after <CSI>)
Cursor Right <CSI>C <CSI> @ (notice the space after <CSI>)

Cursor Position Report

If you have sent the Device Status Report command sequence, the console device returns a cursor position report into your input stream. It takes the form:

<CSI><row>;<column>R

For example, if the cursor is at column 40 and row 12, here are the ASCII values (in hex) you receive in a stream:

9B 34 30 3B 31 32 52

Window Bounds Report

A user may have either moved or resized the window to which your console is bound. By issuing a Window Status Report to the console, you can read the current position and size in the input stream. This window bounds report takes the following form:

<CSI>1;1;<bottom margin>;<right margin> r

The bottom and right margins give you the window row and column dimensions as well. For a window that holds 20 lines with 60 characters per line, you will receive the following in the input stream:

9B 31 3B 31 3B 32 30 3B 36 30 20 72

Copy and Paste Support

As noted above, opening the console device with a unit of CONU_SNIPMAP allows the user to drag-select text with the mouse and copy the selection with Right-Amiga-C.

Internally, the snip is copied to a private buffer managed by the console device where it can be copied to other console device windows by pressing Right-Amiga-V.

However, your application should assume that the user is running the “Conclip” utility which is part of the standard Workbench 2.0 environment. Conclip copies snips from the console device to the clipboard device where they can be used by other applications which support reading from the clipboard.

When Conclip is running and the user presses Right-Amiga-V, the console device puts an escape sequence in your read stream—<CSI>0 v (Hex 9B 30 20 76)—which tells you that the user wants to paste text from the clipboard.

Upon receipt of this sequence, your application should read the contents of the clipboard device, make a copy of any text found there and then release the clipboard so that it can be used by other applications. See Clipboard Device for more information on reading data from it.

You paste what you read from the clipboard by using successive writes to the console. In order to avoid problems with excessively long data in the clipboard, you should limit the size of writes to something reasonable. (We define reasonable as no more than 1K per write with the ideal amount being 256 bytes.) You should also continue to monitor the console read stream for additional use input, paster requests and, possibly, Raw Input Events while you are doing this.

You should not open a character mapped console unit with COPY capability if you are unable to support Paste from the clipboard device. The user will reasonably expect to be able to Paste into windows from which a Copy can be done.

Keep in mind that users do make mistakes, so an Undo mechanism for aborting a Paste is highly desirable—particularly if the user has just accidentally pasted text into an application like a terminal program which is sending data at a slow rate.

Use “CON:”, You’ll Be Glad You Did
It is highly recommended that you consider using the console-handler (CON:) if you want a console window with Copy and Paste capabilities. CON: provides you with free Paste support and is considerably easier to open and use than using the console device directly.

Selecting Raw Input Events

If the keyboard information–including “cooked” keystrokes–does not give you enough information about input events, you can request additional information from the console driver.

The command to Set Raw Events is formatted as:

<CSI>[event-types-separated-by-semicolons]{

If, for example, you need to know when each key is pressed and released, you would request “Raw keyboard input.” This is done by writing <CSI>1<math>\{</math> to the console. In a single Set Raw Events request, you can ask the console to set up for multiple event types at one time. You must send multiple numeric parameters, separating them by semicolons (;). For example, to ask for gadget pressed, gadget released, and close gadget events, write:

<CSI>7;8;11{

You can reset, that is, delete from reporting, one or more of the raw input event types by using the Reset Raw Events command, in the same manner as the Set Raw Events was used to establish them in the first place. This command stream is formatted as:

<CSI>[event-types-separated-by-semicolons]}

So, for example, you could reset all of the events set in the above example by transmitting the command sequence:

<CSI>7;8;11}
The Read Stream May Not Be Dry
There could still be pending Raw Input Events in your read stream after turning off one or more Raw Input Events.

The following table lists the valid raw input event types.

Raw Input Event Types
Request Number Request Description
0 No-op (used internally)
1 RAW keyboard input (Intuition swallows all except the select button)
2 RAW mouse input
3 Private Console Event
4 Pointer position
5 (unused)
6 Timer
7 Gadget pressed
8 Gadget released
9 Requester activity
10 Menu numbers
11 Close Gadget
12 Window resized
13 Window refreshed
14 Preferences changed
15 Disk removed
16 Disk inserted
17 Active window
18 Inactive window
19 New pointer position
20 Menu help
21 Window changed (zoom, move)

The event types—requester, window refreshed, active window, inactive window, window resized and window changed—are dispatched to the console unit which owns the window from which the events are generated, even if it is not the active (selected ) window at the time the event is generated. This ensures that the proper console unit is notified of those events. All other events are dispatched to the active console unit (if it has requested those events).

Input Event Reports

If you select any of these events you will start to get information about the events in the following form:

<CSI><class>;<subclass>;<keycode>;<qualifiers>;<x>;<y>;<seconds>;<microseconds>|
<CSI>
is a one-byte field. It is the “control sequence introducer,” 0x9B in hex.
<class>
is the raw input event type, from the above table.
<subclass>
is usually 0. If the mouse is moved to the right controller, this would be 1.
<keycode>
indicates which raw key number was pressed. This field can also be used for mouse information.
The Raw Key Might Be The Wrong Key
National keyboards often have different keyboard arrangements. This means that a particular raw key number may represent different characters on different national keyboards. The normal console read stream (as opposed to raw events) will contain the proper ASCII character for the keypress as translated according to the user’s keymap.
<qualifiers>
indicates the state of the keyboard and system. The qualifiers are defined as follows:
Input Event Qualifiers
Bit Mask Key Comment
0 0001 Left shift
1 0002 Right shift
2 0004 Caps Lock Associated keycode is special; see below
3 0008 Ctrl
4 0010 Left Alt
5 0020 Right Alt
6 0040 Left Amiga key pressed
7 0080 Right Amiga key pressed
8 0100 Numeric pad
9 0200 Repeat
10 0400 Interrupt Not currently used.
11 0800 Multibroadcast This window (active one) or all windows.
12 1000 Middle mouse button (Not available on standard mouse)
13 2000 Right mouse button
14 4000 Left mouse button
15 8000 Relative mouse Mouse coordinates are relative, not absolute.
The Caps Lock key is handled in a special manner. It generates a keycode only when it is pressed, not when it is released. However, the up/down bit (80 hex) is still used and reported. If pressing the Caps Lock key causes the LED to light, keycode 62 (Caps Lock pressed) is sent. If pressing the Caps Lock key extinguishes the LED, keycode 190 (Caps Lock released) is sent. In effect, the keyboard reports this key as held down until it is struck again.
<x> and <y>
filled by some classes with an Intuition address: x<<16+y.
<seconds> and <microseconds>
contain the system time stamp taken at the time the event occurred. These values are stored as longwords by the system.

With RAW keyboard input selected, keys will no longer return a simple one-character “A” to “Z” but will instead return raw keycode reports of the form:

<CSI>1;0;<keycode>;<qualifiers>;<prev1>;<prev2>;<seconds>;<microseconds>|

For example, if the user pressed and released the A key with the Left Shift and Right Amiga keys also pressed, you might receive the following data:

<CSI>1;0;32;32769;14593;5889;421939940;316673|
<CSI>1;0;160;32769;0;0;421939991;816683|

The <keycode> field is an ASCII decimal value representing the key pressed or released. Adding 128 to the pressed key code will result in the released keycode.

The <prev1> and <prev2> fields are relevant for the interpretation of keys which are modifiable by dead-keys (see “Dead-Class Keys” section). The <prev1> field shows the previous key pressed. The lower byte shows the qualifier, the upper byte shows the key code. The <prev2> field shows the key pressed before the previous key. The lower byte shows the qualifier, the upper byte shows the key code.

Using the Console Device Without a Window

Most console device processing involves a window, but there are functions and special commands that may be used without a window. To use the console device without a window, you call OpenDevice() with the console unit CONU_LIBRARY.

The console device functions are CDInputHandler() and RawKeyConvert(); they may only be used with the CONU_LIBRARY console unit. The console device commands which do not require a window are CD_ASKDEFAULTKEYMAP and CD_SETDEFAULTKEYMAP; they be used with any console unit. The advantage of using the commands with the CONU_LIBRARY unit is the lack of overhead required for CONU_LIBRARY because it doesn’t require a window.

To use the functions requires the following steps:

  • Declare the console device base address variable ConsoleDevice in the global data area.
  • Declare storage for an I/O request of type IOStdReq.
  • Open the console device with CONU_LIBRARY set as the console unit.
  • Set the console device base address variable to point to the device library vector which is returned in io_Device.
  • Call the console device function(s).
  • Close the console device when you are finished.
#include <devices/conunit.h>
 
int main()
{
  struct IOStdReq ConsIO = {0};
 
  /* Open the device with CONU_LIBRARY for function use */
  if (0 == IExec->OpenDevice("console.device", CONU_LIBRARY, (struct IORequest *)&ConsIO, 0))
  {
    /* Set the base address variable to the device library vector */
    struct ConsoleDevice *ConsoleDevice = (struct ConsoleDevice *)ConsIO.io_Device;
    struct ConsoleIFace *IConsole = (struct ConsoleIFace*)
      IExec->GetInterface((struct Library*)ConsoleDevice, "main", 1, NULL);
 
                  .
                  .    (console device functions would be called here)
                  .
 
    IExec->DropInterface((struct Interface *)IConsole);
    IExec->CloseDevice(ConsIO);
  }
}

The code fragment shows only the steps outlined above, it is not complete in any sense of the word. For a complete example of using a console device function, see the rawkey.c code example in the Intuition Keyboard. The example uses the RawKeyConvert() function.

To use the commands with the CONU_LIBRARY console unit, you follow the same steps that were outlined in Opening the Console Device.

struct KeyMap  *keymap;          /* pointer to keymap */
 
    /* Create the message port */
struct MsgPort *ConsoleMP = IExec->AllocSysObjectTags(ASOT_PORT, TAG_END);
if (ConsoleMP != NULL)
    {
        /* Create the I/O request */
    struct IOStdReq *ConsoleIO = IExec->AllocSysObjectTags(ASOT_IOREQUEST,
        ASOIOR_Size, sizeof(struct IOStdReq),
        ASOIOR_ReplyPort, ConsoleMP,
        TAG_END);
 
    if (ConsoleIO != NULL)
        {
            /* Open the Console device */
        if (IExec->OpenDevice("console.device", CONU_LIBRARY, (struct IORequest *)ConsoleIO, 0))
 
            /* Inform user that it could not be opened */
            IDOS->Printf("Error: console.device did not open\n");
        else
            {
               /* Allocate memory for the keymap */
            if (keymap = (struct KeyMap *)
                    AllocVecTags(sizeof(struct KeyMap), AVT_ClearWithValue, 0, TAG_END))
                {
                /* device opened, send CD_ASKKEYMAP command to it */
                ConsoleIO->io_Length  = sizeof(struct KeyMap);
                ConsoleIO->io_Data    = (APTR)keymap;      /* where to put it */
                ConsoleIO->io_Command = CD_ASKKEYMAP;
                IExec->DoIO((struct IORequest *)ConsoleIO))
                }
 
            IExec->CloseDevice(ConsIO);
            }

Again, as in the previous code fragment, this is not complete and you should only use it as a guide.

Console Device Caveats

  • Only one console unit can be attached per window. Sharing a console window must be done at a level higher than the device.

  • Do not mix graphics.library calls with console rendering in the same areas of a window. It is permissible to send console sequences to adjust the area in which console renders, and use graphics.library calls to render outside of the area console is using.

    For example, do not render text with console sequences and scroll using the graphics.library ScrollRaster() function.

  • The character map feature is private and cannot be accessed by the programmer. Implementation details and behaviors of the character map my change in the future.

  • Do not use an IDCMP with character mapped consoles. All Intuition messages should be obtained via Raw Input Events from the console device.

Console Device Example Code

The following is a console device demonstration program with supporting routines:

/*
 * Console.c
 *
 * Example of opening a window and using the console device
 * to send text and control sequences to it.  The example can be
 * easily modified to do additional control sequences.
 *
 * Run from CLI only.
 */
 
#include <exec/types.h>
#include <exec/io.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <libraries/dos.h>
#include <devices/console.h>
 
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <proto/utility.h>
 
/* Note - using two character <CSI> ESC[.  Hex 9B could be used instead */
#define RESETCON  "\033c"
#define CURSOFF   "\033[0 p"
#define CURSON    "\033[ p"
#define DELCHAR   "\033[P"
 
/* SGR (set graphic rendition) */
#define COLOR02   "\033[32m"
#define COLOR03   "\033[33m"
#define ITALICS   "\033[3m"
#define BOLD      "\033[1m"
#define UNDERLINE "\033[4m"
#define NORMAL    "\033[0m"
 
 
/* our functions */
void cleanexit(uint8 *,LONG);
void cleanup(void);
int8 OpenConsole(struct IOStdReq *,struct IOStdReq *, struct Window *);
void CloseConsole(struct IOStdReq *);
void QueueRead(struct IOStdReq *, uint8 *);
uint8 ConGetChar(struct MsgPort *, uint8 *);
LONG ConMayGetChar(struct MsgPort *, uint8 *);
void ConPuts(struct IOStdReq *, uint8 *);
void ConWrite(struct IOStdReq *, uint8 *, LONG);
void ConPutChar(struct IOStdReq *, uint8);
 
struct NewWindow nw =
    {
    10, 10,                           /* starting position (left,top) */
    620,180,                          /* width, height */
    -1,-1,                            /* detailpen, blockpen */
    CLOSEWINDOW,                      /* flags for idcmp */
    WINDOWDEPTH|WINDOWSIZING|
    WINDOWDRAG|WINDOWCLOSE|
    SMART_REFRESH|ACTIVATE,           /* window flags */
    NULL,                             /* no user gadgets */
    NULL,                             /* no user checkmark */
    "Console Test",                   /* title */
    NULL,                             /* pointer to window screen */
    NULL,                             /* pointer to super bitmap */
    100,45,                           /* min width, height */
    640,200,                          /* max width, height */
    WBENCHSCREEN                      /* open on workbench screen */
    };
 
 
/* Opens/allocations we'll need to clean up */
struct Library *IntuitionBase = NULL;
struct IntuitionIFace *IIntuition = NULL;
struct Window   *win = NULL;
struct IOStdReq *writeReq = NULL;    /* IORequest block pointer */
struct MsgPort  *writePort = NULL;   /* replyport for writes      */
struct IOStdReq *readReq = NULL;     /* IORequest block pointer */
struct MsgPort  *readPort = NULL;    /* replyport for reads       */
BOOL OpenedConsole = FALSE;
 
BOOL FromWb;
 
int main(int argc, char **argv)
    {
    struct IntuiMessage *winmsg;
    ULONG signals, conreadsig, windowsig;
    LONG lch;
    SHORT InControl = 0;
    BOOL Done = FALSE;
    uint8 ch, ibuf;
    uint8 obuf[200];
    int8 error;
 
    FromWb = (argc==0) ? TRUE : FALSE;
 
    IntuitionBase = IExec->OpenLibrary("intuition.library", 50);
    IIntuition = (struct IntuitionIFace*)IExec->GetInterface(IntuitionBase, "main", 1, NULL);
    if(IIntuition == NULL)
         cleanexit("Can't open intuition\n",RETURN_FAIL);
 
    /* Create reply port and io block for writing to console */
    writePort = IExec->AllocSysObjectTags(ASOT_PORT,
        ASOPORT_Name, "RKM.console.write",
        TAG_END);
    if(writePort == NULL)
         cleanexit("Can't create write port\n",RETURN_FAIL);
 
    writeReq = IExec->AllocSysObjectTags(ASOT_IOREQUEST,
        ASOIOR_Size, sizeof(struct IOStdReq),
        ASOIOR_ReplyPort, writePort,
        TAG_END);
    if(writeReq == NULL)
         cleanexit("Can't create write request\n",RETURN_FAIL);
 
    /* Create reply port and io block for reading from console */
    readPort = IExec->AllocSysObjectTags(ASOT_PORT,
        ASOPORT_Name, "RKM.console.read",
        TAG_END);
    if(readPort == NULL)
         cleanexit("Can't create read port\n",RETURN_FAIL);
 
    readReq = IExec->AllocSysObjectTags(ASOT_IOREQUEST,
        ASOIOR_Size, sizeof(struct IOStdReq),
        ASOIOR_ReplyPort, readPort,
        TAG_END);
    if(readReq == NULL)
         cleanexit("Can't create read request\n",RETURN_FAIL);
 
    /* Open a window */
    if(!(win = IIntuition->OpenWindow(&nw)))
         cleanexit("Can't open window\n",RETURN_FAIL);
 
    /* Now, attach a console to the window */
    if(error = OpenConsole(writeReq, readReq, win))
         cleanexit("Can't open console.device\n",RETURN_FAIL);
    else OpenedConsole = TRUE;
 
    /* Demonstrate some console escape sequences */
    ConPuts(writeReq,"Here's some normal text\n");
    IUtility->SNPrintf(obuf, sizeof(obuf), "%s%sHere's text in color 3 and italics\n", COLOR03, ITALICS);
    ConPuts(writeReq,obuf);
    ConPuts(writeReq,NORMAL);
    IDOS->Delay(50);      /* Delay for dramatic demo effect */
    ConPuts(writeReq,"We will now delete this asterisk =*=");
    IDOS->Delay(50);
    ConPuts(writeReq,"\b\b");  /* backspace twice */
    IDOS->Delay(50);
    ConPuts(writeReq,DELCHAR); /* delete the character */
    IDOS->Delay(50);
 
    QueueRead(readReq,&ibuf); /* send the first console read request */
 
    ConPuts(writeReq,"\n\nNow reading console\n");
    ConPuts(writeReq,"Type some keys.  Close window when done.\n\n");
 
    conreadsig = 1 << readPort->mp_SigBit;
    windowsig = 1 << win->UserPort->mp_SigBit;
 
    while(!Done)
        {
        /* A character, or an IDCMP msg, or both could wake us up */
        signals = IExec->Wait(conreadsig|windowsig);
 
        /* If a console signal was received, get the character */
        if (signals & conreadsig)
            {
            if((lch = ConMayGetChar(readPort,&ibuf)) != -1)
                {
                ch = lch;
                /* Show hex and ascii (if printable) for char we got.
                 * If you want to parse received control sequences, such as
                 * function or Help keys, you would buffer control sequences
                 * as you receive them, starting to buffer whenever you
                 * receive 0x9B (or 0x1B[ for user-typed sequences) and
                 * ending when you receive a valid terminating character
                 * for the type of control sequence you are receiving.
                 * For CSI sequences, valid terminating characters
                 * are generally 0x40 through 0x7E.
                 * In our example, InControl has the following values:
                 * 0 = no, 1 = have 0x1B, 2 = have 0x9B OR 0x1B and [,
                 * 3 = now inside control sequence, -1 = normal end esc,
                 * -2 = non-CSI(no [) 0x1B end esc
                 * NOTE - a more complex parser is required to recognize
                 *  other types of control sequences.
                 */
 
                /* 0x1B ESC not followed by '[', is not CSI seq */
                if (InControl==1)
                    {
                    if(ch=='[') InControl = 2;
                    else InControl = -2;
                    }
 
                if ((ch==0x9B)||(ch==0x1B))  /* Control seq starting */
                    {
                    InControl = (ch==0x1B) ? 1 : 2;
                    ConPuts(writeReq,"=== Control Seq ===\n");
                    }
 
                /* We'll show value of this char we received */
                if (((ch >= 0x1F)&&(ch <= 0x7E))||(ch >= 0xA0))
                   IUtility->SNPrintf(obuf, sizeof(obuf), "Received: hex %02x = %c\n",ch,ch);
                else IUtility->SNPrintf(obuf, sizeof(obuf), "Received: hex %02x\n",ch);
                ConPuts(writeReq,obuf);
 
                /* Valid ESC sequence terminator ends an ESC seq */
                if ((InControl==3)&&((ch >= 0x40) && (ch <= 0x7E)))
                    {
                    InControl = -1;
                    }
                if (InControl==2) InControl = 3;
                /* ESC sequence finished (-1 if OK, -2 if bogus) */
                if (InControl < 0)
                    {
                    InControl = 0;
                    ConPuts(writeReq,"=== End Control ===\n");
                    }
                }
            }
 
        /* If IDCMP messages received, handle them */
        if (signals & windowsig)
            {
            /* We have to ReplyMsg these when done with them */
            while (winmsg = (struct IntuiMessage *)IExec->GetMsg(win->UserPort))
                {
                switch(winmsg->Class)
                    {
                    case CLOSEWINDOW:
                      Done = TRUE;
                      break;
                    default:
                      break;
                     }
                IExec->ReplyMsg((struct Message *)winmsg);
                }
            }
        }
 
    /* We always have an outstanding queued read request
     * so we must abort it if it hasn't completed,
     * and we must remove it.
     */
    if(!(IExec->CheckIO(readReq)))  IExec->AbortIO(readReq);
    IExec->WaitIO(readReq);     /* clear it from our replyport */
 
    cleanup();
    return RETURN_OK;
    }
 
void cleanexit(uint8 *s,LONG n)
    {
    if(*s & (!FromWb)) IDOS->Printf(s);
    cleanup();
    exit(n);
    }
 
void cleanup()
    {
    if(OpenedConsole) CloseConsole(writeReq);
    IExec->FreeSysObject(ASOT_IOREQUEST, readReq);
    IExec->FreeSysObject(ASOT_PORT, readPort);
    IExec->FreeSysObject(ASOT_IOREQUEST, writeReq);
    IExec->FreeSysObject(ASOT_PORT, writePort);
    if(IIntuition != NULL && win != NULL) IIntuition->CloseWindow(win);
    IExec->DropInterface((struct Interface *)IIntuition);
    IExec->CloseLibrary(IntuitionBase);
    }
 
 
/* Attach console device to an open Intuition window.
 * This function returns a value of 0 if the console
 * device opened correctly and a nonzero value (the error
 * returned from OpenDevice) if there was an error.
 */
int8 OpenConsole(struct IOStdReq *writereq, struct IOStdReq *readreq, struct Window *window)
    {
    int8 error;
 
    writereq->io_Data = (APTR) window;
    writereq->io_Length = sizeof(struct Window);
    error = IExec->OpenDevice("console.device", 0, writereq, 0);
    readreq->io_Device = writereq->io_Device; /* clone required parts */
    readreq->io_Unit   = writereq->io_Unit;
    return(error);
    }
 
void CloseConsole(struct IOStdReq *writereq)
    {
    IExec->CloseDevice(writereq);
    }
 
/* Output a single character to a specified console
 */
void ConPutChar(struct IOStdReq *writereq, uint8 character)
    {
    writereq->io_Command = CMD_WRITE;
    writereq->io_Data = (APTR)&character;
    writereq->io_Length = 1;
    IExec->DoIO(writereq);
    /* command works because DoIO blocks until command is done
     * (otherwise ptr to the character could become invalid)
     */
    }
 
 
/* Output a stream of known length to a console
 */
void ConWrite(struct IOStdReq *writereq, uint8 *string, LONG length)
    {
    writereq->io_Command = CMD_WRITE;
    writereq->io_Data = (APTR)string;
    writereq->io_Length = length;
    IExec->DoIO(writereq);
    /* command works because DoIO blocks until command is done
     * (otherwise ptr to string could become invalid in the meantime)
     */
    }
 
 
/* Output a NULL-terminated string of characters to a console
 */
void ConPuts(struct IOStdReq *writereq,uint8 *string)
    {
    writereq->io_Command = CMD_WRITE;
    writereq->io_Data = (APTR)string;
    writereq->io_Length = -1;  /* means print till terminating null */
    IExec->DoIO(writereq);
    }
 
/* Queue up a read request to console, passing it pointer
 * to a buffer into which it can read the character
 */
void QueueRead(struct IOStdReq *readreq, uint8 *whereto)
   {
   readreq->io_Command = CMD_READ;
   readreq->io_Data = (APTR)whereto;
   readreq->io_Length = 1;
   IExec->SendIO(readreq);
   }
 
 
/* Check if a character has been received.
 * If none, return -1
 */
LONG ConMayGetChar(struct MsgPort *msgport, uint8 *whereto)
    {
    struct IOStdReq *readreq;
 
    if (!(readreq = (struct IOStdReq *)IExec->GetMsg(msgport))) return(-1);
    int temp = *whereto;            /* get the character */
    QueueRead(readreq,whereto);     /* then re-use the request block */
    return(temp);
    }
 
/* Wait for a character
 */
uint8 ConGetChar(struct MsgPort *msgport, uint8 *whereto)
    {
    struct IOStdReq *readreq;
 
    WaitPort(msgport);
    readreq = (struct IOStdReq *)GetMsg(msgport);
    int temp = *whereto;           /* get the character */
    QueueRead(readreq,whereto);    /* then re-use the request block*/
    return((uint8)temp);
    }

Additional Information on the Console Device

Additional programming information on the console device can be found in the include files and the Autodocs for the console device. Both are contained in the SDK.

Includes
devices/console.h
devices/conunit.h
AutoDocs
console.doc