Copyright (c) Hyperion Entertainment and contributors.

Intuition Keyboard

From AmigaOS Documentation Wiki
Jump to navigation Jump to search

The Keyboard

A program can receive keyboard data through an IDCMP port by setting the IDCMP_RAWKEY flag, the IDCMP_VANILLAKEY flag or both. IDCMP_VANILLAKEY events provide for simple ASCII text and standard control keys like space, return and backspace. IDCMP_RAWKEY events provide a more complex input stream, which the program must process to generate ASCII data. IDCMP_RAWKEY returns all keycodes, both key-up and key-down, including function keys.

Keystrokes Are Not Always Paired
Keystrokes do not always come in key-down/key-up pairs. For example, repeating keys appear as a sequence of key-down messages.

IDCMP_RAWKEY and IDCMP_VANILLAKEY may be set together. When both flags are set in the IDCMP, IDCMP_VANILLAKEY messages will be sent for keystrokes that directly map to a single ASCII value. IDCMP_RAWKEY messages will be sent for key sequences that do not map to simple values, i.e. if a key sequence does not map to an IDCMP_VANILLAKEY message, it will be sent as an IDCMP_RAWKEY message. This allows easy access to mapped characters through IDCMP_VANILLAKEY with control characters returned as IDCMP_RAWKEY. Note that the IDCMP_RAWKEY events will only return the key down events when used with IDCMP_VANILLAKEY.

When Intuition responds to an input event or sequence of events, the application will not receive those events. This happens for system shortcuts (left-Amiga-key) if the system shortcut is defined, and for menu shortcuts (right-Amiga-key) if the menu shortcut is defined for the active window. If the shortcut is not defined, then the appropriate key event will be sent with the proper Amiga qualifier set.

Key repeat characters have a queue limit which may be set for each window, much like the mouse queue described above. The key repeat queue limit may only be set when the window is opened using the WA_RptQueue tag, there is no function call for modifying the value after the window is open. The default queue limit for key repeat characters is three. This limit causes any IDCMP_RAWKEY, IDCMP_VANILLAKEY or IDCMP_IDCMPUPDATE message with the IEQUALIFIER_REPEAT bit set to be discarded if the queue is full (IDCMP_IDCMPUPDATE is discussed in the BOOPSI section). The queue is said to be full when the number of waiting repeat key messages is equal to the queue limit. Note that the limit is not per character, it is on the total number of key messages with the repeat bit set. Once the limit is reached, no other repeat characters will be posted to the IDCMP until the application replies to one of the outstanding repeat key messages. The repeat queue limit is not as dangerous as the mouse queue limit as only duplicate keystroke information is discarded, where the mouse queue limit discards information that cannot be easily reproduced.

RAWKEY Keymapping Example

The following example uses RawKeyConvert() to convert the IDCMP_RAWKEY input stream into an ANSI input stream. See the Console Device for more information on RawKeyConvert() and the data it returns.

/*
** rawkey.c - How to correctly convert from RAWKEY to keymapped ASCII
*/
#define INTUI_V36_NAMES_ONLY
 
#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <devices/inputevent.h>
#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/console.h>
#include <proto/dos.h>
 
/* our function prototypes */
LONG deadKeyConvert(struct IntuiMessage *msg, UBYTE *kbuffer,
                    LONG kbsize, struct KeyMap *kmap, struct InputEvent *ievent);
VOID print_qualifiers(ULONG qual);
BOOL doKeys(struct IntuiMessage *msg, struct InputEvent *ievent,
                    UBYTE **buffer, ULONG *bufsize);
VOID process_window(struct Window *win, struct InputEvent *ievent,
                    UBYTE **buffer, ULONG *bufsize);
 
/* A buffer is created for RawKeyConvert() to put its output. BUFSIZE is the size of
** the buffer in bytes.  NOTE that this program starts out with a buffer size of 2.
** This is only to show how the buffer is automatically increased in size  by this
** example!  In an application, start with a much larger buffer and you will probably
** never have to increase its size. 128 bytes or so should do the trick, but always
** be able to change the size if required.
*/
#define BUFSIZE (2)
 
struct IntutionIFace *IIntuition;
struct ConsoleIFace *IConsole;
 
/* main() - set-up everything used by program. */
VOID main(int argc, char **argv)
{
  struct Window *win;
  struct IOStdReq ioreq;
  struct InputEvent *ievent;
  UBYTE *buffer;
  ULONG bufsize = BUFSIZE;
 
  struct Library *IntuitionBase = IExec->OpenLibrary("intuition.library", 50);
  if(IIntuition = IExec->GetInterface(IntuitionBase, "main", 1, NULL))
  {
    /* Open the console device just to do keymapping. (unit -1 means any unit) */
    if (0 == IExec->OpenDevice("console.device",-1,(struct IORequest *)&ioreq,0))
    {
        struct Library *ConsoleDevice = (struct Library *)ioreq.io_Device;
        IConsole = (struct ConsoleIFace*)IExec->GetInterface(ConsoleDevice, "main", 1, NULL);
 
        /* Allocate the initial character buffer used by deadKeyConvert() and RawKeyConvert()
        ** for returning translated characters. If the characters generated by these routines
        ** cannot fit into the buffer, the application must pass a larger buffer.  This is
        ** done in this code by freeing the old buffer and allocating a new one.
        */
        if (buffer = IExec->AllocMem(bufsize,MEMF_CLEAR))
        {
            if (ievent = IExec->AllocMem(sizeof(struct InputEvent),MEMF_CLEAR))
            {
                if (win = IIntuition->OpenWindowTags(NULL,
                        WA_Width, 300,
                        WA_Height, 50,
                        WA_Flags, WFLG_DEPTHGADGET | WFLG_CLOSEGADGET | WFLG_ACTIVATE,
                        WA_IDCMP, IDCMP_CLOSEWINDOW | IDCMP_RAWKEY,
                        WA_Title, "Raw Key Example",
                        TAG_END))
                {
                  IDOS->Printf("Press keyboard keys to see ASCII conversion from rawkey\n");
                  IDOS->Printf("Unprintable characters will be shown as %lc\n\n",0x7f);
                  process_window(win,ievent,&buffer,&bufsize);
                  IIntuition->CloseWindow(win);
                }
                IExec->FreeMem(ievent,sizeof(struct InputEvent));
              }
 
              /* Buffer can be freed elsewhere in the program so test first. */
              if (buffer != NULL)
                IExec->FreeMem(buffer,bufsize);
        }
        IExec->CloseDevice((struct IORequest *)&ioreq);
      }
    IExec->DropInterface((struct Interface*)IIntuition);
  }
  IExec->CloseLibrary(IntuitionBase);
}
 
/* Convert RAWKEYs into VANILLAKEYs, also shows special keys like HELP, Cursor Keys,
** FKeys, etc.  It returns:
**   -2 if not a RAWKEY event.
**   -1 if not enough room in the buffer, try again with a bigger buffer.
**   otherwise, returns the number of characters placed in the buffer.
*/
LONG deadKeyConvert(struct IntuiMessage *msg, UBYTE *kbuffer,
    LONG kbsize, struct KeyMap *kmap, struct InputEvent *ievent)
{
  if (msg->Class != IDCMP_RAWKEY)
    return(-2);
 
  ievent->ie_Class = IECLASS_RAWKEY;
  ievent->ie_Code = msg->Code;
  ievent->ie_Qualifier = msg->Qualifier;
  ievent->ie_position.ie_addr = *((APTR*)msg->IAddress);
 
  return IConsole->RawKeyConvert(ievent,kbuffer,kbsize,kmap);
}
 
/* print_qualifiers() - print out the values found in the qualifier bits of
** the message. This will print out all of the qualifier bits set.
*/
VOID print_qualifiers(ULONG qual)
{
  IDOS->Printf("Qual:");
  if (qual & IEQUALIFIER_LSHIFT)         IDOS->Printf("LShft,");
  if (qual & IEQUALIFIER_RSHIFT)         IDOS->Printf("RShft,");
  if (qual & IEQUALIFIER_CAPSLOCK)       IDOS->Printf("CapLok,");
  if (qual & IEQUALIFIER_CONTROL)        IDOS->Printf("Ctrl,");
  if (qual & IEQUALIFIER_LALT)           IDOS->Printf("LAlt,");
  if (qual & IEQUALIFIER_RALT)           IDOS->Printf("RAlt,");
  if (qual & IEQUALIFIER_LCOMMAND)       IDOS->Printf("LCmd,");
  if (qual & IEQUALIFIER_RCOMMAND)       IDOS->Printf("RCmd,");
  if (qual & IEQUALIFIER_NUMERICPAD)     IDOS->Printf("NumPad,");
  if (qual & IEQUALIFIER_REPEAT)         IDOS->Printf("Rpt,");
  if (qual & IEQUALIFIER_INTERRUPT)      IDOS->Printf("Intrpt,");
  if (qual & IEQUALIFIER_MULTIBROADCAST) IDOS->Printf("Multi Broadcast,");
  if (qual & IEQUALIFIER_MIDBUTTON)      IDOS->Printf("MidBtn,");
  if (qual & IEQUALIFIER_RBUTTON)        IDOS->Printf("RBtn,");
  if (qual & IEQUALIFIER_LEFTBUTTON)     IDOS->Printf("LBtn,");
  if (qual & IEQUALIFIER_RELATIVEMOUSE)  IDOS->Printf("RelMouse,");
}
 
/* doKeys() - Show what keys were pressed. */
BOOL doKeys(struct IntuiMessage *msg, struct InputEvent *ievent,
            UBYTE **buffer, ULONG *bufsize)
{
USHORT char_pos;
USHORT numchars;
BOOL   ret_code = TRUE;
UBYTE  realc, c;
 
/* deadKeyConvert() returns -1 if there was not enough space in the buffer to
** convert the string. Here, the routine increases the size of the buffer on the
** fly...Set the return code to FALSE on failure.
*/
numchars = deadKeyConvert(msg, *buffer, *bufsize - 1, NULL, ievent);
while ((numchars == -1) && (*buffer != NULL))
    {
    /* conversion failed, buffer too small. try to double the size of the buffer. */
    IExec->FreeMem(*buffer, *bufsize);
    *bufsize = *bufsize << 1;
    IDOS->Printf("Increasing buffer size to %ld\n", *bufsize);
 
    if (NULL == (*buffer = IExec->AllocMem(*bufsize, MEMF_CLEAR)))  ret_code = FALSE;
    else  numchars = deadKeyConvert(msg, *buffer, *bufsize - 1, NULL, ievent);
    }
 
/* numchars contains the number of characters placed within the buffer.  Key up events and   */
/* key sequences that do not generate any data for the program (like deadkeys) will return   */
/* zero.  Special keys (like HELP, the cursor keys, FKeys, etc.) return multiple characters  */
/* that have to then be parsed by the application.  Allocation failed above if buffer is NULL*/
if (*buffer != NULL) {
    /* if high bit set, then this is a key up otherwise this is a key down */
    if (msg->Code & 0x80)
          IDOS->Printf("Key Up:   ");
    else
          IDOS->Printf("Key Down: ");
 
    print_qualifiers(msg->Qualifier);
    IDOS->Printf(" rawkey #%ld maps to %ld ASCII character(s)\n", 0x7F & msg->Code, numchars);
    for (char_pos = 0; char_pos < numchars; char_pos++) {
        realc = c = (*buffer)[char_pos];
        if ((c <= 0x1F)||((c >= 0x80)&&(c < 0xa0)))
            c = 0x7f;
        IDOS->Printf("  %3ld ($%02lx) = %lc\n", realc, realc, c);
        }
    }
return(ret_code);
}
 
/* process_window() - simple event loop.  Note that the message is not replied
** to until the end of the loop so that it may be used in the doKeys() call.
*/
VOID process_window(struct Window *win, struct InputEvent *ievent,
    UBYTE **buffer, ULONG *bufsize)
{
  struct IntuiMessage *msg;
  BOOL done;
 
  done = FALSE;
  while (done == FALSE)
  {
    IExec->Wait((1L<<win->UserPort->mp_SigBit));
    while ((done == FALSE) && (msg = (struct IntuiMessage *)IExec->GetMsg(win->UserPort)))
    {
        switch (msg->Class) {     /* handle our events */
            case IDCMP_CLOSEWINDOW:
                done = TRUE;
                break;
            case IDCMP_RAWKEY:
                if (FALSE == doKeys(msg,ievent,buffer,bufsize))
                    done = TRUE;
                break;
            }
        IExec->ReplyMsg((struct Message *)msg);
    }
  }
}

Keyboard Control of the Pointer

All Intuition mouse activities can be emulated using the keyboard, by combining the Amiga command keys with other keystrokes.

The pointer can be moved by holding down either Amiga key along with one of the four cursor keys. The mouse pointer accelerates the longer these keys are held down. Additionally, holding down either Shift key will make the pointer jump in larger increments. The pointer position may also be adjusted in very fine increments through this technique. By holding down either Amiga key and briefly pressing one of the cursor keys, the pointer may be moved one pixel in any direction.

Press the left Alt key and either one of the Amiga keys simultaneously emulates the left button of the mouse. Similarly, pressing the right Alt key and either one of the Amiga keys simultaneously emulates the right button of the mouse. These key combinations permit users to make gadget selections and perform menu operations using the keyboard alone.

Intuition Keyboard Shortcuts

If Intuition sees a command key sequence that means nothing to it, the key sequence is sent to the active window as usual. See the Intuition Input and Output Methods section for how this works. This section and the next section describe what Intuition does when it recognizes certain special command key sequences.

It is recommended that programs abide by certain command key standards to provide a consistent interface for Amiga users. The Amiga User Interface Style Guide contains a complete list of the recommended standards.

There are a number of special keyboard shortcuts supported by Intuition. These involve holding down the left Amiga key and simultaneously pressing a another key. These functions allow the user to do such things as move the Workbench screen to the front using the keyboard.

Intuition Keyboard Shortcuts
Keyboard Shortcut Function Performed
left Amiga M Move frontmost screen to back.
left Amiga N Move Workbench screen to front.
left Amiga B System requester cancel, or select the rightmost button in the system requester.
left Amiga V System requester OK, or select the leftmost button in the system requester.
left Amiga + mouse Screen drag from any point. By holding down the left Amiga key, the mouse select button user may drag the screen with the mouse from any part of the screen or window on the screen.
About System Keyboard Shortcuts
Many of these keyboard commands may be remapped through the IControl Preferences editor. Do not rely on the values reported here.

Intuition consumes these command key sequences for its own use. That is, it always detects these events and removes them from the input stream. The application will not see the events.

Menu Shortcuts

Menu items and sub-items may be paired with command key sequences to associate certain characters with specific menu item selections. This gives the user a shortcut method to select frequently used menu operations, such as Undo, Cut, and Paste. Whenever the user presses the right Amiga key with an alphanumeric key, the menu strip of the active window is scanned to see if there are any command key sequences in the list that match the sequence entered by the user. If there is a match, Intuition translates the key combination into the appropriate menu item number and transmits the menu number to the application program.

Menu Shortcuts Look Like the Real Thing
To the application it looks as if the user had selected a given menu item with the mouse. The program will receive a menu event, not a key event. For more information on menu item selection, see the Intuition Menus chapter.

Amiga Qualifiers

The Amiga keyboard has several special qualifiers which are listed in the next table. Most of these qualifiers are associated with special keys on the keyboard such as the Shift or Ctrl key. These keys are used to modify the meaning of other keys. Other qualifiers are associated with mouse button status. For a complete list of all the qualifiers, see the include file <devices/inputevent.h>.

The Qualifier field of each IntuiMessage contains the status of all the qualifiers. An individual application should never attempt to track the state of any of the qualifier keys or mouse buttons even though key-down and key-up information may be available. Instead use the information available in the Qualifier field of the IntuiMessage structure.

Keyboard Qualifiers
Qualifier Type Key Label Explanation
Control Ctrl The IEQUALIFIER_CONTROL bit indicates that the Control key is depressed.
Amiga Fancy A or Boing Ball There are two Amiga keys, one on each side of the space bar. The left Amiga key is recognized by the Qualifier bit IEQUALIFIER_LCOMMAND, and the right Amiga key by IEQUALIFIER_RCOMMAND.
Alternate Alt There are two separate Alt keys, one on each side of the space bar, next to the Amiga keys. These can be treated separately, if desired. The left Alt key sets the IEQUALIFIER_LALT bit and the right Alt key sets the IEQUALIFIER_RALT bit.
Shift Up Arrow There are two separate Shift keys, one above each Alt key. These can be treated distinctly, if desired. The left Shift key sets the IEQUALIFIER_LSHIFT bit and the right Shift key sets the IEQUALIFIER_RSHIFT bit.
Caps Lock Caps Lock The IEQUALIFIER_CAPSLOCK bit is set as long as the Caps Lock light is illuminated.
Numeric Pad The IEQUALIFIER_NUMERICPAD bit is set for keys on the numeric keypad.
Repeat Repeat key events are sent with the IEQUALIFIER_REPEAT bit set.
Mouse Buttons If mouse buttons are down when the event occurs, one or more of the three bits IEQUALIFIER_LEFTBUTTON, IEQUALIFIER_MIDBUTTON or IEQUALIFIER_RBUTTON will be set.