amiga-bootcamp/10_devices/input.md
Ilia Sharin 0ded078134 Phase 1 continued: serial, input devices + README indexes
10_devices:
- serial.md: expanded with DB-25 pinout, custom chip UART registers,
  baud rate calculation table, open/configure/read/write patterns,
  serial debugging (KPrintF/Sushi)
- input.md: expanded with handler chain architecture diagram,
  InputEvent struct, event classes table, qualifier bits, custom
  handler with key remapping, event consumption/blocking,
  synthetic event injection, Commodities Exchange

Updated indexes:
- 07_dos/README.md — enriched descriptions for all files
- 10_devices/README.md — enriched descriptions for all files
2026-04-23 20:28:03 -04:00

6.5 KiB

← Home · Devices

input.device — Input Event Handler Chain

Overview

input.device is the central dispatcher for all input events on AmigaOS. It collects raw events from keyboard, mouse, joystick, and other sources, then distributes them through a priority-ordered handler chain. Intuition sits at priority 50 in this chain — custom input handlers can intercept events before or after Intuition.


Handler Chain Architecture

flowchart TD
    KB["keyboard.device"] --> ID["input.device"]
    GP["gameport.device"] --> ID
    OTHER["Other sources"] --> ID

    ID --> H1["Handler Pri 100<br/>Commodities Exchange"]
    H1 --> H2["Handler Pri 51<br/>Custom 'hot key' handler"]
    H2 --> H3["Handler Pri 50<br/>Intuition"]
    H3 --> H4["Handler Pri 0<br/>Console.device"]
    H4 --> H5["Handler Pri -50<br/>Application handler"]

    style H3 fill:#e8f4fd,stroke:#2196f3,color:#333

Events flow from highest to lowest priority. Each handler can:

  • Pass the event through (return the event list unchanged)
  • Modify the event (e.g., remap keys)
  • Consume the event (remove from chain — lower handlers never see it)
  • Insert new synthetic events

InputEvent Structure

/* devices/inputevent.h — NDK39 */
struct InputEvent {
    struct InputEvent *ie_NextEvent;  /* linked list */
    UBYTE  ie_Class;        /* event type (IECLASS_*) */
    UBYTE  ie_SubClass;     /* subclass */
    UWORD  ie_Code;         /* key code, button, qualifier */
    UWORD  ie_Qualifier;    /* modifier keys state */
    union {
        struct {
            WORD ie_x;      /* mouse X delta or absolute */
            WORD ie_y;      /* mouse Y delta or absolute */
        } ie_xy;
        APTR ie_addr;       /* pointer data */
    } ie_position;
    struct timeval ie_TimeStamp;  /* when event occurred */
};

Event Classes

Class Constant Source
$01 IECLASS_RAWKEY Raw key press/release
$02 IECLASS_RAWMOUSE Mouse movement + buttons
$04 IECLASS_TIMER Timer tick event
$07 IECLASS_NEWPOINTERPOS Absolute pointer position
$09 IECLASS_DISKINSERTED Disk inserted
$0A IECLASS_DISKREMOVED Disk removed
$0D IECLASS_NEWPREFS Preferences changed

Qualifier Bits

Bit Constant Key
0 IEQUALIFIER_LSHIFT Left Shift
1 IEQUALIFIER_RSHIFT Right Shift
2 IEQUALIFIER_CAPSLOCK Caps Lock
3 IEQUALIFIER_CONTROL Ctrl
4 IEQUALIFIER_LALT Left Alt
5 IEQUALIFIER_RALT Right Alt
6 IEQUALIFIER_LCOMMAND Left Amiga
7 IEQUALIFIER_RCOMMAND Right Amiga
8 IEQUALIFIER_NUMERICPAD Key is on numeric pad
9 IEQUALIFIER_REPEAT Key repeat (auto-repeat)
10 IEQUALIFIER_INTERRUPT Event from interrupt
11 IEQUALIFIER_MULTIBROADCAST Broadcast to all handlers
12 IEQUALIFIER_MIDBUTTON Middle mouse button
13 IEQUALIFIER_RBUTTON Right mouse button
14 IEQUALIFIER_LEFTBUTTON Left mouse button
15 IEQUALIFIER_RELATIVEMOUSE Mouse values are deltas

Installing a Custom Input Handler

#include <devices/input.h>
#include <devices/inputevent.h>

/* Handler function — called from input.device context: */
struct InputEvent * __saveds __asm MyHandler(
    register __a0 struct InputEvent *events,
    register __a1 APTR handlerData)
{
    struct InputEvent *ev;
    for (ev = events; ev; ev = ev->ie_NextEvent)
    {
        if (ev->ie_Class == IECLASS_RAWKEY)
        {
            UWORD key = ev->ie_Code & 0x7F;
            BOOL  up  = ev->ie_Code & 0x80;

            /* Example: remap F10 ($59) to Escape ($45): */
            if (key == 0x59)
                ev->ie_Code = (up ? 0x80 : 0x00) | 0x45;
        }
    }
    return events;  /* pass all events to next handler */
}

/* Install the handler: */
struct Interrupt handlerInt;
handlerInt.is_Node.ln_Type = NT_INTERRUPT;
handlerInt.is_Node.ln_Pri  = 51;  /* just above Intuition (50) */
handlerInt.is_Node.ln_Name = "MyKeyMapper";
handlerInt.is_Data = myData;
handlerInt.is_Code = (APTR)MyHandler;

struct MsgPort *inputPort = CreateMsgPort();
struct IOStdReq *inputReq = (struct IOStdReq *)
    CreateIORequest(inputPort, sizeof(struct IOStdReq));
OpenDevice("input.device", 0, (struct IORequest *)inputReq, 0);

inputReq->io_Command = IND_ADDHANDLER;
inputReq->io_Data    = (APTR)&handlerInt;
DoIO((struct IORequest *)inputReq);

/* ... handler is now active ... */

/* Remove handler before exit: */
inputReq->io_Command = IND_REMHANDLER;
inputReq->io_Data    = (APTR)&handlerInt;
DoIO((struct IORequest *)inputReq);

Consuming Events (Blocking)

/* To consume an event (prevent it from reaching Intuition): */
struct InputEvent * __saveds __asm BlockEscape(
    register __a0 struct InputEvent *events,
    register __a1 APTR data)
{
    struct InputEvent *ev, *prev = NULL;
    for (ev = events; ev; )
    {
        if (ev->ie_Class == IECLASS_RAWKEY &&
            (ev->ie_Code & 0x7F) == 0x45)  /* Escape */
        {
            /* Remove from chain: */
            if (prev)
                prev->ie_NextEvent = ev->ie_NextEvent;
            else
                events = ev->ie_NextEvent;
            ev = ev->ie_NextEvent;
            continue;
        }
        prev = ev;
        ev = ev->ie_NextEvent;
    }
    return events;
}

Injecting Synthetic Events

/* Send a fake key press: */
struct InputEvent fake;
fake.ie_Class     = IECLASS_RAWKEY;
fake.ie_Code      = 0x45;  /* Escape, key down */
fake.ie_Qualifier = 0;

inputReq->io_Command = IND_WRITEEVENT;
inputReq->io_Data    = (APTR)&fake;
inputReq->io_Length  = sizeof(struct InputEvent);
DoIO((struct IORequest *)inputReq);

Commodities Exchange

The Commodities Exchange (commodities.library) provides a high-level framework for input handlers:

/* Define a hot key: */
CxObj *broker = CxBroker(&newBroker, NULL);
CxObj *filter = CxFilter("rawkey control esc");  /* Ctrl+Esc */
CxObj *sender = CxSender(brokerPort, EVT_HOTKEY);
AttachCxObj(filter, sender);
AttachCxObj(broker, filter);
ActivateCxObj(broker, TRUE);

This is the preferred method for applications — avoids writing raw input handlers.


References

  • NDK39: devices/input.h, devices/inputevent.h
  • ADCD 2.1: input.device autodocs
  • See also: keyboard.md — raw key code map
  • See also: idcmp.md — Intuition input handling
  • See also: input_events.md — event flow through Intuition