Copyright (c) Hyperion Entertainment and contributors.
Difference between revisions of "Input Device"
Steven Solie (talk | contribs) |
Ryan Dixon (talk | contribs) m |
||
(14 intermediate revisions by 3 users not shown) | |||
Line 1: | Line 1: | ||
− | [[Category:Devices|Input]]{{ |
+ | [[Category:Devices|Input]]{{CodeReview}} |
== Input Device == |
== Input Device == |
||
Line 17: | Line 17: | ||
|- |
|- |
||
| CMD_STOP || Stop any currently active input and prevent queued I/O requests from starting. |
| CMD_STOP || Stop any currently active input and prevent queued I/O requests from starting. |
||
+ | |- |
||
+ | | IND_ADDEVENT || Propagate an input event to all handlers, updating event qualifiers. |
||
|- |
|- |
||
| IND_ADDHANDLER || Add an input-stream handler into the handler chain. |
| IND_ADDHANDLER || Add an input-stream handler into the handler chain. |
||
|- |
|- |
||
+ | | IND_ADDNOTIFY || Add a hook which will be invoked whenever a configuration option changes. |
||
− | | IND_REMHANDLER || Remove an input-stream handler from the handler chain. |
||
|- |
|- |
||
| IND_GETHANDLERLIST || Obtain a pointer to the list of input handlers. |
| IND_GETHANDLERLIST || Obtain a pointer to the list of input handlers. |
||
Line 31: | Line 33: | ||
|- |
|- |
||
| IND_GETTHRESH || Get the key repeat threshold. |
| IND_GETTHRESH || Get the key repeat threshold. |
||
+ | |- |
||
+ | | IND_IMMEDIATEADDNOTIFY || Add a hook which will be invoked whenever a configuration option changes. The hook will be invoked on the current configuration first. |
||
+ | |- |
||
+ | | IND_REMHANDLER || Remove an input-stream handler from the handler chain. |
||
+ | |- |
||
+ | | IND_REMOVENOTIFY || Remove a hook previously installed with the IND_ADDNOTIFY command. |
||
|- |
|- |
||
| IND_SETKDEVICE || Choose the device to collect keyboard input events from. |
| IND_SETKDEVICE || Choose the device to collect keyboard input events from. |
||
Line 47: | Line 55: | ||
|- |
|- |
||
| IND_WRITEEVENT || Propagate an input event stream to all devices. |
| IND_WRITEEVENT || Propagate an input event stream to all devices. |
||
− | |- |
||
− | | IND_ADDEVENT || Propagate an input event to all handlers, updating event qualifiers. |
||
− | |- |
||
− | | IND_ADDEVENT || Propagate an input event to all handlers, updating event qualifiers. |
||
− | |- |
||
− | | IND_ADDNOTIFY || Add a hook which will be invoked whenever an input.device configuration option is changed. |
||
− | |- |
||
− | | IND_IMMEDIATEADDNOTIFY || Add a hook which will be invoked whenever an input.device configuration option is changed. The hook will be invoked on the current configuration first. |
||
− | |- |
||
− | | IND_REMOVENOTIFY || Remove a hook previously installed with the IND_ADDNOTIFY command. |
||
|} |
|} |
||
− | |||
− | Input Device Functions: |
||
{| class="wikitable" |
{| class="wikitable" |
||
+ | |+ Input Device Functions |
||
| PeekQualifier() || Return the input device’s current qualifiers. |
| PeekQualifier() || Return the input device’s current qualifiers. |
||
|} |
|} |
||
Line 174: | Line 171: | ||
The various types of input events are listed below. |
The various types of input events are listed below. |
||
− | '''Input Device Event Types''' |
||
{| class="wikitable" |
{| class="wikitable" |
||
+ | |+ Input Device Event Types |
||
| IECLASS_NULL || A NOP input event |
| IECLASS_NULL || A NOP input event |
||
|- |
|- |
||
Line 354: | Line 351: | ||
</syntaxhighlight> |
</syntaxhighlight> |
||
− | See |
+ | See [[Gameport Device]] for a full description of setting mouse port trigger conditions. |
== Adding an Input Handler == |
== Adding an Input Handler == |
||
Line 417: | Line 414: | ||
You remove a handler from the handler chain by passing an IOStdReq to the device IND_REMHANDLER set in io_Command and a pointer to the Interrupt structure used to add the handler. |
You remove a handler from the handler chain by passing an IOStdReq to the device IND_REMHANDLER set in io_Command and a pointer to the Interrupt structure used to add the handler. |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
struct Interrupt *InputHandler; |
struct Interrupt *InputHandler; |
||
struct IOStdReq *InputIO; |
struct IOStdReq *InputIO; |
||
− | InputIO- |
+ | InputIO->io_Data = (APTR)InputHandler; /* Which handler to REM */ |
− | InputIO- |
+ | InputIO->io_Command = IND_REMHANDLER; /* The REM command */ |
− | DoIO((struct IORequest *)InputIO); |
+ | IExec->DoIO((struct IORequest *)InputIO); /* Send the command */ |
+ | </syntaxhighlight> |
||
− | </pre> |
||
== Writing Events to the Input Device Stream == |
== Writing Events to the Input Device Stream == |
||
Line 434: | Line 431: | ||
You generate an input event by passing an IOStdReq to the device with IND_WRITEEVENT set in io_Command, a pointer to an InputEvent structure set in io_Data and the length of the structure set in io_Length. |
You generate an input event by passing an IOStdReq to the device with IND_WRITEEVENT set in io_Command, a pointer to an InputEvent structure set in io_Data and the length of the structure set in io_Length. |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
struct InputEvent *FakeEvent; |
struct InputEvent *FakeEvent; |
||
struct IOStdReq *InputIO; |
struct IOStdReq *InputIO; |
||
− | InputIO- |
+ | InputIO->io_Data = (APTR)FakeEvent; |
− | InputIO- |
+ | InputIO->io_Length = sizeof(struct InputEvent); |
− | InputIO- |
+ | InputIO->io_Command = IND_WRITEEVENT; |
− | DoIO((struct IORequest *)InputIO); |
+ | IExec->DoIO((struct IORequest *)InputIO); |
+ | </syntaxhighlight> |
||
− | </pre> |
||
+ | {{Note|title=You Know What Happens When You Assume|text=This command propagates the input event through the handler chain. The handlers may link other events onto the end of this event or modify the contents of the data structure you constructed in any way they wish. Therefore, do not assume any of the data will be the same from event to event.}} |
||
− | {| class="wikitable" |
||
− | | ''You Know What Happens When You Assume.'' This command propagates the input event through the handler chain. The handlers may link other events onto the end of this event or modify the contents of the data structure you constructed in any way they wish. Therefore, do not assume any of the data will be the same from event to event. |
||
− | |} |
||
=== Setting the Position of the Mouse === |
=== Setting the Position of the Mouse === |
||
Line 457: | Line 452: | ||
* At a position relative to the current mouse pointer position on the current screen. |
* At a position relative to the current mouse pointer position on the current screen. |
||
− | In both cases, you set the Class field of the InputEvent structure to IECLASS_POINTERPOS, ie_X with the new x-coordinate and ie_Y with the new y-coordinate. Absolute positioning is done by setting ie_Qualifier to |
+ | In both cases, you set the Class field of the InputEvent structure to IECLASS_POINTERPOS, ie_X with the new x-coordinate and ie_Y with the new y-coordinate. Absolute positioning is done by setting ie_Qualifier to 0 and relative positioning is done by setting ie_Qualifier to IEQUALIFIER_RELATIVEMOUSE. |
Once the proper values are set, pass an IOStdReq to the input device with a pointer to the InputEvent structure set in io_Data and io_Command set to IND_WRITEEVENT. |
Once the proper values are set, pass an IOStdReq to the input device with a pointer to the InputEvent structure set in io_Data and io_Command set to IND_WRITEEVENT. |
||
Line 472: | Line 467: | ||
* Set up a structure to indicate the new position of the pointer. |
* Set up a structure to indicate the new position of the pointer. |
||
− | For absolute and relative positioning, you set up an IEPointerPixel structure with iepp_Position.X set to the new x-coordinate, iepp_Position.Y set to the new y-coordinate and iepp_Screen set to the screen pointer. You set up an InputEvent structure with ie_SubClass set to IESUBCLASS_PIXEL, a pointer to the IEPointerPixel structure set in ie_EventAddress, IECLASS_NEWPOINTERPOS set in Class, and ie_Qualifier set to either IEQUALIFIER_RELATIVEMOUSE for relative positioning or |
+ | For absolute and relative positioning, you set up an IEPointerPixel structure with iepp_Position.X set to the new x-coordinate, iepp_Position.Y set to the new y-coordinate and iepp_Screen set to the screen pointer. You set up an InputEvent structure with ie_SubClass set to IESUBCLASS_PIXEL, a pointer to the IEPointerPixel structure set in ie_EventAddress, IECLASS_NEWPOINTERPOS set in Class, and ie_Qualifier set to either IEQUALIFIER_RELATIVEMOUSE for relative positioning or 0 for absolute positioning. |
For tablet positioning, you set up an IEPointerTablet structure with iept_Range.X set to the maximum x-coordinate and iept_Range.Y set to the maximum y-coordinate, and iept_Value.X set to the new x-coordinate and iept_Value.Y set to the new y-coordinate. You set up an InputEvent structure with a pointer to the IEPointerTablet structure set in ie_EventAddress, ie_SubClass to IESUBCLASS_TABLET and Class set to IECLASS_NEWPOINTERPOS. |
For tablet positioning, you set up an IEPointerTablet structure with iept_Range.X set to the maximum x-coordinate and iept_Range.Y set to the maximum y-coordinate, and iept_Value.X set to the new x-coordinate and iept_Value.Y set to the new y-coordinate. You set up an InputEvent structure with a pointer to the IEPointerTablet structure set in ie_EventAddress, ie_SubClass to IESUBCLASS_TABLET and Class set to IECLASS_NEWPOINTERPOS. |
||
Line 480: | Line 475: | ||
The following example sets the mouse pointer at an absolute position on a public screen using IECLASS_NEWPOINTERPOS. |
The following example sets the mouse pointer at an absolute position on a public screen using IECLASS_NEWPOINTERPOS. |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
/* |
/* |
||
* Set_Mouse.c |
* Set_Mouse.c |
||
* |
* |
||
* This example sets the mouse at x=100 and y=200 |
* This example sets the mouse at x=100 and y=200 |
||
− | * |
||
− | * Compile with SAS C 5.10: LC -b1 -cfistq -v -y -L |
||
− | * Requires Kickstart 36 or greater. |
||
* |
* |
||
* Run from CLI only |
* Run from CLI only |
||
*/ |
*/ |
||
− | #include |
+ | #include <exec/types.h> |
− | #include |
+ | #include <exec/memory.h> |
− | #include |
+ | #include <devices/input.h> |
− | #include |
+ | #include <devices/inputevent.h> |
− | #include |
+ | #include <intuition/screens.h> |
− | #include |
+ | #include <proto/dos.h> |
− | #include |
+ | #include <proto/exec.h> |
+ | #include <proto/intuition.h> |
||
+ | struct IntuitionIFace *IIntuition = NULL; |
||
− | #include <stdio.h> |
||
+ | int main() |
||
− | #ifdef LATTICE |
||
− | int CXBRK(void) { return(0); } /* Disable SAS CTRL/C handling */ |
||
− | int chkabort(void) { return(0); } /* really */ |
||
− | #endif |
||
− | |||
− | struct IntuitionBase *IntuitionBase; |
||
− | |||
− | void main(void) |
||
{ |
{ |
||
+ | struct MsgPort *InputMP = IExec->AllocSysObjectTags(ASOT_PORT, TAG_END); |
||
− | struct IOStdReq *InputIO; /* I/O request block */ |
||
+ | if (InputMP != NULL) |
||
− | struct MsgPort *InputMP; /* Message port */ |
||
− | struct InputEvent *FakeEvent; /* InputEvent pointer */ |
||
− | struct IEPointerPixel *NeoPix; /* New mouse position pointer */ |
||
− | struct Screen *PubScreen; /* Screen pointer */ |
||
− | |||
− | if (InputMP = CreateMsgPort()) |
||
{ |
{ |
||
− | + | struct InputEvent *FakeEvent = IExec->AllocVecTags(sizeof(struct InputEvent), |
|
+ | AVT_Type, MEMF_SHARED, |
||
+ | TAG_END); |
||
+ | |||
+ | if (FakeEvent != NULL) |
||
{ |
{ |
||
− | + | struct IEPointerPixel *NeoPix = IExec->AllocVecTags(sizeof(struct IEPointerPixel), |
|
+ | AVT_Type, MEMF_SHARED, |
||
+ | TAG_END); |
||
+ | |||
+ | if (NeoPix != NULL) |
||
{ |
{ |
||
− | + | struct IOStdReq *InputIO = IExec->AllocSysObjectTags(ASOT_IOREQUEST, |
|
+ | ASOIOR_Size, sizeof(struct IOStdReq), |
||
+ | ASOIOR_ReplyPort, InputMP, |
||
+ | TAG_END); |
||
+ | |||
+ | if (InputIO != NULL) |
||
{ |
{ |
||
− | if (!OpenDevice( |
+ | if (!IExec->OpenDevice("input.device", NULL, (struct IORequest *)InputIO, NULL)) |
{ |
{ |
||
− | + | struct Library *IntuitionBase = IExec->OpenLibrary("intuition.library", 50); |
|
− | + | IIntuition = (struct IntuitionIFace*)IExec->GetInterface(IntuitionBase, "main", 1, NULL); |
|
+ | |||
− | OpenLibrary("intuition.library",36L)) |
||
+ | if (IIntuition != NULL) |
||
{ |
{ |
||
+ | struct Screen *PubScreen; |
||
/* Get pointer to screen and lock screen */ |
/* Get pointer to screen and lock screen */ |
||
− | if (PubScreen = (struct Screen *)LockPubScreen(NULL)) |
+ | if (PubScreen = (struct Screen *)IIntuition->LockPubScreen(NULL)) |
{ |
{ |
||
/* Set up IEPointerPixel fields */ |
/* Set up IEPointerPixel fields */ |
||
− | NeoPix- |
+ | NeoPix->iepp_Screen = (struct Screen *)PubScreen; /* WB screen */ |
− | NeoPix- |
+ | NeoPix->iepp_Position.X = 100; /* put pointer at x = 100 */ |
− | NeoPix- |
+ | NeoPix->iepp_Position.Y = 200; /* put pointer at y = 200 */ |
/* Set up InputEvent fields */ |
/* Set up InputEvent fields */ |
||
− | FakeEvent- |
+ | FakeEvent->ie_EventAddress = (APTR)NeoPix; /* IEPointerPixel */ |
− | FakeEvent- |
+ | FakeEvent->ie_NextEvent = NULL; |
− | FakeEvent- |
+ | FakeEvent->ie_Class = IECLASS_NEWPOINTERPOS; /* new mouse pos */ |
− | FakeEvent- |
+ | FakeEvent->ie_SubClass = IESUBCLASS_PIXEL; /* on pixel */ |
− | FakeEvent- |
+ | FakeEvent->ie_Code = IECODE_NOBUTTON; |
− | FakeEvent- |
+ | FakeEvent->ie_Qualifier = 0; /* absolute positioning */ |
− | InputIO- |
+ | InputIO->io_Data = (APTR)FakeEvent; /* InputEvent */ |
− | InputIO- |
+ | InputIO->io_Length = sizeof(struct InputEvent); |
− | InputIO- |
+ | InputIO->io_Command = IND_WRITEEVENT; |
− | DoIO((struct IORequest *)InputIO); |
+ | IExec->DoIO((struct IORequest *)InputIO); |
− | UnlockPubScreen(NULL,PubScreen); /* Unlock screen */ |
+ | IIntuition->UnlockPubScreen(NULL, PubScreen); /* Unlock screen */ |
} |
} |
||
else |
else |
||
− | + | IDOS->Printf("Could not get pointer to screen\n"); |
|
− | |||
− | CloseLibrary(IntuitionBase); /* Close intuition library */ |
||
} |
} |
||
else |
else |
||
− | + | IDOS->Printf("Error: Could not open intuition.library\n"); |
|
+ | |||
+ | IExec->DropInterface((struct Interface*)IIntuition); |
||
+ | IExec->CloseLibrary(IntuitionBase); |
||
− | CloseDevice((struct IORequest *)InputIO); |
+ | IExec->CloseDevice((struct IORequest *)InputIO); |
} |
} |
||
else |
else |
||
− | + | IDOS->Printf("Error: Could not open input.device\n"); |
|
− | + | IExec->FreeSysObject(ASOT_IOREQUEST, InputIO); |
|
} |
} |
||
else |
else |
||
− | + | IDOS->Printf("Error: Could not create IORequest\n"); |
|
− | + | IExec->FreeVec(NeoPix); |
|
} |
} |
||
else |
else |
||
− | + | IDOS->Printf("Error: Could not allocate memory for NeoPix\n"); |
|
− | + | IExec->FreeVec(FakeEvent); |
|
} |
} |
||
else |
else |
||
− | + | IDOS->Printf("Error: Could not allocate memory for FakeEvent\n"); |
|
− | + | IExec->FreeSysObject(ASOT_PORT, InputMP); |
|
} |
} |
||
else |
else |
||
− | + | IDOS->Printf("Error: Could not create message port\n"); |
|
+ | |||
+ | return 0; |
||
} |
} |
||
+ | </syntaxhighlight> |
||
− | </pre> |
||
== Setting the Key Repeat Threshold == |
== Setting the Key Repeat Threshold == |
||
Line 594: | Line 593: | ||
The key repeat threshold is the number of seconds and microseconds a user must hold down a key before it begins to repeat. This delay is normally set by the Preferences tool or by Intuition when it notices that the Preferences have been changed, but you can also do it directly through the input device. |
The key repeat threshold is the number of seconds and microseconds a user must hold down a key before it begins to repeat. This delay is normally set by the Preferences tool or by Intuition when it notices that the Preferences have been changed, but you can also do it directly through the input device. |
||
− | You set the key repeat threshold by passing a |
+ | You set the key repeat threshold by passing a TimeRequest with IND_SETTHRESH set in io_Command and the number of seconds to delay set in Seconds and the number of microseconds to delay set in Microseconds. |
+ | <syntaxhighlight> |
||
− | <pre>#include <devices/timer.h> |
||
+ | #include <devices/timer.h> |
||
− | struct |
+ | struct TimeRequest *InputTime; /* Initialize with AllocSysObject(ASOT_IOREQUEST) before using */ |
+ | |||
+ | InputTime->Time.Seconds = 1; /* 1 second */ |
||
+ | InputTime->Time.Microseconds = 500000; /* 500,000 microseconds */ |
||
+ | InputTime->Request.io_Command = IND_SETTHRESH; |
||
+ | IExec->DoIO((struct IORequest *)InputTime); |
||
+ | </syntaxhighlight> |
||
− | InputTime->tr_time.tv_secs=1; /* 1 second */ |
||
− | InputTime->tr_time.tv_micro=500000; /* 500,000 microseconds */ |
||
− | InputTime->tr_node.io_Command=IND_SETTHRESH; |
||
− | DoIO((struct IORequest *)InputTime);</pre> |
||
The code above will set the key repeat threshold to 1.5 seconds. |
The code above will set the key repeat threshold to 1.5 seconds. |
||
== Setting the Key Repeat Interval == |
== Setting the Key Repeat Interval == |
||
− | The key repeat interval is the time period, in seconds and microseconds, between key repeat events once the initial key repeat threshold has elapsed. (See |
+ | The key repeat interval is the time period, in seconds and microseconds, between key repeat events once the initial key repeat threshold has elapsed. (See "Setting the Key Repeat Threshold" above.) Like the key repeat threshold, this is normally issued by Intuition and preset by the Preferences tool. |
− | You set the key repeat interval by passing a |
+ | You set the key repeat interval by passing a TimeRequest with IND_SETPERIOD set in io_Command and the number of seconds set in Seconds and the number of microseconds set in Microseconds. |
+ | <syntaxhighlight> |
||
− | <pre>struct timerequest *InputTime; /* Initialize with CreateExtIO() before using */ |
||
+ | struct TimeRequest *InputTime; /* Initialize with AllocSysObject(ASOT_IOREQUEST) before using */ |
||
+ | |||
+ | InputTime->Time.Seconds = 0; |
||
+ | InputTime->Time.Microseconds = 12000; /* 0.012 seconds */ |
||
+ | InputTime->Request.io_Command = IND_SETPERIOD; |
||
+ | IExec->DoIO((struct IORequest *)InputTime); |
||
+ | </syntaxhighlight> |
||
− | InputTime->tr_time.tv_secs=0; |
||
− | InputTime->tr_time.tv_micro=12000; /* 0.012 seconds */ |
||
− | InputTime->tr_node.io_Command=IND_SETPERIOD; |
||
− | DoIO((struct IORequest *)InputTime);</pre> |
||
The code above sets the key repeat interval to 0.012 seconds. |
The code above sets the key repeat interval to 0.012 seconds. |
||
− | {{Note|title=The Right Tool For The Right Job|text=As previously stated, you ''must'' use a |
+ | {{Note|title=The Right Tool For The Right Job|text=As previously stated, you ''must'' use a TimeRequest structure with IND_SETTHRESH and IND_SETPERIOD.}} |
== Determining the Current Qualifiers == |
== Determining the Current Qualifiers == |
||
Line 628: | Line 633: | ||
PeekQualifier() returns what the input device ''considers'' to be the current qualifiers at the time PeekQualifier() is called (e.g., keyboard qualifiers and mouse buttons). This does not include any qualifiers which have been added, removed or otherwise modified by input handlers. |
PeekQualifier() returns what the input device ''considers'' to be the current qualifiers at the time PeekQualifier() is called (e.g., keyboard qualifiers and mouse buttons). This does not include any qualifiers which have been added, removed or otherwise modified by input handlers. |
||
− | In order to call the function, you must set a pointer to the input device base address |
+ | In order to call the function, you must set a pointer to the input device base address and get the interface. Once you set the interface pointer, you can call the function. ''You must open the device in order to access the device base address and interface.'' |
PeekQualifier() returns an unsigned word with bits set according to the qualifiers in effect at the ''time'' the function is called. It takes no parameters. |
PeekQualifier() returns an unsigned word with bits set according to the qualifiers in effect at the ''time'' the function is called. It takes no parameters. |
||
+ | <syntaxhighlight> |
||
− | <pre> |
||
+ | int main() |
||
− | struct Library *InputBase; /* Input device base address pointer */ |
||
− | |||
− | VOID main(VOID) |
||
{ |
{ |
||
struct IOStdReq *InputIO; /* I/O request block */ |
struct IOStdReq *InputIO; /* I/O request block */ |
||
− | + | uint16 Quals; /* qualifiers */ |
|
. |
. |
||
. |
. |
||
. |
. |
||
− | if (!OpenDevice( |
+ | if (!IExec->OpenDevice("input.device", NULL, (struct IORequest *)InputIO, NULL)) |
{ |
{ |
||
/* Set input device base address in InputBase */ |
/* Set input device base address in InputBase */ |
||
− | InputBase = (struct Library *)InputIO- |
+ | struct Library *InputBase = (struct Library *)InputIO->io_Device; |
+ | struct InputIFace *IInput = (struct InputIFace *)IExec->GetInterface(InputBase, "main", 1, NULL); |
||
/* Call the function */ |
/* Call the function */ |
||
− | Quals = PeekQualifier(); |
+ | Quals = IInput->PeekQualifier(); |
. |
. |
||
. |
. |
||
. |
. |
||
+ | IExec->DropInterface((struct Interface *)IInput); |
||
− | CloseDevice(InputIO); |
||
+ | IExec->CloseDevice(InputIO); |
||
} |
} |
||
} |
} |
||
+ | </syntaxhighlight> |
||
− | </pre> |
||
The qualifiers returned are listed in the table below. |
The qualifiers returned are listed in the table below. |
||
Line 694: | Line 699: | ||
== Example Input Device Program == |
== Example Input Device Program == |
||
+ | === Swap_Buttons.c === |
||
− | <pre>/* |
||
− | * Swap_Buttons.c |
||
− | * |
||
− | * This example swaps the function of the left and right mouse buttons |
||
− | * The C code is just the wrapper that installs and removes the |
||
− | * input.device handler that does the work. |
||
− | * |
||
− | * The handler is written in assembly code since it is important that |
||
− | * handlers be as fast as possible while processing the input events. |
||
− | * |
||
− | * Compile and link as follows: |
||
− | * |
||
− | * SAS C 5.10: |
||
− | * LC -b1 -cfirst -v -w Swap_Buttons.c |
||
− | * |
||
− | * Adapt assemble: |
||
− | * HX68 InputHandler.a to InputHandler.o |
||
− | * |
||
− | * BLink: |
||
− | * BLink from LIB:c.o+Swap_Buttons.o+InputHandler.o LIB LIB:lc.lib LIB:amiga.lib TO Swap_Buttons |
||
− | * |
||
− | */ |
||
+ | <syntaxhighlight> |
||
− | #include <exec/types.h> |
||
− | #include |
+ | #include <exec/types.h> |
− | #include |
+ | #include <exec/memory.h> |
− | #include |
+ | #include <exec/interrupts.h> |
− | #include |
+ | #include <devices/input.h> |
+ | #include <intuition/intuition.h> |
||
− | |||
− | #include <clib/exec_protos.h> |
||
− | #include <clib/alib_protos.h> |
||
− | #include <clib/intuition_protos.h> |
||
− | #include |
+ | #include <proto/dos.h> |
+ | #include <proto/exec.h> |
||
+ | #include <proto/intuition.h> |
||
+ | struct IntuitionIFace *IIntuition = NULL; |
||
− | #ifdef LATTICE |
||
− | int CXBRK(void) { return(0); } /* Disable SAS CTRL/C handling */ |
||
− | int chkabort(void) { return(0); } /* really */ |
||
− | #endif |
||
− | UBYTE NameString[]= |
+ | UBYTE NameString[] = "Swap Buttons"; |
struct NewWindow mywin={50,40,124,18,0,1,CLOSEWINDOW, |
struct NewWindow mywin={50,40,124,18,0,1,CLOSEWINDOW, |
||
Line 741: | Line 721: | ||
extern VOID ButtonSwap(); |
extern VOID ButtonSwap(); |
||
− | |||
− | extern struct IntuitionBase *IntuitionBase; |
||
/* |
/* |
||
Line 751: | Line 729: | ||
VOID WaitForUser(VOID) |
VOID WaitForUser(VOID) |
||
{ |
{ |
||
+ | struct Library *IntuitionBase = IExec->OpenLibrary("intuition.library", 50); |
||
− | struct Window *win; |
||
+ | IIntuition = (struct IntuitionIFace *)IExec->GetInterface(IntuitionBase, "main", 1, NULL); |
||
+ | if (IIntuition != NULL) |
||
− | if (IntuitionBase=(struct IntuitionBase *) |
||
− | OpenLibrary("intuition.library",33L)) |
||
{ |
{ |
||
− | + | struct Window *win = IIntuition->OpenWindow(&mywin); |
|
+ | if (win != NULL) |
||
{ |
{ |
||
− | WaitPort(win- |
+ | IExec->WaitPort(win->UserPort); |
− | ReplyMsg(GetMsg(win- |
+ | IExec->ReplyMsg(IExec->GetMsg(win->UserPort)); |
− | CloseWindow(win); |
+ | IIntuition->CloseWindow(win); |
} |
} |
||
− | CloseLibrary((struct Library *)IntuitionBase); |
||
} |
} |
||
+ | |||
+ | IExec->DropInterface((struct Interface *)IIntuition); |
||
+ | IExec->CloseLibrary(IntuitionBase); |
||
} |
} |
||
− | + | int main() |
|
{ |
{ |
||
+ | struct MsgPort *inputPort = IExec->AllocSysObjectTags(ASOT_PORT, TAG_END); |
||
− | struct IOStdReq *inputReqBlk; |
||
− | struct MsgPort *inputPort; |
||
− | struct Interrupt *inputHandler; |
||
− | if (inputPort= |
+ | if (inputPort != NULL) |
{ |
{ |
||
+ | struct Interrupt *inputHandler = IExec->AllocSysObjectTags(ASOT_INTERRUPT, |
||
− | if (inputHandler=AllocMem(sizeof(struct Interrupt), |
||
+ | ASOINTR_Code, ButtonSwap, |
||
− | MEMF_PUBLIC|MEMF_CLEAR)) |
||
+ | TAG_END); |
||
+ | |||
+ | if (inputHandler != NULL) |
||
{ |
{ |
||
− | + | struct IOStdReq *inputReqBlk = IExec->AllocSysObjectTags(ASOT_IOREQUEST, |
|
− | + | ASOIOR_Size, sizeof(struct IOStdReq), |
|
+ | ASOIOR_ReplyPort, inputPort, |
||
+ | TAG_END); |
||
+ | |||
+ | if (inputReqBlk != NULL) |
||
{ |
{ |
||
− | if (!OpenDevice( |
+ | if (!IExec->OpenDevice("input.device", NULL, |
− | (struct IORequest *)inputReqBlk,NULL)) |
+ | (struct IORequest *)inputReqBlk, NULL)) |
{ |
{ |
||
− | inputHandler- |
+ | inputHandler->is_Node.ln_Pri = 100; |
− | inputHandler- |
+ | inputHandler->is_Node.ln_Name = NameString; |
− | + | ||
− | + | inputReqBlk->io_Data = (APTR)inputHandler; |
|
− | inputReqBlk- |
+ | inputReqBlk->io_Command = IND_ADDHANDLER; |
− | + | IExec->DoIO((struct IORequest *)inputReqBlk); |
|
− | DoIO((struct IORequest *)inputReqBlk); |
||
WaitForUser(); |
WaitForUser(); |
||
− | inputReqBlk- |
+ | inputReqBlk->io_Data = (APTR)inputHandler; |
− | inputReqBlk- |
+ | inputReqBlk->io_Command = IND_REMHANDLER; |
− | DoIO((struct IORequest *)inputReqBlk); |
+ | IExec->DoIO((struct IORequest *)inputReqBlk); |
− | CloseDevice((struct IORequest *)inputReqBlk); |
+ | IExec->CloseDevice((struct IORequest *)inputReqBlk); |
} |
} |
||
else |
else |
||
− | + | IDOS->Printf("Error: Could not open input.device\n"); |
|
− | + | IExec->FreeSysObject(ASOT_IOREQUEST, inputReqBlk); |
|
} |
} |
||
else |
else |
||
− | + | IDOS->Printf("Error: Could not create I/O request\n"); |
|
+ | IExec->FreeSysObject(ASOT_INTERRUPT, inputHandler); |
||
− | FreeMem(inputHandler,sizeof(struct Interrupt)); |
||
} |
} |
||
else |
else |
||
− | + | IDOS->Printf("Error: Could not allocate interrupt\n"); |
|
− | + | IExec->FreeSysObject(ASOT_PORT, inputPort); |
|
} |
} |
||
else |
else |
||
− | + | IDOS->Printf("Error: Could not create message port\n"); |
|
+ | |||
− | }</pre> |
||
+ | return 0; |
||
− | <pre>**************************************************************************** |
||
+ | } |
||
− | * InputHandler.a |
||
+ | </syntaxhighlight> |
||
− | * |
||
+ | |||
− | * InputHandler that does a Left/Right mouse button swap... |
||
+ | === InputHandler.c === |
||
− | * |
||
+ | |||
− | * See Swap_Buttons.c for details on how to compile/assemble/link... |
||
+ | <pre> |
||
− | * |
||
+ | struct InputEvent *ButtonSwap(struct InputEvent *eventList, struct MsgPort *port) |
||
− | ************************************************************************ |
||
+ | { |
||
− | * |
||
+ | /* |
||
− | * Required includes... |
||
+ | * Since the event list could be a linked list, we start a loop |
||
− | * |
||
+ | * here to handle all of the events passed to us. |
||
− | INCDIR "include:" |
||
+ | */ |
||
− | INCLUDE "exec/types.i" |
||
+ | struct InputEvent *currEvent = eventList; |
||
− | INCLUDE "exec/io.i" |
||
+ | while (currEvent) /* Do some more. */ |
||
− | INCLUDE "devices/inputevent.i" |
||
+ | { |
||
− | * |
||
+ | /* |
||
− | ************************************************************************ |
||
+ | * Since we are changing left and right mouse buttons, we need to make |
||
− | * |
||
+ | * sure that we change the qualifiers on all of the messages. The |
||
− | * Make the entry point external... |
||
+ | * left and right mouse buttons are tracked in the message qualifiers |
||
− | * |
||
+ | * for use in such things as dragging. To make sure that we continue |
||
− | xdef _ButtonSwap |
||
+ | * to drag correctly, we change the qualifiers. |
||
− | * |
||
+ | * |
||
− | ************************************************************************ |
||
+ | * Save a copy |
||
− | * |
||
+ | */ |
||
− | * This is the input handler that will swap the |
||
+ | |||
− | * mouse buttons for left handed use. |
||
+ | uint16 qualActual = currEvent->ie_Qualifier, qualFinal = qualActual; |
||
− | * |
||
+ | int RBUTTON = 1<<IEQUALIFIER_RBUTTON; |
||
− | * The event list gets passed to you in a0. |
||
+ | int LBUTTON = 1<<IEQUALIFIER_LEFTBUTTON; |
||
− | * The is_Data field is passed to you in a1. |
||
+ | |||
− | * This example does not use the is_Data field... |
||
+ | if (RBUTTON & qualActual) /* Check for right */ |
||
− | * |
||
+ | { |
||
− | * On exit you must return the event list in d0. In this way |
||
+ | qualFinal |= LBUTTON; /* Set the left */ |
||
− | * you could add or remove items from the event list. |
||
+ | } |
||
− | * |
||
+ | else |
||
− | * The handler gets called here... |
||
+ | { |
||
− | * |
||
+ | qualFinal &= ~LBUTTON; /* Clear the left */ |
||
− | * |
||
+ | } |
||
− | _ButtonSwap: move.l a0,-(sp) ; Save the event list |
||
+ | |||
− | * |
||
+ | if (LBUTTON & qualActual) /* Check for left */ |
||
− | * Since the event list could be a linked list, we start a loop |
||
+ | { |
||
− | * here to handle all of the events passed to us. |
||
+ | qualFinal |= RBUTTON; /* Set the right */ |
||
− | * |
||
+ | } |
||
− | CheckLoop: move.w ie_Qualifier(a0),d1 ; Get qualifiers... |
||
+ | else |
||
− | move.w d1,d0 ; Two places... |
||
+ | { |
||
− | * |
||
+ | qualFinal &= ~RBUTTON; /* Set the right */ |
||
− | * Since we are changing left and right mouse buttons, we need to make |
||
+ | } |
||
− | * sure that we change the qualifiers on all of the messages. The |
||
+ | |||
− | * left and right mouse buttons are tracked in the message qualifiers |
||
+ | currEvent->ie_Qualifier = qualFinal; /* Save back */ |
||
− | * for use in such things as dragging. To make sure that we continue |
||
+ | |||
− | * to drag correctly, we change the qualifiers. |
||
+ | /* |
||
− | * |
||
+ | * The actual button up/down events are transmitted as the |
||
− | CheckRight: btst #IEQUALIFIERB_RBUTTON,d1 ; Check for right |
||
− | + | * code field in RAWMOUSE events. The code field must the be |
|
+ | * checked and modified when needed on RAWMOUSE events. If the |
||
− | bset #IEQUALIFIERB_LEFTBUTTON,d0 ; Set the left... |
||
− | + | * event is not a RAWMOUSE, we are done with it. |
|
+ | */ |
||
− | NoRight: bclr #IEQUALIFIERB_LEFTBUTTON,d0 ; Clear the left... |
||
+ | if (IECLASS_RAWMOUSE == currEvent->ie_Class) /* Check for mouse */ |
||
− | * |
||
+ | { |
||
− | CheckLeft: btst #IEQUALIFIERB_LEFTBUTTON,d1 ; Check for left |
||
− | + | /* Get code */ |
|
+ | /* Save a copy */ |
||
− | bset #IEQUALIFIERB_RBUTTON,d0 ; Set the right... |
||
− | + | /* We do not care if it is an UP or DOWN click */ |
|
+ | uint16 codeActual = currEvent->ie_Code, codeFinal = (IECODE_UP_PREFIX-0x1) & codeActual; |
||
− | NoLeft: bclr #IEQUALIFIERB_RBUTTON,d0 ; Clear the right... |
||
+ | |||
− | * |
||
+ | /* Check for left/right */ |
||
− | SaveQual: move.w d0,ie_Qualifier(a0) ; Save back... |
||
+ | if (IECODE_LBUTTON & codeActual || IECODE_RBUTTON & codeActual) |
||
− | * |
||
+ | { |
||
− | * The actual button up/down events are transmitted as the |
||
+ | /* If so, swap */ |
||
− | * code field in RAWMOUSE events. The code field must the be |
||
+ | /* Flip bottom bit */ |
||
− | * checked and modified when needed on RAWMOUSE events. If the |
||
+ | codeFinal = 0x1 ^ codeActual; |
||
− | * event is not a RAWMOUSE, we are done with it. |
||
+ | currEvent->ie_Code = codeFinal; |
||
− | * |
||
+ | } |
||
− | cmp.b #IECLASS_RAWMOUSE,ie_Class(a0) ; Check for mouse |
||
+ | } |
||
− | bne.s NextEvent ; If not, next... |
||
+ | |||
− | * |
||
+ | currEvent=currEvent->ie_NextEvent; /* Get next event */ |
||
− | move.w ie_Code(a0),d0 ; Get code... |
||
+ | } |
||
− | move.w d0,d1 ; Save... |
||
+ | return eventList; |
||
− | and.w #$7F,d0 ; Mask UP_PREFIX |
||
+ | } |
||
− | cmp.w #IECODE_LBUTTON,d0 ; Check for Left... |
||
+ | </pre> |
||
− | beq.s SwapThem ; If so, swap... |
||
+ | |||
− | cmp.w #IECODE_RBUTTON,d0 ; Check for Right... |
||
− | bne.s NextEvent ; If not, next... |
||
− | * |
||
− | SwapThem: eor.w #1,d1 ; Flip bottom bit |
||
− | move.w d1,ie_Code(a0) ; Save it... |
||
− | * |
||
− | * The event list is linked via a pointer to the next event |
||
− | * in the first element of the structure. That is why it is not |
||
− | * nessesary to use: move.l ie_NextEvent(a0),d0 |
||
− | * |
||
− | * The reason I move to d0 first is that this also checks for zero. |
||
− | * The last event in the list will have a NULL ie_NextEvent field. |
||
− | * This is NOT as standard EXEC list where the node after the last |
||
− | * node is NULL. Input events are single-linked for performance. |
||
− | * |
||
− | NextEvent: move.l (a0),d0 ; Get next event |
||
− | move.l d0,a0 ; into a0... |
||
− | bne.s CheckLoop ; Do some more. |
||
− | * |
||
− | * All done, just return the event list... (in d0) |
||
− | * |
||
− | move.l (sp)+,d0 ; Get event list back... |
||
− | rts ; return from handler...</pre> |
||
== Additional Information on the Input Device == |
== Additional Information on the Input Device == |
||
Latest revision as of 02:13, 4 November 2023
Code samples on this page are not yet updated to AmigaOS 4.x some of them may be obsolete or incompatible with AmigaOS 4.x. |
Contents
- 1 Input Device
- 2 Input Device Commands and Functions
- 3 Device Interface
- 4 Using the Mouse Port With the Input Device
- 5 Adding an Input Handler
- 6 Writing Events to the Input Device Stream
- 7 Setting the Key Repeat Threshold
- 8 Setting the Key Repeat Interval
- 9 Determining the Current Qualifiers
- 10 Input Device and Intuition
- 11 Example Input Device Program
- 12 Additional Information on the Input Device
Input Device
The input device is the central collection point for input events disseminated throughout the system. The best way to describe the input device is a manager of a stream with feeders. The input device itself and other modules such as the file system add events to the stream; so do input device “users”—programs or other devices that use parts of the stream or change it in some way. Feeders of the input device include the keyboard, timer and gameport devices. The keyboard, gameport, and timer devices are special cases in that the input device opens them and asks them for input. Users of the input device include Intuition and the console device.
Input Device Commands and Functions
Command | Command Operation |
---|---|
CMD_FLUSH | Purge all active and queued requests for the input device. |
CMD_RESET | Reset the input port to its initialized state. All active and queued I/O requests will be aborted. Restarts the device if it has been stopped. |
CMD_START | Restart the currently active input (if any) and resume queued I/O requests. |
CMD_STOP | Stop any currently active input and prevent queued I/O requests from starting. |
IND_ADDEVENT | Propagate an input event to all handlers, updating event qualifiers. |
IND_ADDHANDLER | Add an input-stream handler into the handler chain. |
IND_ADDNOTIFY | Add a hook which will be invoked whenever a configuration option changes. |
IND_GETHANDLERLIST | Obtain a pointer to the list of input handlers. |
IND_GETKDEVICE | Query the name and unit number of the device keyboard input events are collected from. |
IND_GETMDEVICE | Query the name and unit number of the device mouse input events are collected from. |
IND_GETPERIOD | Get the key repeat period. |
IND_GETTHRESH | Get the key repeat threshold. |
IND_IMMEDIATEADDNOTIFY | Add a hook which will be invoked whenever a configuration option changes. The hook will be invoked on the current configuration first. |
IND_REMHANDLER | Remove an input-stream handler from the handler chain. |
IND_REMOVENOTIFY | Remove a hook previously installed with the IND_ADDNOTIFY command. |
IND_SETKDEVICE | Choose the device to collect keyboard input events from. |
IND_SETMDEVICE | Choose the device to collect mouse input events from. |
IND_SETMPORT | Set the controller port to which the mouse is connected. |
IND_SETMTRIG | Set conditions that must be met by a mouse before a pending read request will be satisfied. |
IND_SETMTYPE | Set the type of device at the mouse port. |
IND_SETPERIOD | Set the period at which a repeating key repeats. |
IND_SETTHRESH | Set the repeating key hold-down time before repeat starts. |
IND_WRITEEVENT | Propagate an input event stream to all devices. |
PeekQualifier() | Return the input device’s current qualifiers. |
Device Interface
The input device operates like the other Amiga devices. To use it, you must first open the input 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.
A number of structures are used by the input device to do its processing. Some are used to pass commands and data to the device, some are used to describe input events like mouse movements and key depressions, and one structure is used to describe the environment for input event handlers.
The I/O request used by the input device for most commands is IOStdReq.
struct IOStdReq { struct Message io_Message; /* message reply port */ struct Device *io_Device; /* device node pointer */ struct Unit *io_Unit; /* unit */ UWORD io_Command; /* input device command */ UBYTE io_Flags; /* input device flags */ BYTE io_Error; /* error code */ ULONG io_Length; /* number of bytes to transfer */ APTR io_Data; /* pointer to data area */ };
See the include file exec/io.h for the complete structure definition.
Two of the input device commands—IND_SETTHRESH and IND_SETPERIOD—require a time specification and must use a TimeRequest structure instead of an IOStdReq.
struct TimeRequest { struct IORequest Request; struct TimeVal Time; };
As you can see, the TimeRequest structure includes an IORequest structure. The io_Command field of the IORequest indicates the command to the input device and the TimeVal structure sets the time values. See the include file devices/timer.h for the complete structure definition.
In Case You Feel Like Reinventing the Wheel... |
---|
You could define a “super-IORequest” structure for the input device which would combine the IOStdReq fields with the TimeVal structure of the TimeRequest structure. |
Opening the Input Device
Three primary steps are required to open the input device:
- Create a message port using AllocSysObject() and ASOT_PORT type. Reply messages from the device must be directed to a message port.
- Create an extended I/O request structure of type IOStdReq or TimeRequest using AllocSysObject() and ASOT_IOREQUEST type. The I/O request created by the AllocSysObject() function will be used to pass commands and data to the input device.
- Open the Input device. Call OpenDevice(), passing the I/O request.
struct MsgPort *InputMP = IExec->AllocSysObjectTags(ASOT_PORT, TAG_END); if (InputMP != NULL) { struct IOStdReq *InputIO = IExec->AllocSysObjectTags(ASOT_IOREQUEST, ASOIOR_Size, sizeof(struct IOStdReq), ASOIOR_ReplyPort, InputMP, TAG_END); if (ClipIO != NULL) { if (IExec->OpenDevice("input.device", 0, (struct IORequest *)InputIO, 0)) IDOS->Printf("input.device did not open\n");
The above code will work for all the input device commands except for the ones which require a time specification. For those, the code would look like this:
#include <devices/timer.h> struct MsgPort *InputMP = IExec->AllocSysObjectTags(ASOT_PORT, TAG_END); if (InputMP != NULL) { struct TimeRequest *InputIO = IExec->AllocSysObjectTags(ASOT_IOREQUEST, ASOIOR_Size, sizeof(struct TimeRequest), ASOIOR_ReplyPort, InputMP, TAG_END); if (ClipIO != NULL) { if (IExec->OpenDevice("input.device", 0, (struct IORequest *)InputIO, 0)) IDOS->Printf("input.device did not open\n");
Input Device Event Types
The input device is automatically opened by the console device when the system boots. When the input device is opened, a task named “input.device” is started. The input device task communicates directly with the keyboard device to obtain raw key events. It also communicates with the gameport device to obtain mouse button and mouse movement events and with the timer device to obtain time events. In addition to these events, you can add your own input events to the input device, to be fed to the handler chain (see below).
The keyboard device is accessible directly (see Keyboard Device). However, once the input.device task has started, you should not read events from the keyboard device directly, since doing so will deprive the input device of the events and confuse key repeating.
The gameport device has two units. As you view the Amiga, looking at the gameport connectors, the left connector is assigned as the primary mouse input for Intuition and contributes gameport input events to the input event stream.
The right connector is handled by the other gameport unit and is currently unassigned. While the input device task is running, that task expects to read the input from the left connector. Direct use of the gameport device is covered in Gameport Device.
The timer device is used to generate time events for the input device. It is also used to control key repeat rate and key repeat threshold. The timer device is a shared-access device and is described in Timer Device.
The device-specific commands are described below. First though, it may be helpful to consider the types of input events that the input device deals with. An input event is a data structure that describes the following:
- The class of the event — often describes the device that generated the event.
- The subclass of the event — space for more information if needed.
- The code — keycode if keyboard, button information if mouse, others.
- A qualifier such as “Alt key also down,”or “key repeat active”.
- A position field that contains a data address or a mouse position count.
- A time stamp, to determine the sequence in which the events occurred.
- A link-field by which input events are linked together.
- The class, subclass, code and qualifier of the previous down key.
The full definitions for each field can be found in the include file devices/inputevent.h. You can find more information about input events in Gameport Device and Console Device.
The various types of input events are listed below.
IECLASS_NULL | A NOP input event |
IECLASS_RAWKEY | A raw keycode from the keyboard device |
IECLASS_RAWMOUSE | The raw mouse report from the gameport device |
IECLASS_EVENT | A private console event |
IECLASS_POINTERPOS | A pointer position report |
IECLASS_TIMER | A timer event |
IECLASS_GADGETDOWN | Select button pressed down over a gadget (address in ie_EventAddress) |
IECLASS_GADGETUP | Select button released over the same gadget (address in ie_EventAddress) |
IECLASS_REQUESTER | Some requester activity has taken place. |
IECLASS_MENULIST | This is a menu number transmission (menu number is in ie_Code) |
IECLASS_CLOSEWINDOW | User has selected the active window’s Close Gadget |
IECLASS_SIZEWINDOW | This window has a new size |
IECLASS_REFRESHWINDOW | The window pointed to by ie_EventAddress needs to be refreshed |
IECLASS_NEWPREFS | New preferences are available |
IECLASS_DISKREMOVED | The disk has been removed |
IECLASS_DISKINSERTED | The disk has been inserted |
IECLASS_ACTIVEWINDOW | The window is about to be been made active |
IECLASS_INACTIVEWINDOW | The window is about to be made inactive |
IECLASS_NEWPOINTERPOS | Extended-function pointer position report |
IECLASS_MENUHELP | Help key report during Menu session |
IECLASS_CHANGEWINDOW | The Window has been modified with move, size, zoom, or change |
There is a difference between simply receiving an input event from a device and actually becoming a handler of an input event stream. A handler is a routine that is passed an input event list. It is up to the handler to decide if it can process the input events. If the handler does not recognize an event, it leaves it undisturbed in the event list.
It All Flows Downhill |
---|
Handlers can themselves generate new linked lists of events which can be passed down to lower priority handlers. |
The InputEvent structure is used by the input device to describe an input event such as a keypress or a mouse movement.
struct InputEvent { struct InputEvent *ie_NextEvent; /* the chronologically next event */ UBYTE ie_Class; /* the input event class */ UBYTE ie_SubClass; /* optional subclass of the class */ UWORD ie_Code; /* the input event code */ UWORD ie_Qualifier; /* qualifiers in effect for the event*/ union { struct { WORD ie_x; /* the pointer position for the event*/ WORD ie_y; } ie_xy; APTR ie_addr; /* the event address */ struct { UBYTE ie_prev1DownCode; /* previous down keys for dead */ UBYTE ie_prev1DownQual; /* key translation: the ie_Code */ UBYTE ie_prev2DownCode; /* & low byte of ie_Qualifier for */ UBYTE ie_prev2DownQual; /* last & second last down keys */ } ie_dead; } ie_position; struct timeval ie_TimeStamp; /* the system tick at the event */ };
The IEPointerPixel and IEPointerTablet structures are used to set the mouse position with the IECLASS_NEWPOINTERPOS input event class.
struct IEPointerPixel { struct Screen *iepp_Screen; /* pointer to an open screen */ struct { /* pixel coordinates in iepp_Screen */ WORD X; WORD Y; } iepp_Position; }; struct IEPointerTablet { struct { UWORD X; UWORD Y; } iept_Range; /* 0 is min, these are max */ struct { UWORD X; UWORD Y; } iept_Value; /* between 0 and iept_Range */ WORD iept_Pressure; /* -128 to 127 (unused, set to 0) */ };
See the include file devices/inputevent.h for the complete structure definitions.
For input device handler installation, the Interrupt structure is used.
struct Interrupt { struct Node is_Node; APTR is_Data; /* server data segment */ VOID (*is_Code)(); /* server code entry */ };
See the include file exec/interrupts.h for the complete structure definition.
Closing the Input 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(InputIO))) { IExec->AbortIO(InputIO); /* Ask device to abort request, if pending */ } IExec->WaitIO(InputIO); /* Wait for abort, then clean up */ IExec->CloseDevice((struct IORequest *)InputIO);
Using the Mouse Port With the Input Device
To get mouse port information you must first set the current mouse port by passing an IOStdReq to the device with IND_SETMPORT set in io_Command and a pointer to a byte set in io_Data. If the byte is set to 0 the left controller port will be used as the current mouse port; if it is set to 1, the right controller port will be used.
int8 port = 1; /* set mouse port to right controller */ InputIO->io_Data = &port; InputIO->io_Flags = IOF_QUICK; InputIO->io_Length = 1; InputIO->io_Command = IND_SETMPORT; IExec->BeginIO((struct IORequest *)InputIO); if (InputIO->io_Error) IDOS->Printf("\nSETMPORT failed %ld\n", InputIO->io_Error);
Put That Back! |
---|
The default mouse port is the left controller. Don’t forget to set the mouse port back to the left controller before exiting if you change it to the right controller during your application. |
Setting the Conditions for a Mouse Port Report
You set the conditions for a mouse port report by passing an IOStdReq to the device with IND_SETMTRIG set in io_Command, the address of a GamePortTrigger structure set in io_Data and the length of the structure set in io_Length.
struct GamePortTrigger InputTR; InputIO->io_Data = (APTR)InputTR; /* set trigger conditions */ InputIO->io_Command = IND_SETMTRIG; /* from InputTR */ InputIO->io_Length = sizeof(struct GamePortTrigger); IExec->DoIO(InputIO);
The information needed for mouse port report setting is contained in a GamePortTrigger data structure which is defined in the include file devices/gameport.h.
struct GamePortTrigger { UWORD gpt_Keys; /* key transition triggers */ UWORD gpt_Timeout; /* time trigger (vertical blank units) */ UWORD gpt_XDelta; /* X distance trigger */ UWORD gpt_YDelta; /* Y distance trigger */ };
See Gameport Device for a full description of setting mouse port trigger conditions.
Adding an Input Handler
You add an input-stream handler to the input chain by passing an IOStdReq to the device with IND_ADDHANDLER set in io_Command and a pointer to an Interrupt structure set in io_Data.
struct Interrupt *InputHandler; struct IOStdReq *InputIO InputHandler->is_Code = ButtonSwap; /* Address of code */ InputHandler->is_Data = NULL; /* User Value passed in A1 */ InputHandler->is_Node.ln_Pri = 100; /* Priority in food chain */ InputHandler->is_Node.ln_Name = NameString; /* Name of handler */ InputIO->io_Data = (APTR)inputHandler; /* Point to the structure */ InputIO->io_Command = IND_ADDHANDLER; /* Set command ... */ IExec->DoIO((struct IORequest *)InputIO); /* DoIO( ) the command */
Intuition is one of the input device handlers and normally distributes most of the input events.
Intuition inserts itself at priority position 50. The console device sits at priority position 0. You can choose the position in the chain at which your handler will be inserted by setting the priority field in the list-node part of the interrupt data structure you pass to this routine.
Speed Saves |
---|
Any processing time expended by a handler subtracts from the time available before the next event happens. Therefore, handlers for the input stream must be fast. |
Rules for Input Device Handlers
The following rules should be followed when you are designing an input handler:
- If an input handler is capable of processing a specific kind of an input event and that event has no links (ie_NextEvent = 0), the handler can end the handler chain by returning a NULL (0) value.
- If there are multiple events linked together, the handler is free to unlink an event from the input event chain, thereby passing a shorter list of events to subsequent handlers. The starting address of the modified list is the return value.
- If a handler wishes to add new events to the chain that it passes to a lower-priority handler, it may initialize memory to contain the new event or event chain. The handler, when it again gets control on the next round of event handling, should assume nothing about the current contents of the memory blocks attached to the event chain. Lower priority handlers may have modified the memory as they handled their part of the event. The handler that allocates the memory for this purpose should keep track of the starting address and the size of this memory chunk so that the memory can be returned to the free memory list when it is no longer needed.
Your handler routine should be structured similar to the following pseudo-language statement:
newEventChain = yourHandlerCode(oldEventChain, yourHandlerData);
where:
- yourHandlerCode
- is the entry point to your routine.
- oldEventChain
- is the starting address for the current chain of input events.
- yourHandlerData
- is a user-definable value, usually a pointer to some data structure your handler requires.
- newEventChain
- is the starting address of an event chain which you are passing to the next handler, if any.
When your handler code is called, the event chain and the handler data is passed in. When your code returns, it should return the pointer to the event chain. If all of the events were removed by the routine, return NULL. A NULL (0) value terminates the handling thus freeing more CPU resources.
Memory that you use to describe a new input event that you have added to the event chain is available for reuse or deallocation when the handler is called again or after the IND_REMHANDLER command for the handler is complete. There is no guarantee that any field in the event is unchanged since a handler may change any field of an event that comes through the food chain.
Because IND_ADDHANDLER installs a handler in any position in the handler chain, it can, for example, ignore specific types of input events as well as act upon and modify existing streams of input. It can even create new input events for Intuition or other programs to interpret.
Removing an Input Handler
You remove a handler from the handler chain by passing an IOStdReq to the device IND_REMHANDLER set in io_Command and a pointer to the Interrupt structure used to add the handler.
struct Interrupt *InputHandler; struct IOStdReq *InputIO; InputIO->io_Data = (APTR)InputHandler; /* Which handler to REM */ InputIO->io_Command = IND_REMHANDLER; /* The REM command */ IExec->DoIO((struct IORequest *)InputIO); /* Send the command */
Writing Events to the Input Device Stream
Typically, input events are internally generated by the timer device, keyboard device, and input device.
An application can also generate an input event by setting the appropriate fields for the event in an InputEvent structure and sending it to the input device. It will then be treated as any other event and passed through to the input handler chain. However, I/O requests for IND_WRITEVENT cannot be made from interrupt code.
You generate an input event by passing an IOStdReq to the device with IND_WRITEEVENT set in io_Command, a pointer to an InputEvent structure set in io_Data and the length of the structure set in io_Length.
struct InputEvent *FakeEvent; struct IOStdReq *InputIO; InputIO->io_Data = (APTR)FakeEvent; InputIO->io_Length = sizeof(struct InputEvent); InputIO->io_Command = IND_WRITEEVENT; IExec->DoIO((struct IORequest *)InputIO);
You Know What Happens When You Assume |
---|
This command propagates the input event through the handler chain. The handlers may link other events onto the end of this event or modify the contents of the data structure you constructed in any way they wish. Therefore, do not assume any of the data will be the same from event to event. |
Setting the Position of the Mouse
One use of writing input events to the input device is to set the position of the mouse pointer. The mouse pointer can be positioned by using the input classes IECLASS_POINTERPOS and IECLASS_NEWPOINTERPOS.
There are two ways to set the position of the mouse pointer using the input class IECLASS_POINTERPOS:
- At an absolute position on the current screen.
- At a position relative to the current mouse pointer position on the current screen.
In both cases, you set the Class field of the InputEvent structure to IECLASS_POINTERPOS, ie_X with the new x-coordinate and ie_Y with the new y-coordinate. Absolute positioning is done by setting ie_Qualifier to 0 and relative positioning is done by setting ie_Qualifier to IEQUALIFIER_RELATIVEMOUSE.
Once the proper values are set, pass an IOStdReq to the input device with a pointer to the InputEvent structure set in io_Data and io_Command set to IND_WRITEEVENT.
There are three ways to set the mouse pointer position using IECLASS_NEWPOINTERPOS:
- At an absolute x-y coordinate on a screen—you specify the exact location of the pointer and which screen.
- At an relative x-y coordinate—you specify where it will go in relation to the current pointer position and which screen.
- At a normalized position on a tablet device—you specify the maximum x-value and y-value of the tablet and an x-y coordinate between them and the input device will normalize it to fit.
The basic steps required are the same for all three methods.
- Get a pointer to the screen where you want to position the pointer. This is not necessary for the tablet device.
- Set up a structure to indicate the new position of the pointer.
For absolute and relative positioning, you set up an IEPointerPixel structure with iepp_Position.X set to the new x-coordinate, iepp_Position.Y set to the new y-coordinate and iepp_Screen set to the screen pointer. You set up an InputEvent structure with ie_SubClass set to IESUBCLASS_PIXEL, a pointer to the IEPointerPixel structure set in ie_EventAddress, IECLASS_NEWPOINTERPOS set in Class, and ie_Qualifier set to either IEQUALIFIER_RELATIVEMOUSE for relative positioning or 0 for absolute positioning.
For tablet positioning, you set up an IEPointerTablet structure with iept_Range.X set to the maximum x-coordinate and iept_Range.Y set to the maximum y-coordinate, and iept_Value.X set to the new x-coordinate and iept_Value.Y set to the new y-coordinate. You set up an InputEvent structure with a pointer to the IEPointerTablet structure set in ie_EventAddress, ie_SubClass to IESUBCLASS_TABLET and Class set to IECLASS_NEWPOINTERPOS.
Finally, for all three methods, pass an IOStdReq to the device with a pointer to the InputEvent structure set in io_Data and io_Command set to IND_WRITEEVENT.
The following example sets the mouse pointer at an absolute position on a public screen using IECLASS_NEWPOINTERPOS.
/* * Set_Mouse.c * * This example sets the mouse at x=100 and y=200 * * Run from CLI only */ #include <exec/types.h> #include <exec/memory.h> #include <devices/input.h> #include <devices/inputevent.h> #include <intuition/screens.h> #include <proto/dos.h> #include <proto/exec.h> #include <proto/intuition.h> struct IntuitionIFace *IIntuition = NULL; int main() { struct MsgPort *InputMP = IExec->AllocSysObjectTags(ASOT_PORT, TAG_END); if (InputMP != NULL) { struct InputEvent *FakeEvent = IExec->AllocVecTags(sizeof(struct InputEvent), AVT_Type, MEMF_SHARED, TAG_END); if (FakeEvent != NULL) { struct IEPointerPixel *NeoPix = IExec->AllocVecTags(sizeof(struct IEPointerPixel), AVT_Type, MEMF_SHARED, TAG_END); if (NeoPix != NULL) { struct IOStdReq *InputIO = IExec->AllocSysObjectTags(ASOT_IOREQUEST, ASOIOR_Size, sizeof(struct IOStdReq), ASOIOR_ReplyPort, InputMP, TAG_END); if (InputIO != NULL) { if (!IExec->OpenDevice("input.device", NULL, (struct IORequest *)InputIO, NULL)) { struct Library *IntuitionBase = IExec->OpenLibrary("intuition.library", 50); IIntuition = (struct IntuitionIFace*)IExec->GetInterface(IntuitionBase, "main", 1, NULL); if (IIntuition != NULL) { struct Screen *PubScreen; /* Get pointer to screen and lock screen */ if (PubScreen = (struct Screen *)IIntuition->LockPubScreen(NULL)) { /* Set up IEPointerPixel fields */ NeoPix->iepp_Screen = (struct Screen *)PubScreen; /* WB screen */ NeoPix->iepp_Position.X = 100; /* put pointer at x = 100 */ NeoPix->iepp_Position.Y = 200; /* put pointer at y = 200 */ /* Set up InputEvent fields */ FakeEvent->ie_EventAddress = (APTR)NeoPix; /* IEPointerPixel */ FakeEvent->ie_NextEvent = NULL; FakeEvent->ie_Class = IECLASS_NEWPOINTERPOS; /* new mouse pos */ FakeEvent->ie_SubClass = IESUBCLASS_PIXEL; /* on pixel */ FakeEvent->ie_Code = IECODE_NOBUTTON; FakeEvent->ie_Qualifier = 0; /* absolute positioning */ InputIO->io_Data = (APTR)FakeEvent; /* InputEvent */ InputIO->io_Length = sizeof(struct InputEvent); InputIO->io_Command = IND_WRITEEVENT; IExec->DoIO((struct IORequest *)InputIO); IIntuition->UnlockPubScreen(NULL, PubScreen); /* Unlock screen */ } else IDOS->Printf("Could not get pointer to screen\n"); } else IDOS->Printf("Error: Could not open intuition.library\n"); IExec->DropInterface((struct Interface*)IIntuition); IExec->CloseLibrary(IntuitionBase); IExec->CloseDevice((struct IORequest *)InputIO); } else IDOS->Printf("Error: Could not open input.device\n"); IExec->FreeSysObject(ASOT_IOREQUEST, InputIO); } else IDOS->Printf("Error: Could not create IORequest\n"); IExec->FreeVec(NeoPix); } else IDOS->Printf("Error: Could not allocate memory for NeoPix\n"); IExec->FreeVec(FakeEvent); } else IDOS->Printf("Error: Could not allocate memory for FakeEvent\n"); IExec->FreeSysObject(ASOT_PORT, InputMP); } else IDOS->Printf("Error: Could not create message port\n"); return 0; }
Setting the Key Repeat Threshold
The key repeat threshold is the number of seconds and microseconds a user must hold down a key before it begins to repeat. This delay is normally set by the Preferences tool or by Intuition when it notices that the Preferences have been changed, but you can also do it directly through the input device.
You set the key repeat threshold by passing a TimeRequest with IND_SETTHRESH set in io_Command and the number of seconds to delay set in Seconds and the number of microseconds to delay set in Microseconds.
#include <devices/timer.h> struct TimeRequest *InputTime; /* Initialize with AllocSysObject(ASOT_IOREQUEST) before using */ InputTime->Time.Seconds = 1; /* 1 second */ InputTime->Time.Microseconds = 500000; /* 500,000 microseconds */ InputTime->Request.io_Command = IND_SETTHRESH; IExec->DoIO((struct IORequest *)InputTime);
The code above will set the key repeat threshold to 1.5 seconds.
Setting the Key Repeat Interval
The key repeat interval is the time period, in seconds and microseconds, between key repeat events once the initial key repeat threshold has elapsed. (See "Setting the Key Repeat Threshold" above.) Like the key repeat threshold, this is normally issued by Intuition and preset by the Preferences tool.
You set the key repeat interval by passing a TimeRequest with IND_SETPERIOD set in io_Command and the number of seconds set in Seconds and the number of microseconds set in Microseconds.
struct TimeRequest *InputTime; /* Initialize with AllocSysObject(ASOT_IOREQUEST) before using */ InputTime->Time.Seconds = 0; InputTime->Time.Microseconds = 12000; /* 0.012 seconds */ InputTime->Request.io_Command = IND_SETPERIOD; IExec->DoIO((struct IORequest *)InputTime);
The code above sets the key repeat interval to 0.012 seconds.
The Right Tool For The Right Job |
---|
As previously stated, you must use a TimeRequest structure with IND_SETTHRESH and IND_SETPERIOD. |
Determining the Current Qualifiers
Some applications need to know whether the user is holding down a qualifier key or a mouse button during an operation. To determine the current qualifiers, you call the input device function PeekQualifier().
PeekQualifier() returns what the input device considers to be the current qualifiers at the time PeekQualifier() is called (e.g., keyboard qualifiers and mouse buttons). This does not include any qualifiers which have been added, removed or otherwise modified by input handlers.
In order to call the function, you must set a pointer to the input device base address and get the interface. Once you set the interface pointer, you can call the function. You must open the device in order to access the device base address and interface.
PeekQualifier() returns an unsigned word with bits set according to the qualifiers in effect at the time the function is called. It takes no parameters.
int main() { struct IOStdReq *InputIO; /* I/O request block */ uint16 Quals; /* qualifiers */ . . . if (!IExec->OpenDevice("input.device", NULL, (struct IORequest *)InputIO, NULL)) { /* Set input device base address in InputBase */ struct Library *InputBase = (struct Library *)InputIO->io_Device; struct InputIFace *IInput = (struct InputIFace *)IExec->GetInterface(InputBase, "main", 1, NULL); /* Call the function */ Quals = IInput->PeekQualifier(); . . . IExec->DropInterface((struct Interface *)IInput); IExec->CloseDevice(InputIO); } }
The qualifiers returned are listed in the table below.
Bit | Qualifier | Key or Button |
---|---|---|
0 | IEQUALIFIER_LSHIFT | Left Shift |
1 | IEQUALIFIER_RSHIFT | Right Shift |
2 | IEQUALIFIER_CAPSLOCK | Caps Lock |
3 | IEQUALIFIER_CONTROL | Control |
4 | IEQUALIFIER_LALT | Left Alt |
5 | IEQUALIFIER_RALT | Right Alt |
6 | IEQUALIFIER_LCOMMAND | Left-Amiga |
7 | IEQUALIFIER_RCOMMAND | Right-Amiga |
12 | IEQUALIFIER_MIDBUTTON | Middle Mouse |
13 | IEQUALIFIER_RBUTTON | Right Mouse |
14 | IEQUALIFIER_LEFTBUTTON | Left Mouse |
Input Device and Intuition
There are several ways to receive information from the various devices that are part of the input device. The first way is to communicate directly with the device. This method is not recommended while the input device task is running – which is most of the time. The second way is to become a handler for the stream of events which the input device produces. That method is shown above.
The third method of getting input from the input device is to retrieve the data from the console device or from the IDCMP (Intuition Direct Communications Message Port). These are the preferred methods for applications in a multitasking environment because each application can receive juts its own input (i.e., only the input which occurs when one of its window is active). See the Intuition Input and Output Methods for more information on IDCMP messages. See Console Device for more information on console device I/O.
Example Input Device Program
Swap_Buttons.c
#include <exec/types.h> #include <exec/memory.h> #include <exec/interrupts.h> #include <devices/input.h> #include <intuition/intuition.h> #include <proto/dos.h> #include <proto/exec.h> #include <proto/intuition.h> struct IntuitionIFace *IIntuition = NULL; UBYTE NameString[] = "Swap Buttons"; struct NewWindow mywin={50,40,124,18,0,1,CLOSEWINDOW, WINDOWDRAG|WINDOWCLOSE|SIMPLE_REFRESH|NOCAREREFRESH, NULL,NULL,NameString,NULL,NULL,0,0,0,0,WBENCHSCREEN}; extern VOID ButtonSwap(); /* * This routine opens a window and waits for the one event that * can happen (CLOSEWINDOW) This is just to let the user play with * the swapped buttons and then close the program... */ VOID WaitForUser(VOID) { struct Library *IntuitionBase = IExec->OpenLibrary("intuition.library", 50); IIntuition = (struct IntuitionIFace *)IExec->GetInterface(IntuitionBase, "main", 1, NULL); if (IIntuition != NULL) { struct Window *win = IIntuition->OpenWindow(&mywin); if (win != NULL) { IExec->WaitPort(win->UserPort); IExec->ReplyMsg(IExec->GetMsg(win->UserPort)); IIntuition->CloseWindow(win); } } IExec->DropInterface((struct Interface *)IIntuition); IExec->CloseLibrary(IntuitionBase); } int main() { struct MsgPort *inputPort = IExec->AllocSysObjectTags(ASOT_PORT, TAG_END); if (inputPort != NULL) { struct Interrupt *inputHandler = IExec->AllocSysObjectTags(ASOT_INTERRUPT, ASOINTR_Code, ButtonSwap, TAG_END); if (inputHandler != NULL) { struct IOStdReq *inputReqBlk = IExec->AllocSysObjectTags(ASOT_IOREQUEST, ASOIOR_Size, sizeof(struct IOStdReq), ASOIOR_ReplyPort, inputPort, TAG_END); if (inputReqBlk != NULL) { if (!IExec->OpenDevice("input.device", NULL, (struct IORequest *)inputReqBlk, NULL)) { inputHandler->is_Node.ln_Pri = 100; inputHandler->is_Node.ln_Name = NameString; inputReqBlk->io_Data = (APTR)inputHandler; inputReqBlk->io_Command = IND_ADDHANDLER; IExec->DoIO((struct IORequest *)inputReqBlk); WaitForUser(); inputReqBlk->io_Data = (APTR)inputHandler; inputReqBlk->io_Command = IND_REMHANDLER; IExec->DoIO((struct IORequest *)inputReqBlk); IExec->CloseDevice((struct IORequest *)inputReqBlk); } else IDOS->Printf("Error: Could not open input.device\n"); IExec->FreeSysObject(ASOT_IOREQUEST, inputReqBlk); } else IDOS->Printf("Error: Could not create I/O request\n"); IExec->FreeSysObject(ASOT_INTERRUPT, inputHandler); } else IDOS->Printf("Error: Could not allocate interrupt\n"); IExec->FreeSysObject(ASOT_PORT, inputPort); } else IDOS->Printf("Error: Could not create message port\n"); return 0; }
InputHandler.c
struct InputEvent *ButtonSwap(struct InputEvent *eventList, struct MsgPort *port) { /* * Since the event list could be a linked list, we start a loop * here to handle all of the events passed to us. */ struct InputEvent *currEvent = eventList; while (currEvent) /* Do some more. */ { /* * Since we are changing left and right mouse buttons, we need to make * sure that we change the qualifiers on all of the messages. The * left and right mouse buttons are tracked in the message qualifiers * for use in such things as dragging. To make sure that we continue * to drag correctly, we change the qualifiers. * * Save a copy */ uint16 qualActual = currEvent->ie_Qualifier, qualFinal = qualActual; int RBUTTON = 1<<IEQUALIFIER_RBUTTON; int LBUTTON = 1<<IEQUALIFIER_LEFTBUTTON; if (RBUTTON & qualActual) /* Check for right */ { qualFinal |= LBUTTON; /* Set the left */ } else { qualFinal &= ~LBUTTON; /* Clear the left */ } if (LBUTTON & qualActual) /* Check for left */ { qualFinal |= RBUTTON; /* Set the right */ } else { qualFinal &= ~RBUTTON; /* Set the right */ } currEvent->ie_Qualifier = qualFinal; /* Save back */ /* * The actual button up/down events are transmitted as the * code field in RAWMOUSE events. The code field must the be * checked and modified when needed on RAWMOUSE events. If the * event is not a RAWMOUSE, we are done with it. */ if (IECLASS_RAWMOUSE == currEvent->ie_Class) /* Check for mouse */ { /* Get code */ /* Save a copy */ /* We do not care if it is an UP or DOWN click */ uint16 codeActual = currEvent->ie_Code, codeFinal = (IECODE_UP_PREFIX-0x1) & codeActual; /* Check for left/right */ if (IECODE_LBUTTON & codeActual || IECODE_RBUTTON & codeActual) { /* If so, swap */ /* Flip bottom bit */ codeFinal = 0x1 ^ codeActual; currEvent->ie_Code = codeFinal; } } currEvent=currEvent->ie_NextEvent; /* Get next event */ } return eventList; }
Additional Information on the Input Device
Additional programming information on the input device can be found in the include files and the autodocs for the input device. Both are contained in the SDK.
Includes |
---|
devices/input.h |
devices/inputevent.h |
AutoDocs |
---|
input.doc |