[← Home](../README.md) · [Devices](README.md)
# 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
```mermaid
flowchart TD
KB["keyboard.device"] --> ID["input.device"]
GP["gameport.device"] --> ID
OTHER["Other sources"] --> ID
ID --> H1["Handler Pri 100
Commodities Exchange"]
H1 --> H2["Handler Pri 51
Custom 'hot key' handler"]
H2 --> H3["Handler Pri 50
Intuition"]
H3 --> H4["Handler Pri 0
Console.device"]
H4 --> H5["Handler Pri -50
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
```c
/* 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
```c
#include
#include
/* 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)
```c
/* 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
```c
/* 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:
```c
/* 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](keyboard.md) — raw key code map
- See also: [idcmp.md](../09_intuition/idcmp.md) — Intuition input handling
- See also: [input_events.md](../09_intuition/input_events.md) — event flow through Intuition