Copyright (c) Hyperion Entertainment and contributors.

Difference between revisions of "Input Device"

From AmigaOS Documentation Wiki
Jump to navigation Jump to search
(Created page with "= 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 ...")
 
m
 
(53 intermediate revisions by 4 users not shown)
Line 1: Line 1:
  +
[[Category:Devices|Input]]{{CodeReview}}
= 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.
 
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.
 
|ll|
 
 
2c'''New Features for Version 2.0'''<br />
 
'''Feature''' &amp; '''Description'''<br />
 
IECLASS_NEWPOINTERPOS &amp; Input Event Class<br />
 
IECLASS_MENUHELP &amp; Input Event Class<br />
 
IECLASS_CHANGEWINDOW &amp; Input Event Class<br />
 
IESUBCLASS_COMPATIBLE &amp; Input Event SubClass<br />
 
IESUBCLASS_PIXEL &amp; Input Event SubClass<br />
 
IESUBCLASS_TABLET &amp; Input Event SubClass<br />
 
PeekQualifier() &amp; Function<br />
 
 
 
<sub>b</sub>oxCompatibility Warning:The new features for the 2.0 input device are not backwards compatible.
 
   
 
== Input Device Commands and Functions ==
 
== Input Device Commands and Functions ==
   
  +
{| class="wikitable"
<table>
 
  +
! Command
<tbody>
 
  +
! Command Operation
<tr class="odd">
 
  +
|-
<td align="left">'''Command'''</td>
 
  +
| CMD_FLUSH || Purge all active and queued requests for the input device.
<td align="left">'''Command Operation'''</td>
 
  +
|-
</tr>
 
  +
| 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.
<tr class="even">
 
  +
|-
<td align="left">CMD_FLUSH</td>
 
  +
| CMD_START || Restart the currently active input (if any) and resume queued I/O requests.
<td align="left">8.8cmPurge all active and queued requests for the input device.</td>
 
  +
|-
</tr>
 
  +
| CMD_STOP || Stop any currently active input and prevent queued I/O requests from starting.
<tr class="odd">
 
  +
|-
<td align="left">CMD_RESET</td>
 
  +
| IND_ADDEVENT || Propagate an input event to all handlers, updating event qualifiers.
<td align="left">8.8cmReset 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.</td>
 
  +
|-
</tr>
 
  +
| IND_ADDHANDLER || Add an input-stream handler into the handler chain.
<tr class="even">
 
  +
|-
<td align="left">CMD_START</td>
 
  +
| IND_ADDNOTIFY || Add a hook which will be invoked whenever a configuration option changes.
<td align="left">8.8cmRestart the currently active input (if any) and resume queued I/O requests.</td>
 
  +
|-
</tr>
 
  +
| IND_GETHANDLERLIST || Obtain a pointer to the list of input handlers.
<tr class="odd">
 
  +
|-
<td align="left">CMD_STOP</td>
 
  +
| IND_GETKDEVICE || Query the name and unit number of the device keyboard input events are collected from.
<td align="left">8.8cmStop any currently active input and prevent queued I/O requests from starting.</td>
 
  +
|-
</tr>
 
  +
| IND_GETMDEVICE || Query the name and unit number of the device mouse input events are collected from.
<tr class="even">
 
  +
|-
<td align="left">IND_ADDHANDLER</td>
 
  +
| IND_GETPERIOD || Get the key repeat period.
<td align="left">8.8cmAdd an input-stream handler into the handler chain.</td>
 
  +
|-
</tr>
 
  +
| IND_GETTHRESH || Get the key repeat threshold.
<tr class="odd">
 
  +
|-
<td align="left">IND_REMHANDLER</td>
 
  +
| IND_IMMEDIATEADDNOTIFY || Add a hook which will be invoked whenever a configuration option changes. The hook will be invoked on the current configuration first.
<td align="left">8.8cmRemove an input-stream handler from the handler chain.</td>
 
  +
|-
</tr>
 
  +
| IND_REMHANDLER || Remove an input-stream handler from the handler chain.
<tr class="even">
 
  +
|-
<td align="left">IND_SETMPORT</td>
 
  +
| IND_REMOVENOTIFY || Remove a hook previously installed with the IND_ADDNOTIFY command.
<td align="left">8.8cmSet the controller port to which the mouse is connected.</td>
 
  +
|-
</tr>
 
  +
| IND_SETKDEVICE || Choose the device to collect keyboard input events from.
<tr class="odd">
 
  +
|-
<td align="left">IND_SETMTRIG</td>
 
  +
| IND_SETMDEVICE || Choose the device to collect mouse input events from.
<td align="left">8.8cmSet conditions that must be met by a mouse before a pending read request will be satisfied.</td>
 
  +
|-
</tr>
 
  +
| IND_SETMPORT || Set the controller port to which the mouse is connected.
<tr class="even">
 
  +
|-
<td align="left">IND_SETMTYPE</td>
 
  +
| IND_SETMTRIG || Set conditions that must be met by a mouse before a pending read request will be satisfied.
<td align="left">8.8cmSet the type of device at the mouse port.</td>
 
  +
|-
</tr>
 
  +
| IND_SETMTYPE || Set the type of device at the mouse port.
<tr class="odd">
 
  +
|-
<td align="left">IND_SETPERIOD</td>
 
<td align="left">8.8cmSet the period at which a repeating key repeats.</td>
+
| IND_SETPERIOD || Set the period at which a repeating key repeats.
  +
|-
</tr>
 
  +
| IND_SETTHRESH || Set the repeating key hold-down time before repeat starts.
<tr class="even">
 
  +
|-
<td align="left">IND_SETTHRESH</td>
 
  +
| IND_WRITEEVENT || Propagate an input event stream to all devices.
<td align="left">8.8cmSet the repeating key hold-down time before repeat starts.</td>
 
  +
|}
</tr>
 
<tr class="odd">
 
<td align="left">IND_WRITEEVENT</td>
 
<td align="left">8.8cmPropagate an input event stream to all devices.</td>
 
</tr>
 
</tbody>
 
</table>
 
 
Input Device Functions:
 
 
<table>
 
<tbody>
 
<tr class="odd">
 
<td align="left">PeekQualifier()</td>
 
<td align="left">Return the input device’s current qualifiers. (V36)</td>
 
</tr>
 
</tbody>
 
</table>
 
 
Exec Functions as Used in This Chapter:
 
 
<table>
 
<tbody>
 
<tr class="odd">
 
<td align="left">AbortIO()</td>
 
<td align="left">8.9cmAbort a command to the input device.</td>
 
</tr>
 
<tr class="even">
 
<td align="left">CheckIO()</td>
 
<td align="left">8.9cmReturn the status of an I/O request.</td>
 
</tr>
 
<tr class="odd">
 
<td align="left">CloseDevice()</td>
 
<td align="left">8.9cmRelinquish use of the input device.</td>
 
</tr>
 
<tr class="even">
 
<td align="left">DoIO()</td>
 
<td align="left">8.9cmInitiate a command and wait for completion (synchronous request).</td>
 
</tr>
 
<tr class="odd">
 
<td align="left">OpenDevice()</td>
 
<td align="left">8.9cmObtain shared use of the input device.</td>
 
</tr>
 
<tr class="even">
 
<td align="left">SendIO()</td>
 
<td align="left">8.9cmInitiate a command and return immediately (asynchronous request).</td>
 
</tr>
 
</tbody>
 
</table>
 
 
Exec Support Functions as Used in This Chapter:
 
   
  +
{| class="wikitable"
<table>
 
  +
|+ Input Device Functions
<tbody>
 
  +
| PeekQualifier() || Return the input device’s current qualifiers.
<tr class="odd">
 
  +
|}
<td align="left">CreateExtIO()</td>
 
<td align="left">8.9cmCreate an extended I/O request structure of type IOStdReq. This structure will be used to communicate commands to the input device.</td>
 
</tr>
 
<tr class="even">
 
<td align="left">CreatePort()</td>
 
<td align="left">8.9cmCreate a signal message port for reply messages from the input device. Exec will signal a task when a message arrives at the reply port.</td>
 
</tr>
 
<tr class="odd">
 
<td align="left">DeleteExtIO()</td>
 
<td align="left">8.9cmDelete an I/O request structure created by CreateExtIO().</td>
 
</tr>
 
<tr class="even">
 
<td align="left">DeletePort()</td>
 
<td align="left">8.9cmDelete the message port created by CreatePort().</td>
 
</tr>
 
</tbody>
 
</table>
 
   
 
== Device Interface ==
 
== 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 the “Introduction to Amiga System Devices” chapter for general information on device usage.
+
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|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.
 
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.
Line 150: Line 70:
 
The I/O request used by the input device for most commands is IOStdReq.
 
The I/O request used by the input device for most commands is IOStdReq.
   
  +
<syntaxhighlight>
<pre>struct IOStdReq
 
  +
struct IOStdReq
 
{
 
{
 
struct Message io_Message; /* message reply port */
 
struct Message io_Message; /* message reply port */
Line 160: Line 81:
 
ULONG io_Length; /* number of bytes to transfer */
 
ULONG io_Length; /* number of bytes to transfer */
 
APTR io_Data; /* pointer to data area */
 
APTR io_Data; /* pointer to data area */
};</pre>
+
};
  +
</syntaxhighlight>
  +
 
See the include file exec/io.h for the complete structure definition.
 
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.
+
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.
   
  +
<syntaxhighlight>
<pre>struct timerequest
 
  +
struct TimeRequest
 
{
 
{
struct IORequest tr_node;
+
struct IORequest Request;
struct timeval tr_time;
+
struct TimeVal Time;
};</pre>
+
};
  +
</syntaxhighlight>
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.
 
  +
  +
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.
   
<sub>b</sub>oxIn 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.
+
{{Note|title=In Case You Feel Like Reinventing the Wheel...|text=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 ===
 
=== Opening the Input Device ===
Line 178: Line 104:
 
Three primary steps are required to open the input device:
 
Three primary steps are required to open the input device:
   
* Create a message port using CreatePort(). Reply messages from the device must be directed to a message port.
+
* Create a message port using AllocSysObject() and ASOT_PORT type. Reply messages from the device must be directed to a message port.
* Create an I/O request structure of type IOStdReq or timerequest. The I/O request created by the CreateExtIO() function will be used to pass commands and data to the input device.
+
* 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.
 
* Open the Input device. Call OpenDevice(), passing the I/O request.
   
  +
<syntaxhighlight>
<pre>struct MsgPort *InputMP; /* Message port pointer */
 
  +
struct MsgPort *InputMP = IExec->AllocSysObjectTags(ASOT_PORT, TAG_END);
struct IOStdReq *InputIO; /* I/O request pointer */
 
   
if (InputMP=CreatePort(0,0) )
+
if (InputMP != NULL)
  +
{
if (InputIO=(struct IOStdReq *)
 
  +
struct IOStdReq *InputIO = IExec->AllocSysObjectTags(ASOT_IOREQUEST,
CreateExtIO(InputMP,sizeof(struct IOStdReq)) )
 
if (OpenDevice(&quot;input.device&quot;,0L,(struct IORequest *)InputIO,0) )
+
ASOIOR_Size, sizeof(struct IOStdReq),
  +
ASOIOR_ReplyPort, InputMP,
printf(&quot;input.device did not open\n&quot;);</pre>
 
  +
TAG_END);
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:
 
   
  +
if (ClipIO != NULL)
<pre>#include &lt;devices/timer.h&gt;
 
  +
{
  +
if (IExec->OpenDevice("input.device", 0, (struct IORequest *)InputIO, 0))
  +
IDOS->Printf("input.device did not open\n");
  +
</syntaxhighlight>
   
  +
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:
struct MsgPort *InputMP; /* Message port pointer */
 
struct timerequest *InputIO; /* I/O request pointer */
 
   
  +
<syntaxhighlight>
if (InputMP=CreatePort(0,0) )
 
  +
#include <devices/timer.h>
if (InputIO=(struct timerequest *)
 
CreateExtIO(InputMP,sizeof(struct timerequest)) )
 
if (OpenDevice(&quot;input.device&quot;,0L,(struct IORequest *)InputIO,0) )
 
printf(&quot;input.device did not open\n&quot;);</pre>
 
=== Input Device Event Types ===
 
   
  +
struct MsgPort *InputMP = IExec->AllocSysObjectTags(ASOT_PORT, TAG_END);
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).
 
   
  +
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");
  +
</syntaxhighlight>
   
  +
=== Input Device Event Types ===
The keyboard device is accessible directly (see the “Keyboard Device” chapter). 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 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|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 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 the “Gameport Device” chapter of this manual.
+
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|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” chapter of this manual.
+
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|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 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:
Line 229: Line 167:
 
* The class, subclass, code and qualifier of the previous down key.
 
* 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 the “Gameport Device” and “Console Device” chapters of this manual.
+
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|Gameport Device]] and [[Console_Device|Console Device]].
 
 
   
 
The various types of input events are listed below.
 
The various types of input events are listed below.
   
  +
{| class="wikitable"
ll
 
  +
|+ Input Device Event Types
 
  +
| IECLASS_NULL || A NOP input event
2c'''Input Device Event Types'''<br />
 
  +
|-
IECLASS_NULL &amp; A NOP input event<br />
 
IECLASS_RAWKEY &amp; A raw keycode from the keyboard device<br />
+
| IECLASS_RAWKEY || A raw keycode from the keyboard device
  +
|-
IECLASS_RAWMOUSE &amp; The raw mouse report from the gameport device<br />
 
  +
| IECLASS_RAWMOUSE || The raw mouse report from the gameport device
IECLASS_EVENT &amp; A private console event<br />
 
  +
|-
IECLASS_POINTERPOS &amp; A pointer position report<br />
 
  +
| IECLASS_EVENT || A private console event
IECLASS_TIMER &amp; A timer event<br />
 
  +
|-
IECLASS_GADGETDOWN &amp; Select button pressed down over a gadget<br />
 
  +
| IECLASS_POINTERPOS || A pointer position report
&amp; (address in ie_EventAddress)<br />
 
  +
|-
IECLASS_GADGETUP &amp; Select button released over the same gadget<br />
 
  +
| IECLASS_TIMER || A timer event
&amp; (address in ie_EventAddress)<br />
 
  +
|-
IECLASS_REQUESTER &amp; Some requester activity has taken place.<br />
 
  +
| IECLASS_GADGETDOWN || Select button pressed down over a gadget (address in ie_EventAddress)
IECLASS_MENULIST &amp; This is a menu number transmission (menu<br />
 
  +
|-
&amp; number is in ie_Code)<br />
 
  +
| IECLASS_GADGETUP || Select button released over the same gadget (address in ie_EventAddress)
IECLASS_CLOSEWINDOW &amp; User has selected the active window’s Close Gadget<br />
 
  +
|-
IECLASS_SIZEWINDOW &amp; This window has a new size<br />
 
  +
| IECLASS_REQUESTER || Some requester activity has taken place.
IECLASS_REFRESHWINDOW &amp; The window pointed to by ie_EventAddress<br />
 
  +
|-
&amp; needs to be refreshed<br />
 
  +
| IECLASS_MENULIST || This is a menu number transmission (menu number is in ie_Code)
IECLASS_NEWPREFS &amp; New preferences are available<br />
 
  +
|-
IECLASS_DISKREMOVED &amp; The disk has been removed<br />
 
  +
| IECLASS_CLOSEWINDOW || User has selected the active window’s Close Gadget
IECLASS_DISKINSERTED &amp; The disk has been inserted<br />
 
  +
|-
IECLASS_ACTIVEWINDOW &amp; The window is about to be been made active<br />
 
  +
| IECLASS_SIZEWINDOW || This window has a new size
IECLASS_INACTIVEWINDOW &amp; The window is about to be made inactive<br />
 
  +
|-
IECLASS_NEWPOINTERPOS &amp; Extended-function pointer position report (V36)<br />
 
  +
| IECLASS_REFRESHWINDOW || The window pointed to by ie_EventAddress needs to be refreshed
IECLASS_MENUHELP &amp; Help key report during Menu session (V36)<br />
 
  +
|-
IECLASS_CHANGEWINDOW &amp; The Window has been modified with move, size,<br />
 
  +
| IECLASS_NEWPREFS || New preferences are available
&amp; zoom, or change (V36)<br />
 
  +
|-
 
  +
| 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.
 
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.
   
<sub>b</sub>oxIt All Flows Downhill.Handlers can themselves generate new linked lists of events which can be passed down to lower priority handlers.
+
{{Note|title=It All Flows Downhill|text=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.
 
The InputEvent structure is used by the input device to describe an input event such as a keypress or a mouse movement.
   
  +
<syntaxhighlight>
<pre>struct InputEvent
 
  +
struct InputEvent
 
{
 
{
 
struct InputEvent *ie_NextEvent; /* the chronologically next event */
 
struct InputEvent *ie_NextEvent; /* the chronologically next event */
Line 291: Line 242:
 
UBYTE ie_prev1DownCode; /* previous down keys for dead */
 
UBYTE ie_prev1DownCode; /* previous down keys for dead */
 
UBYTE ie_prev1DownQual; /* key translation: the ie_Code */
 
UBYTE ie_prev1DownQual; /* key translation: the ie_Code */
UBYTE ie_prev2DownCode; /* &amp; low byte of ie_Qualifier for */
+
UBYTE ie_prev2DownCode; /* & low byte of ie_Qualifier for */
UBYTE ie_prev2DownQual; /* last &amp; second last down keys */
+
UBYTE ie_prev2DownQual; /* last & second last down keys */
 
} ie_dead;
 
} ie_dead;
 
} ie_position;
 
} ie_position;
 
struct timeval ie_TimeStamp; /* the system tick at the event */
 
struct timeval ie_TimeStamp; /* the system tick at the event */
};</pre>
+
};
  +
</syntaxhighlight>
The IEPointerPixel and IEPointerTablet structures are used to set the mouse position with the IECLASS_NEWPOINTERPOS input event class.
 
 
   
  +
The IEPointerPixel and IEPointerTablet structures are used to set the mouse position with the IECLASS_NEWPOINTERPOS input event class.
   
  +
<syntaxhighlight>
<pre>struct IEPointerPixel
 
  +
struct IEPointerPixel
 
{
 
{
 
struct Screen *iepp_Screen; /* pointer to an open screen */
 
struct Screen *iepp_Screen; /* pointer to an open screen */
Line 325: Line 277:
   
 
WORD iept_Pressure; /* -128 to 127 (unused, set to 0) */
 
WORD iept_Pressure; /* -128 to 127 (unused, set to 0) */
};</pre>
+
};
  +
</syntaxhighlight>
  +
 
See the include file devices/inputevent.h for the complete structure definitions.
 
See the include file devices/inputevent.h for the complete structure definitions.
   
 
For input device handler installation, the Interrupt structure is used.
 
For input device handler installation, the Interrupt structure is used.
   
  +
<syntaxhighlight>
<pre>struct Interrupt
 
  +
struct Interrupt
 
{
 
{
 
struct Node is_Node;
 
struct Node is_Node;
 
APTR is_Data; /* server data segment */
 
APTR is_Data; /* server data segment */
 
VOID (*is_Code)(); /* server code entry */
 
VOID (*is_Code)(); /* server code entry */
};</pre>
+
};
  +
</syntaxhighlight>
  +
 
See the include file exec/interrupts.h for the complete structure definition.
 
See the include file exec/interrupts.h for the complete structure definition.
   
Line 342: Line 299:
 
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():
 
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():
   
  +
<syntaxhighlight>
<pre>if (!(CheckIO(InputIO)))
 
  +
if (!(IExec->CheckIO(InputIO)))
 
{
 
{
AbortIO(InputIO); /* Ask device to abort request, if pending */
+
IExec->AbortIO(InputIO); /* Ask device to abort request, if pending */
 
}
 
}
WaitIO(InputIO); /* Wait for abort, then clean up */
+
IExec->WaitIO(InputIO); /* Wait for abort, then clean up */
CloseDevice((struct IORequest *)InputIO);</pre>
+
IExec->CloseDevice((struct IORequest *)InputIO);
  +
</syntaxhighlight>
  +
 
== Using the Mouse Port With the Input Device ==
 
== 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.
 
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.
   
  +
<syntaxhighlight>
  +
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);
  +
</syntaxhighlight>
   
<pre>BYTE port = 1; /* set mouse port to right controller */
+
{{Note|title=Put That Back!|text=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.}}
 
InputIO-&gt;io_Data = &amp;port;
 
InputIO-&gt;io_Flags = IOF_QUICK;
 
InputIO-&gt;io_Length = 1;
 
InputIO-&gt;io_Command = IND_SETMPORT;
 
BeginIO((struct IORequest *)InputIO);
 
if (InputIO-&gt;io_Error)
 
printf(&quot;\nSETMPORT failed %d\n&quot;,InputIO-&gt;io_Error);</pre>
 
<sub>b</sub>oxPut 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 ===
 
=== Setting the Conditions for a Mouse Port Report ===
Line 369: Line 330:
 
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.
 
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.
   
  +
<syntaxhighlight>
  +
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);
  +
</syntaxhighlight>
   
<pre>struct GamePortTrigger InputTR;
 
 
InputIO-&gt;io_Data = (APTR)InputTR; /* set trigger conditions */
 
InputIO-&gt;io_Command = IND_SETMTRIG; /* from InputTR */
 
InputIO-&gt;io_Length = sizeof(struct GamePortTrigger);
 
DoIO(InputIO);</pre>
 
 
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.
 
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.
   
  +
<syntaxhighlight>
<pre>struct GamePortTrigger
 
  +
struct GamePortTrigger
 
{
 
{
 
UWORD gpt_Keys; /* key transition triggers */
 
UWORD gpt_Keys; /* key transition triggers */
Line 385: Line 348:
 
UWORD gpt_XDelta; /* X distance trigger */
 
UWORD gpt_XDelta; /* X distance trigger */
 
UWORD gpt_YDelta; /* Y distance trigger */
 
UWORD gpt_YDelta; /* Y distance trigger */
};</pre>
+
};
  +
</syntaxhighlight>
See the “Gameport Device” chapter of this manual for a full description of setting mouse port trigger conditions.
 
  +
  +
See [[Gameport Device]] for a full description of setting mouse port trigger conditions.
   
 
== Adding an Input Handler ==
 
== Adding an Input Handler ==
Line 392: Line 357:
 
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.
 
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.
   
  +
<syntaxhighlight>
  +
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 */
<pre>struct Interrupt *InputHandler;
 
  +
InputIO->io_Command = IND_ADDHANDLER; /* Set command ... */
struct IOStdReq *InputIO
 
  +
IExec->DoIO((struct IORequest *)InputIO); /* DoIO( ) the command */
  +
</syntaxhighlight>
   
InputHandler-&gt;is_Code=ButtonSwap; /* Address of code */
 
InputHandler-&gt;is_Data=NULL; /* User Value passed in A1 */
 
InputHandler-&gt;is_Node.ln_Pri=100; /* Priority in food chain */
 
InputHandler-&gt;is_Node.ln_Name=NameString; /* Name of handler */
 
 
InputIO-&gt;io_Data=(APTR)inputHandler; /* Point to the structure */
 
InputIO-&gt;io_Command=IND_ADDHANDLER; /* Set command ... */
 
DoIO((struct IORequest *)InputIO); /* DoIO( ) the command */</pre>
 
 
Intuition is one of the input device handlers and normally distributes most of the input events.
 
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.
 
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.
   
<sub>b</sub>oxSpeed 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. For this reason it is recommended that the handlers be written in assembly.
+
{{Note|title=Speed Saves|text=''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 ===
 
=== Rules for Input Device Handlers ===
Line 418: Line 382:
   
 
* 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 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 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.
 
* 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 assembly language handler routine should be structured similar to the following pseudo-language statement:
+
Your handler routine should be structured similar to the following pseudo-language statement:
  +
  +
<syntaxhighlight>
  +
newEventChain = yourHandlerCode(oldEventChain, yourHandlerData);
  +
</syntaxhighlight>
   
<pre>newEventChain = yourHandlerCode(oldEventChain, yourHandlerData);
 
d0 = a0 a1</pre>
 
 
where:
 
where:
   
  +
; yourHandlerCode
<ul>
 
  +
: is the entry point to your routine.
<li><p>yourHandlerCode</p>
 
  +
; oldEventChain
<p>is the entry point to your routine.</p></li>
 
  +
: is the starting address for the current chain of input events.
<li><p>oldEventChain</p>
 
  +
; yourHandlerData
<p>is the starting address for the current chain of input events.</p></li>
 
  +
: is a user-definable value, usually a pointer to some data structure your handler requires.
<li><p>yourHandlerData</p>
 
  +
; newEventChain
<p>is a user-definable value, usually a pointer to some data structure your handler requires.</p></li>
 
  +
: is the starting address of an event chain which you are passing to the next handler, if any.
<li><p>newEventChain</p>
 
<p>is the starting address of an event chain which you are passing to the next handler, if any.</p></li></ul>
 
   
When your handler code is called, the event chain is passed in A0 and the handler data is passed in A1. (You may choose not to use A1.) When your code returns, it should return the pointer to the event chain in D0. If all of the events were removed by the routine, return NULL. A NULL (0) value terminates the handling thus freeing more CPU resources.
+
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.
 
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.
 
<sub>b</sub>oxDo Not Confuse the Device.Altering a repeat key report will confuse the input device when it tries to stop the repeating after the key is raised under pre-V36 Kickstart.
 
   
 
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.
 
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 ===
 
=== 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.
 
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-&gt;io_Data=(APTR)InputHandler; /* Which handler to REM */
+
InputIO->io_Data = (APTR)InputHandler; /* Which handler to REM */
InputIO-&gt;io_Command=IND_REMHANDLER; /* The REM command */
+
InputIO->io_Command = IND_REMHANDLER; /* The REM command */
DoIO((struct IORequest *)InputIO); /* Send the command */</pre>
+
IExec->DoIO((struct IORequest *)InputIO); /* Send the command */
  +
</syntaxhighlight>
  +
 
== Writing Events to the Input Device Stream ==
 
== Writing Events to the Input Device Stream ==
 
 
   
 
Typically, input events are internally generated by the timer device, keyboard device, and input device.
 
Typically, input events are internally generated by the timer device, keyboard device, and input device.
Line 467: 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-&gt;io_Data=(APTR)FakeEvent;
+
InputIO->io_Data = (APTR)FakeEvent;
InputIO-&gt;io_Length=sizeof(struct InputEvent);
+
InputIO->io_Length = sizeof(struct InputEvent);
InputIO-&gt;io_Command=IND_WRITEEVENT;
+
InputIO->io_Command = IND_WRITEEVENT;
DoIO((struct IORequest *)InputIO);</pre>
+
IExec->DoIO((struct IORequest *)InputIO);
  +
</syntaxhighlight>
<sub>b</sub>oxYou 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.
 
  +
  +
{{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.}}
   
 
=== Setting the Position of the Mouse ===
 
=== Setting the Position of the Mouse ===
Line 480: Line 447:
 
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.
 
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 pre-V36 Kickstart input class IECLASS_POINTERPOS:
+
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 an absolute position on the current screen.
 
* 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 NULL and relative positioning is done by setting ie_Qualifier to RELATIVE_MOUSE.
+
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.
 
 
   
 
There are three ways to set the mouse pointer position using IECLASS_NEWPOINTERPOS:
 
There are three ways to set the mouse pointer position using IECLASS_NEWPOINTERPOS:
Line 502: 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 NULL for absolute positioning.
+
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 508: Line 473:
 
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.
 
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. Notice that it uses V36 functions wherever possible.
+
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 &lt;exec/types.h&gt;
+
#include <exec/types.h>
#include &lt;exec/memory.h&gt;
+
#include <exec/memory.h>
#include &lt;devices/input.h&gt;
+
#include <devices/input.h>
#include &lt;devices/inputevent.h&gt;
+
#include <devices/inputevent.h>
#include &lt;intuition/screens.h&gt;
+
#include <intuition/screens.h>
   
#include &lt;clib/exec_protos.h&gt;
+
#include <proto/dos.h>
#include &lt;clib/intuition_protos.h&gt;
+
#include <proto/exec.h>
  +
#include <proto/intuition.h>
   
  +
struct IntuitionIFace *IIntuition = NULL;
#include &lt;stdio.h&gt;
 
   
  +
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())
 
 
{
 
{
if (FakeEvent = AllocMem(sizeof(struct InputEvent),MEMF_PUBLIC))
+
struct InputEvent *FakeEvent = IExec->AllocVecTags(sizeof(struct InputEvent),
  +
AVT_Type, MEMF_SHARED,
  +
TAG_END);
  +
  +
if (FakeEvent != NULL)
 
{
 
{
if (NeoPix = AllocMem(sizeof(struct IEPointerPixel),MEMF_PUBLIC))
+
struct IEPointerPixel *NeoPix = IExec->AllocVecTags(sizeof(struct IEPointerPixel),
  +
AVT_Type, MEMF_SHARED,
  +
TAG_END);
  +
  +
if (NeoPix != NULL)
 
{
 
{
if (InputIO = CreateIORequest(InputMP,sizeof(struct IOStdReq)))
+
struct IOStdReq *InputIO = IExec->AllocSysObjectTags(ASOT_IOREQUEST,
  +
ASOIOR_Size, sizeof(struct IOStdReq),
  +
ASOIOR_ReplyPort, InputMP,
  +
TAG_END);
  +
  +
if (InputIO != NULL)
 
{
 
{
if (!OpenDevice(&quot;input.device&quot;,NULL,(struct IORequest *)InputIO,NULL))
+
if (!IExec->OpenDevice("input.device", NULL, (struct IORequest *)InputIO, NULL))
 
{
 
{
/* Open Intuition library */
+
struct Library *IntuitionBase = IExec->OpenLibrary("intuition.library", 50);
if (IntuitionBase = (struct IntuitionBase *)
+
IIntuition = (struct IntuitionIFace*)IExec->GetInterface(IntuitionBase, "main", 1, NULL);
  +
OpenLibrary(&quot;intuition.library&quot;,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-&gt;iepp_Screen =(struct Screen *)PubScreen; /* WB screen */
+
NeoPix->iepp_Screen = (struct Screen *)PubScreen; /* WB screen */
NeoPix-&gt;iepp_Position.X = 100; /* put pointer at x = 100 */
+
NeoPix->iepp_Position.X = 100; /* put pointer at x = 100 */
NeoPix-&gt;iepp_Position.Y = 200; /* put pointer at y = 200 */
+
NeoPix->iepp_Position.Y = 200; /* put pointer at y = 200 */
   
 
/* Set up InputEvent fields */
 
/* Set up InputEvent fields */
FakeEvent-&gt;ie_EventAddress = (APTR)NeoPix; /* IEPointerPixel */
+
FakeEvent->ie_EventAddress = (APTR)NeoPix; /* IEPointerPixel */
FakeEvent-&gt;ie_NextEvent = NULL;
+
FakeEvent->ie_NextEvent = NULL;
FakeEvent-&gt;ie_Class = IECLASS_NEWPOINTERPOS; /* new mouse pos */
+
FakeEvent->ie_Class = IECLASS_NEWPOINTERPOS; /* new mouse pos */
FakeEvent-&gt;ie_SubClass = IESUBCLASS_PIXEL; /* on pixel */
+
FakeEvent->ie_SubClass = IESUBCLASS_PIXEL; /* on pixel */
FakeEvent-&gt;ie_Code = IECODE_NOBUTTON;
+
FakeEvent->ie_Code = IECODE_NOBUTTON;
FakeEvent-&gt;ie_Qualifier = NULL; /* absolute positioning */
+
FakeEvent->ie_Qualifier = 0; /* absolute positioning */
   
InputIO-&gt;io_Data = (APTR)FakeEvent; /* InputEvent */
+
InputIO->io_Data = (APTR)FakeEvent; /* InputEvent */
InputIO-&gt;io_Length = sizeof(struct InputEvent);
+
InputIO->io_Length = sizeof(struct InputEvent);
InputIO-&gt;io_Command = IND_WRITEEVENT;
+
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
printf(&quot;Could not get pointer to screen\n&quot;);
+
IDOS->Printf("Could not get pointer to screen\n");
 
CloseLibrary(IntuitionBase); /* Close intuition library */
 
 
}
 
}
 
else
 
else
printf(&quot;Error: Could not open V36 or higher intuition.library\n&quot;);
+
IDOS->Printf("Error: Could not open intuition.library\n");
   
CloseDevice((struct IORequest *)InputIO);
+
IExec->DropInterface((struct Interface*)IIntuition);
  +
IExec->CloseLibrary(IntuitionBase);
  +
  +
IExec->CloseDevice((struct IORequest *)InputIO);
 
}
 
}
 
else
 
else
printf(&quot;Error: Could not open input.device\n&quot;);
+
IDOS->Printf("Error: Could not open input.device\n");
   
DeleteIORequest(InputIO);
+
IExec->FreeSysObject(ASOT_IOREQUEST, InputIO);
 
}
 
}
 
else
 
else
printf(&quot;Error: Could not create IORequest\n&quot;);
+
IDOS->Printf("Error: Could not create IORequest\n");
   
FreeMem(NeoPix,sizeof(struct IEPointerPixel));
+
IExec->FreeVec(NeoPix);
 
}
 
}
 
else
 
else
printf(&quot;Error: Could not allocate memory for NeoPix\n&quot;);
+
IDOS->Printf("Error: Could not allocate memory for NeoPix\n");
   
FreeMem(FakeEvent,sizeof(struct InputEvent));
+
IExec->FreeVec(FakeEvent);
 
}
 
}
 
else
 
else
printf(&quot;Error: Could not allocate memory for FakeEvent\n&quot;);
+
IDOS->Printf("Error: Could not allocate memory for FakeEvent\n");
   
DeleteMsgPort(InputMP);
+
IExec->FreeSysObject(ASOT_PORT, InputMP);
 
}
 
}
 
else
 
else
printf(&quot;Error: Could not create message port\n&quot;);
+
IDOS->Printf("Error: Could not create message port\n");
  +
}</pre>
 
  +
return 0;
  +
}
  +
</syntaxhighlight>
  +
 
== Setting the Key Repeat Threshold ==
 
== 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.
 
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 tv_secs and the number of microseconds to delay set in tv_micro.
+
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>
  +
#include <devices/timer.h>
   
  +
struct TimeRequest *InputTime; /* Initialize with AllocSysObject(ASOT_IOREQUEST) before using */
<pre>#include &lt;devices/timer.h&gt;
 
   
  +
InputTime->Time.Seconds = 1; /* 1 second */
struct timerequest *InputTime; /* Initialize with CreateExtIO() before using */
 
  +
InputTime->Time.Microseconds = 500000; /* 500,000 microseconds */
  +
InputTime->Request.io_Command = IND_SETTHRESH;
  +
IExec->DoIO((struct IORequest *)InputTime);
  +
</syntaxhighlight>
   
InputTime-&gt;tr_time.tv_secs=1; /* 1 second */
 
InputTime-&gt;tr_time.tv_micro=500000; /* 500,000 microseconds */
 
InputTime-&gt;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 "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.
   
  +
<syntaxhighlight>
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.
 
  +
struct TimeRequest *InputTime; /* Initialize with AllocSysObject(ASOT_IOREQUEST) before using */
   
  +
InputTime->Time.Seconds = 0;
You set the key repeat interval by passing a timerequest with IND_SETPERIOD set in io_Command and the number of seconds set in tv_secs and the number of microseconds set in tv_micro.
 
  +
InputTime->Time.Microseconds = 12000; /* 0.012 seconds */
  +
InputTime->Request.io_Command = IND_SETPERIOD;
  +
IExec->DoIO((struct IORequest *)InputTime);
  +
</syntaxhighlight>
   
<pre>struct timerequest *InputTime; /* Initialize with CreateExtIO() before using */
 
 
InputTime-&gt;tr_time.tv_secs=0;
 
InputTime-&gt;tr_time.tv_micro=12000; /* 0.012 seconds */
 
InputTime-&gt;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.
   
<sub>b</sub>oxThe Right Tool For The Right Job.As previously stated, you ''must'' use a timerequest structure with IND_SETTHRESH and IND_SETPERIOD.
+
{{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 ==
 
 
   
 
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().
 
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.
PeekQualifier()
 
   
  +
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.''
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 an unsigned word with bits set according to the qualifiers in effect at the ''time'' the function is called. It takes no parameters.
In order to call the function, you must set a pointer to the input device base address. The pointer must be declared in the global data area of your program. Once you set the pointer, you can call the function. ''You must open the device in order to access the device base address.''
 
   
  +
<syntaxhighlight>
PeekQualifier()
 
  +
int main()
 
returns an unsigned word with bits set according to the qualifiers in effect at the ''time'' the function is called. It takes no parameters.
 
 
<pre>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 */
UWORD Quals; /* qualifiers */
+
uint16 Quals; /* qualifiers */
 
.
 
.
 
.
 
.
 
.
 
.
if (!OpenDevice(&quot;input.device&quot;,NULL,(struct IORequest *)InputIO,NULL))
+
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-&gt;io_Device;
+
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);
 
}
 
}
  +
}
}</pre>
 
  +
</syntaxhighlight>
  +
 
The qualifiers returned are listed in the table below.
 
The qualifiers returned are listed in the table below.
  +
{| class="wikitable"
 
  +
! Bit
<table>
 
  +
! Qualifier
<tbody>
 
  +
! Key or Button
<tr class="odd">
 
  +
|-
<td align="right">'''Bit'''</td>
 
  +
| 0 || IEQUALIFIER_LSHIFT || Left Shift
<td align="left">'''Qualifier'''</td>
 
  +
|-
<td align="left">'''Key or Button'''</td>
 
  +
| 1 || IEQUALIFIER_RSHIFT || Right Shift
</tr>
 
  +
|-
<tr class="even">
 
  +
| 2 || IEQUALIFIER_CAPSLOCK || Caps Lock
<td align="right">0</td>
 
  +
|-
<td align="left">IEQUALIFIER_LSHIFT</td>
 
  +
| 3 || IEQUALIFIER_CONTROL || Control
<td align="left">Left Shift</td>
 
  +
|-
</tr>
 
  +
| 4 || IEQUALIFIER_LALT || Left Alt
<tr class="odd">
 
  +
|-
<td align="right">1</td>
 
  +
| 5 || IEQUALIFIER_RALT || Right Alt
<td align="left">IEQUALIFIER_RSHIFT</td>
 
  +
|-
<td align="left">Right Shift</td>
 
  +
| 6 || IEQUALIFIER_LCOMMAND || Left-Amiga
</tr>
 
  +
|-
<tr class="even">
 
  +
| 7 || IEQUALIFIER_RCOMMAND || Right-Amiga
<td align="right">2</td>
 
  +
|-
<td align="left">IEQUALIFIER_CAPSLOCK</td>
 
  +
| 12 || IEQUALIFIER_MIDBUTTON || Middle Mouse
<td align="left">Caps Lock</td>
 
  +
|-
</tr>
 
  +
| 13 || IEQUALIFIER_RBUTTON || Right Mouse
<tr class="odd">
 
  +
|-
<td align="right">3</td>
 
  +
| 14 || IEQUALIFIER_LEFTBUTTON || Left Mouse
<td align="left">IEQUALIFIER_CONTROL</td>
 
  +
|}
<td align="left">Control</td>
 
</tr>
 
<tr class="even">
 
<td align="right">4</td>
 
<td align="left">IEQUALIFIER_LALT</td>
 
<td align="left">Left Alt</td>
 
</tr>
 
<tr class="odd">
 
<td align="right">5</td>
 
<td align="left">IEQUALIFIER_RALT</td>
 
<td align="left">Right Alt</td>
 
</tr>
 
<tr class="even">
 
<td align="right">6</td>
 
<td align="left">IEQUALIFIER_LCOMMAND</td>
 
<td align="left">Left-Amiga</td>
 
</tr>
 
<tr class="odd">
 
<td align="right">7</td>
 
<td align="left">IEQUALIFIER_RCOMMAND</td>
 
<td align="left">Right-Amiga</td>
 
</tr>
 
<tr class="even">
 
<td align="right">12</td>
 
<td align="left">IEQUALIFIER_MIDBUTTON</td>
 
<td align="left">Middle Mouse</td>
 
</tr>
 
<tr class="odd">
 
<td align="right">13</td>
 
<td align="left">IEQUALIFIER_RBUTTON</td>
 
<td align="left">Right Mouse</td>
 
</tr>
 
<tr class="even">
 
<td align="right">14</td>
 
<td align="left">IEQUALIFIER_LEFTBUTTON</td>
 
<td align="left">Left Mouse</td>
 
</tr>
 
</tbody>
 
</table>
 
   
 
== Input Device and Intuition ==
 
== Input Device and Intuition ==
Line 760: Line 695:
 
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.
 
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” chapter of ''Amiga ROM Kernel Reference Manual: Libraries'' for more information on IDCMP messages. See the “Console Device” chapter of this manual for more information on console device I/O.
+
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|Intuition Input and Output Methods]] for more information on IDCMP messages. See [[Console_Device|Console Device]] for more information on console device I/O.
   
 
== 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 &lt;exec/types.h&gt;
 
#include &lt;exec/memory.h&gt;
+
#include <exec/types.h>
#include &lt;exec/interrupts.h&gt;
+
#include <exec/memory.h>
#include &lt;devices/input.h&gt;
+
#include <exec/interrupts.h>
#include &lt;intuition/intuition.h&gt;
+
#include <devices/input.h>
  +
#include <intuition/intuition.h>
   
#include &lt;clib/exec_protos.h&gt;
+
#include <proto/dos.h>
#include &lt;clib/alib_protos.h&gt;
+
#include <proto/exec.h>
#include &lt;clib/intuition_protos.h&gt;
+
#include <proto/intuition.h>
   
  +
struct IntuitionIFace *IIntuition = NULL;
#include &lt;stdio.h&gt;
 
   
  +
UBYTE NameString[] = "Swap Buttons";
#ifdef LATTICE
 
int CXBRK(void) { return(0); } /* Disable SAS CTRL/C handling */
 
int chkabort(void) { return(0); } /* really */
 
#endif
 
 
UBYTE NameString[]=&quot;Swap Buttons&quot;;
 
   
 
struct NewWindow mywin={50,40,124,18,0,1,CLOSEWINDOW,
 
struct NewWindow mywin={50,40,124,18,0,1,CLOSEWINDOW,
Line 811: Line 721:
   
 
extern VOID ButtonSwap();
 
extern VOID ButtonSwap();
 
extern struct IntuitionBase *IntuitionBase;
 
   
 
/*
 
/*
Line 821: 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(&quot;intuition.library&quot;,33L))
 
 
{
 
{
if (win=OpenWindow(&amp;mywin))
+
struct Window *win = IIntuition->OpenWindow(&mywin);
  +
if (win != NULL)
 
{
 
{
WaitPort(win-&gt;UserPort);
+
IExec->WaitPort(win->UserPort);
ReplyMsg(GetMsg(win-&gt;UserPort));
+
IExec->ReplyMsg(IExec->GetMsg(win->UserPort));
   
CloseWindow(win);
+
IIntuition->CloseWindow(win);
 
}
 
}
CloseLibrary((struct Library *)IntuitionBase);
 
 
}
 
}
  +
  +
IExec->DropInterface((struct Interface *)IIntuition);
  +
IExec->CloseLibrary(IntuitionBase);
 
}
 
}
   
VOID main(VOID)
+
int main()
 
{
 
{
  +
struct MsgPort *inputPort = IExec->AllocSysObjectTags(ASOT_PORT, TAG_END);
struct IOStdReq *inputReqBlk;
 
struct MsgPort *inputPort;
 
struct Interrupt *inputHandler;
 
   
if (inputPort=CreatePort(NULL,NULL))
+
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)
 
{
 
{
if (inputReqBlk=(struct IOStdReq *)CreateExtIO(inputPort,
+
struct IOStdReq *inputReqBlk = IExec->AllocSysObjectTags(ASOT_IOREQUEST,
sizeof(struct IOStdReq)))
+
ASOIOR_Size, sizeof(struct IOStdReq),
  +
ASOIOR_ReplyPort, inputPort,
  +
TAG_END);
  +
  +
if (inputReqBlk != NULL)
 
{
 
{
if (!OpenDevice(&quot;input.device&quot;,NULL,
+
if (!IExec->OpenDevice("input.device", NULL,
(struct IORequest *)inputReqBlk,NULL))
+
(struct IORequest *)inputReqBlk, NULL))
 
{
 
{
inputHandler-&gt;is_Code=ButtonSwap;
+
inputHandler->is_Node.ln_Pri = 100;
inputHandler-&gt;is_Data=NULL;
+
inputHandler->is_Node.ln_Name = NameString;
inputHandler-&gt;is_Node.ln_Pri=100;
+
inputHandler-&gt;is_Node.ln_Name=NameString;
+
inputReqBlk->io_Data = (APTR)inputHandler;
inputReqBlk-&gt;io_Data=(APTR)inputHandler;
+
inputReqBlk->io_Command = IND_ADDHANDLER;
inputReqBlk-&gt;io_Command=IND_ADDHANDLER;
+
IExec->DoIO((struct IORequest *)inputReqBlk);
DoIO((struct IORequest *)inputReqBlk);
 
   
 
WaitForUser();
 
WaitForUser();
   
inputReqBlk-&gt;io_Data=(APTR)inputHandler;
+
inputReqBlk->io_Data = (APTR)inputHandler;
inputReqBlk-&gt;io_Command=IND_REMHANDLER;
+
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
printf(&quot;Error: Could not open input.device\n&quot;);
+
IDOS->Printf("Error: Could not open input.device\n");
   
DeleteExtIO((struct IORequest *)inputReqBlk);
+
IExec->FreeSysObject(ASOT_IOREQUEST, inputReqBlk);
 
}
 
}
 
else
 
else
printf(&quot;Error: Could not create I/O request\n&quot;);
+
IDOS->Printf("Error: Could not create I/O request\n");
   
  +
IExec->FreeSysObject(ASOT_INTERRUPT, inputHandler);
FreeMem(inputHandler,sizeof(struct Interrupt));
 
 
}
 
}
 
else
 
else
printf(&quot;Error: Could not allocate interrupt struct memory\n&quot;);
+
IDOS->Printf("Error: Could not allocate interrupt\n");
   
DeletePort(inputPort);
+
IExec->FreeSysObject(ASOT_PORT, inputPort);
 
}
 
}
 
else
 
else
printf(&quot;Error: Could not create message port\n&quot;);
+
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 &quot;include:&quot;
 
  +
*/
INCLUDE &quot;exec/types.i&quot;
 
  +
struct InputEvent *currEvent = eventList;
INCLUDE &quot;exec/io.i&quot;
 
  +
while (currEvent) /* Do some more. */
INCLUDE &quot;devices/inputevent.i&quot;
 
  +
{
*
 
  +
/*
************************************************************************
 
  +
* 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
 
beq.s NoRight
+
* 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...
 
beq.s CheckLeft
+
* 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
 
beq.s NoLeft
+
/* Get code */
  +
/* Save a copy */
bset #IEQUALIFIERB_RBUTTON,d0 ; Set the right...
 
beq.s SaveQual
+
/* 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 ==
   
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 ''Amiga ROM Kernel Reference Manual: Includes and Autodocs''.
+
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.
   
  +
{| class="wikitable"
|ll|
 
  +
! Includes
  +
|-
  +
| devices/input.h
  +
|-
  +
| devices/inputevent.h
  +
|}
   
  +
{| class="wikitable"
2c'''Input Device Information'''<br />
 
  +
! AutoDocs
Includes &amp; devices/input.h<br />
 
  +
|-
&amp; devices/input.i<br />
 
  +
| input.doc
&amp; devices/inputevent.h<br />
 
  +
|}
&amp; devices/inputevent.i<br />
 
AutoDocs &amp; input.doc<br />
 

Latest revision as of 02:13, 4 November 2023

Codereview.png 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.

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.
Input Device Functions
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.

Input Device Event Types
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