Copyright (c) Hyperion Entertainment and contributors.

Difference between revisions of "Intuition Mouse"

From AmigaOS Documentation Wiki
Jump to navigation Jump to search
Line 382: Line 382:
 
For instance, by specifying offsets of (0,0), the top left corner of the sprite image will be placed at the pointer position. On the other hand, specifying an xOffset of -7 (remember, sprites are 16 pixels wide) will center the sprite over the pointer position. Specifying an xOffset of -15 will place the right edge of the sprite will be over the pointer position.
 
For instance, by specifying offsets of (0,0), the top left corner of the sprite image will be placed at the pointer position. On the other hand, specifying an xOffset of -7 (remember, sprites are 16 pixels wide) will center the sprite over the pointer position. Specifying an xOffset of -15 will place the right edge of the sprite will be over the pointer position.
   
  +
{{Note|title=Specifying the Hot Spot|text=For compatibility, the application must specify that the "hot spot" of the pointer is one pixel to the left of the desired position. Changes to the pointer done by a program must compensate for this. The Preferences Pointer editor correctly handles this situation.}}
{| class="wikitable"
 
| ''Specifying the Hot Spot''. For compatibility, the application must specify that the "hot spot" of the pointer is one pixel to the left of the desired position. Changes to the pointer done by a program must compensate for this. The Preferences Pointer editor correctly handles this situation.
 
|}
 
   
 
<pre>
 
<pre>

Revision as of 20:33, 26 November 2012

WIP.png This page is currently being updated to AmigaOS 4.x. Some of the information contained here may not yet be applicable in part or totally.

Intuition Mouse and Keyboard

In the Intuition system, the mouse is the normal method of making selections and the keyboard is used for entering character data. This section describes how users employ the mouse to interact with the system and how to arrange for a program to use the mouse. It also describes the use of the keyboard, both as a character input device and as an alternate method of controlling the mouse pointer.

The Mouse

The Amiga mouse is a small, hand-held input device connected to the Amiga by a flexible cable. The user can input horizontal and vertical coordinates with the mouse by sliding it around on a smooth surface. This movement causes the repositioning of a pointer on the display; whenever the mouse is moved the pointer moves, and in the same direction.

The mouse also provides two or three input keys, called mouse buttons, that allow the user to input information to the computer. The basic activities the user can perform with the mouse are shown below.

Mouse Activities
Action Explanation
Moving the Mouse Sliding the body of the mouse over a surface, such as a desk top.
Pressing a button Pushing down a mouse button (which is released at some later time).
Clicking a button Quickly pressing and releasing a mouse button.
Double clicking a button Clicking a button twice in a short period of time.
Dragging Pressing a button and moving the mouse while the button is held down. The drag operation is completed by releasing the button.

The action associated with mouse button presses can occur when the button is first pressed, or while the button is held down, or when the button is released. As an example of this, consider the drag gadget of a window. When the select button of the mouse is first pressed an outline representing the window frame is drawn. While the button is held down the outline remains, and it moves with the pointer as the mouse is moved. When the button is released, the outline is erased and the window takes its new position.

Intuition's Use of Mouse Events

When the mouse is moved or its buttons are pressed, the system generates input events that represent the actions. The input events are taken from the input chain by Intuition when the active window requires the events. Note that only input for a specific window will be affected by changes in that window's IDCMP flags.

Most events generated by the user with the mouse are used by Intuition.

As the user moves the mouse, Intuition changes the position of its pointer. The Intuition pointer moves around the entire video display, mimicking the user's movement of the mouse. The user points at an object by positioning the hot spot of the pointer over the object. The hot spot is the active part of the pointer image; the hot spot for Intuition's default pointer is the pixel at the tip of the arrow.

After pointing to an object, the user can perform some action on that object by selecting it with one of the mouse buttons. These can include any of the actions specified above, such as dragging or double clicking.

The left mouse button is generally used for selection, while the right mouse button is most often used for information transfer. The terms selection and information are intentionally left open to some interpretation, as it is impossible to imagine all the possible uses for the mouse buttons.

The selection/information paradigm can be crafted to cover most interaction between the user and an application. When using the mouse, the application should emphasize this model. It will help the user to understand and remember the mouse control of the application.

Applications that handle mouse button events directly, bypassing the menu and gadget systems, should use the same selection/information model used by Intuition.

Select Button

When the user presses the left, or select button, Intuition examines the state of the system and the position of the pointer. This information is used to decide whether or not the user is trying to select some object, operation, or option. For example, the user positions the pointer over a gadget and then presses the left button to select that gadget. Alternatively, the user can position the pointer over a window and press the select button to activate the window. The pointer is said to be over an object when the pointer's hot spot is positioned within the selection region of the object.

A number of other common techniques involving the select button are available. They include:

Multiple objects or an extended area may be selected by dragging the mouse over a range with the select button held down. For instance, in Release 2, multiple icons can be selected in a Workbench window by pressing the select button while the pointer is over the background of the window (not an icon or a system gadget) and then moving the mouse with the select button held down. A selection rectangle will be displayed and all icons within the rectangle will be selected. Similarly, the user may highlight blocks of text in a console window by pressing the select button over the first desired character and dragging the mouse to the last desired character while holding the button down.

Another way to select multiple objects or an extended area is through the shift select technique. First, select the first member of the group of objects in the normal way. Additional objects can be added to the group by holding down the Shift key while the select button is pressed. This technique works with Workbench icons, where icons may be added one-at-a-time to the list of selected icons; and with text in a console window, where the selected text is extended to include the new position. Note that text need not operate this way, and the application may allow multiple discrete blocks to be selected at any given time.

Both drag select and the dragging of individual objects may often be canceled by pressing the right mouse button before completing the drag operation (before releasing the select button). Examples of this include window dragging and sizing, and positioning of Workbench icons.

Menu Button

The right mouse button is used to initiate and control information gathering processes. Intuition uses this button most often for menu operations.

For most active windows, pressing the menu button will display the window's menu bar at the top of the screen. Dragging the mouse with the menu button depressed allows the user to browse through the available menus. Releasing the right mouse button over a menu item will select that item, if it is a valid choice. Additionally, the user can select multiple items by repeatedly pressing the select button while the menu button is held down.

Drag selection is also available in menu operations. When the menu system is activated, and the user has the menu button pressed, the select button may be pressed and the mouse dragged over all items to be selected. This only works if the select button is pressed after the menu button, and all items that the pointer travels over will be selected.

Double clicking the right mouse button can bring up a special requester for extended exchange of information. This requester is called the double-menu requester, because a double click of the menu button is required to reveal it, and because this requester acts like a super menu through which a complex exchange of information can take place. Because the requester is used for the transfer of information, it is appropriate that this mechanism is called up by using the right button.

The programmer should consult the Amiga User Interface Style Guide for more information on the standard uses of the mouse and its buttons.

Button activation and mouse movements can be combined to create compound instructions. For example, Intuition combines multiple mouse events when displaying the menu system. While the right button is pressed to reveal the menu items of the active window, the user can move the mouse to position the pointer and display different menu items and sub-items. Additionally, multiple presses of the left button can be used to select more than one option from the menus.

Dragging can have different effects, depending on the object being dragged. Dragging a window by the drag gadget will change the position of the window. Dragging a window by the sizing gadget will change the size of the window. Dragging a range in a Workbench window will select all of the icons in the rectangular range.

Mouse Messages

Mouse events are broadcast to the application via the IDCMP or the console device. See the "Intuition Input and Output Methods" chapter in this book for information on the IDCMP. See the Console Device for more about the console device.

Simple mouse button activity not associated with any Intuition function will be reported to the window as an IntuiMessage with a Class of IDCMP_MOUSEBUTTONS. The IntuiMessage Code field will be set to SELECTDOWN, SELECTUP, MIDDLEDOWN, MIDDLEUP, MENUDOWN or MENUUP to specify changes in the state of the left, middle and right buttons, respectively.

Direct select button events will not be received by the program if the select button is pressed while the pointer is positioned over a gadget or other object which uses the button event. For example, select button activity over a gadget is reported with a Class of IDCMP_GADGETDOWN or IDCMP_GADGETUP. The gadget is said to have consumed the mouse events and produced gadget events.

If the menu system is enabled, menu selections appear with a Class of IDCMP_MENUPICK. To directly receive menu button events, the application must set the flag WFLG_RMBTRAP for the window either when the window is opened or by changing the flag in a single, atomic operation. See the chapter "Intuition Windows" for more information on the flag WFLG_RMBTRAP.

The program receives mouse position changes in the event Class IDCMP_MOUSEMOVE. The MouseX and MouseY position coordinates describe the position of the mouse relative to the upper left corner of the reference window. These coordinates are always in the resolution of the screen being used, and may represent any pixel position on the screen, even though the hardware sprites can be positioned only on the even numbered pixels of a high resolution screen and on the even numbered rows of an interlaced screen. Enabling IDCMP_MOUSEMOVE messages is discussed below in the section on "The Pointer".

To get mouse movement reported as deltas (amount of change from the last position) instead of as absolute positions, set the IDCMP flag IDCMP_DELTAMOVE. When IDCMP_DELTAMOVE is set, the IDCMP_MOUSEMOVE messages received by the program will have delta values rather than absolute values. Note that IDCMP_DELTAMOVE is simply a flag used to modify the behavior of IDCMP_MOUSEMOVE, and that no messages of class IDCMP_DELTAMOVE are ever sent.

Each window has a queue limit for the number of IDCMP_MOUSEMOVE messages waiting on its IDCMP at any given time. If the number of mouse move messages waiting at the IDCMP is equal to the queue limit, then Intuition will discard additional IDCMP_MOUSEMOVE messages until the application replies to one of the queued mouse move messages. The default queue limit for mouse move messages is five.

Be aware that this may cause some data loss, especially when the application is using IDCMP_DELTAMOVE, as the information contained in the discarded messages is not repeated. When using IDCMP_DELTAMOVE, this could cause the application to lose track of the actual pointer position. The application may wish to change the default mouse queue size if it is unable to reply to messages queued at the IDCMP for an extended period. The mouse queue can be set when the window is opened by using the WA_MouseQueue tag, and may later be modified using the SetMouseQueue() call. Note that the actual mouse position is always available to the application through the Window structure MouseX and MouseY.

Mouse Usage Example

The example program below shows the use of IDCMP_MOUSEBUTTONS, IDCMP_MOUSEMOVE and DoubleClick(). DoubleClick() is used to test the interval between two times and determine if the interval is within the user specified time for double clicking as set in the Preferences Input editor.

BOOL DoubleClick( ULONG sSeconds, ULONG sMicros,
                  ULONG cSeconds, ULONG cMicros );

The sSeconds and sMicros arguments specify a timestamp value describing the start of the double click time interval to be tested. The cSeconds and cMicros arguments specify a timestamp value describing the end of the double click time interval to be tested.

DoubleClick() returns TRUE if the time interval was short enough to qualify as a double-click. A FALSE return indicates that the time interval between presses took too long. The button presses should be treated as separate events in that case.

;/* mousetest.c - Execute me to compile me with SAS C 5.10
LC -b1 -cfistq -v -y -j73 mousetest.c
Blink FROM LIB:c.o,mousetest.o TO mousetest LIBRARY LIB:LC.lib,LIB:Amiga.lib
quit

** mousetest.c - Read position and button events from the mouse.
*/
#define INTUI_V36_NAMES_ONLY

#include <exec/types.h>
#include <intuition/intuition.h>
#include <graphics/gfxbase.h>
#include <devices/inputevent.h>

#include <clib/exec_protos.h>
#include <clib/graphics_protos.h>
#include <clib/intuition_protos.h>

#include <stdio.h>

#ifdef LATTICE
int CXBRK(void)    { return(0); }  /* Disable Lattice CTRL/C handling */
int chkabort(void) { return(0); }  /* really */
#endif

#define BUFSIZE 16

/* something to use to track the time between messages
** to test for double-clicks.
*/
typedef struct myTimeVal
    {
    ULONG LeftSeconds;
    ULONG LeftMicros;
    ULONG RightSeconds;
    ULONG RightMicros;
    } MYTIMEVAL;


/* our function prototypes */
VOID doButtons(struct IntuiMessage *msg, MYTIMEVAL *tv);
VOID process_window(struct Window *win);

struct Library *IntuitionBase;
struct GfxBase       *GfxBase;  /* we need GfxBase->DefaultFont */


/*
** main() -- set-up everything.
*/
VOID main(int argc, char **argv)
{
struct Window *win;
struct Screen *scr;
struct DrawInfo *dr_info;
ULONG width;

/* Open the libraries we will use.  Requires Release 2 (KS V2.04, V37) */
if (IntuitionBase = OpenLibrary("intuition.library",37))
    {
    if (GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 37))
        {
        /* Lock the default public screen in order to read its DrawInfo data */
        if (scr = LockPubScreen(NULL))
            {
            if (dr_info = GetScreenDrawInfo(scr))
                {
                /* use wider of space needed for output (18 chars and spaces)
                 * or titlebar text plus room for titlebar gads (approx 18 each)
                 */
                width = max((GfxBase->DefaultFont->tf_XSize * 18),
                            (18 * 2) + TextLength(&scr->RastPort,"MouseTest",9));

                if (win = OpenWindowTags(NULL,
                            WA_Top,    20,
                            WA_Left,   100,
                            WA_InnerWidth,  width,
                            WA_Height, (2 * GfxBase->DefaultFont->tf_YSize) +
                                       scr->WBorTop + scr->Font->ta_YSize + 1 +
                                       scr->WBorBottom,
                            WA_Flags, WFLG_DEPTHGADGET | WFLG_CLOSEGADGET |
                                      WFLG_ACTIVATE    | WFLG_REPORTMOUSE |
                                      WFLG_RMBTRAP     | WFLG_DRAGBAR,
                            WA_IDCMP, IDCMP_CLOSEWINDOW | IDCMP_RAWKEY |
                                      IDCMP_MOUSEMOVE   | IDCMP_MOUSEBUTTONS,
                            WA_Title, "MouseTest",
                            WA_PubScreen, scr,
                            TAG_END))
                    {
                    printf("Monitors the Mouse:\n");
                    printf("    Move Mouse, Click and DoubleClick in Window\n");

                    SetAPen(win->RPort,dr_info->dri_Pens[TEXTPEN]);
                    SetBPen(win->RPort,dr_info->dri_Pens[BACKGROUNDPEN]);
                    SetDrMd(win->RPort,JAM2);

                    process_window(win);

                    CloseWindow(win);
                    }
                FreeScreenDrawInfo(scr, dr_info);
                }
            UnlockPubScreen(NULL,scr);
            }
        CloseLibrary((struct Library *)GfxBase);
        }
    CloseLibrary(IntuitionBase);
    }
}


/*
** process_window() - simple message loop for processing IntuiMessages
*/
VOID process_window(struct Window *win)
{
USHORT done;
struct IntuiMessage *msg;
MYTIMEVAL tv;
UBYTE prt_buff[14];
LONG xText, yText;  /* places to position text in window. */

done = FALSE;
tv.LeftSeconds = 0; /* initial values for testing double-click */
tv.LeftMicros  = 0;
tv.RightSeconds = 0;
tv.RightMicros  = 0;
xText = win->BorderLeft + (win->IFont->tf_XSize * 2);
yText = win->BorderTop + 3 + win->IFont->tf_Baseline;

while (!done)
    {
    Wait((1L<<win->UserPort->mp_SigBit));

    while ((!done) &&
           (msg = (struct IntuiMessage *)GetMsg(win->UserPort)))
        {
        switch (msg->Class)
            {
            case IDCMP_CLOSEWINDOW:
                done = TRUE;
                break;
            /* NOTE NOTE NOTE:  If the mouse queue backs up a lot, Intuition
            ** will start dropping MOUSEMOVE messages off the end until the
            ** queue is serviced.  This may cause the program to lose some
            ** of the MOUSEMOVE events at the end of the stream.
            **
            ** Look in the window structure if you need the true position
            ** of the mouse pointer at any given time.  Look in the
            ** MOUSEBUTTONS message if you need position when it clicked.
            ** An alternate to this processing would be to set a flag that
            ** a mousemove event arrived, then print the position of the
            ** mouse outside of the "while (GetMsg())" loop.  This allows
            ** a single processing call for many mouse events, which speeds
            ** up processing A LOT!  Something like:
            **
            ** while (GetMsg())
            **    {
            **    if (class == IDCMP_MOUSEMOVE)
            **        mouse_flag = TRUE;
            **    ReplyMsg();   NOTE: copy out all needed fields first !
            **    }
            ** if (mouse_flag)
            **    {
            **    process_mouse_event();
            **    mouse_flag = FALSE;
            **    }
            **
            ** You can also use IDCMP_INTUITICKS for slower paced messages
            ** (all messages have mouse coordinates.)
            */
            case IDCMP_MOUSEMOVE:
                /* Show the current position of the mouse relative to the
                ** upper left hand corner of our window
                */
                Move(win->RPort,xText,yText);
                sprintf(prt_buff, "X%5d Y%5d", msg->MouseX, msg->MouseY);
                Text(win->RPort,prt_buff,13);
                break;
            case IDCMP_MOUSEBUTTONS:
                doButtons(msg,&tv);
                break;
            }
        ReplyMsg((struct Message *)msg);
        }
    }
}

/*
** Show what mouse buttons where pushed
*/
VOID doButtons(struct IntuiMessage *msg, MYTIMEVAL *tv)
{
/* Yes, qualifiers can apply to the mouse also.  That is how
** we get the shift select on the Workbench.  This shows how
** to see if a specific bit is set within the qualifier
*/
if (msg->Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
    printf("Shift ");

switch (msg->Code)
    {
    case SELECTDOWN:
        printf("Left Button Down at X%ld Y%ld", msg->MouseX, msg->MouseY);
        if(DoubleClick(tv->LeftSeconds, tv->LeftMicros, msg->Seconds, msg->Micros))
            printf(" DoubleClick!");
        else
            {
            tv->LeftSeconds = msg->Seconds;
            tv->LeftMicros  = msg->Micros;
            tv->RightSeconds = 0;
            tv->RightMicros  = 0;
            }
        break;
    case SELECTUP:
        printf("Left Button Up   at X%ld Y%ld", msg->MouseX, msg->MouseY);
        break;
    case MENUDOWN:
        printf("Right Button down at X%ld Y%ld", msg->MouseX, msg->MouseY);
        if(DoubleClick(tv->RightSeconds, tv->RightMicros, msg->Seconds, msg->Micros))
            printf(" DoubleClick!");
        else
            {
            tv->LeftSeconds = 0;
            tv->LeftMicros  = 0;
            tv->RightSeconds = msg->Seconds;
            tv->RightMicros  = msg->Micros;
            }
        break;
    case MENUUP:
        printf("Right Button Up   at X%ld Y%ld", msg->MouseX, msg->MouseY);
        break;
    }
printf("\n");
}

The Pointer

The system provides a pointer to allow the user to make selections from menus, choose gadgets, and so on. The user may control the pointer with a mouse, the keyboard cursor keys or some other type of controller. The specific type of controller is not important, as long as the proper types of input events can be generated.

The pointer is associated with the active window and the input focus. The active window controls the pointer imagery and receives the input stream from the mouse. The pointer and mouse may be used to change the input focus by selecting another window.

Pointer Position

There are two ways to determine the position of the pointer: by direct examination of variables in the window structure at any time, and by examining messages sent by Intuition which inform the application of pointer movement. The pointer coordinates are relative to the upper left corner of the window and are reported in the resolution of the screen, even though the pointer's visible resolution is always in low-resolution pixels (note that the pointer is actually a sprite).

The MouseX and MouseY fields of the Window structure always contain the current pointer x and y coordinates, whether or not the window is the active one. If the window is a GimmeZeroZero window, the variables GZZMouseX and GZZMouseY in the Window structure contain the position of the mouse relative to the upper left corner of the inner window.

If the window is receiving mouse move messages, it will get a set of x,y coordinates each time the pointer moves. To receive messages about pointer movements, the WFLG_REPORTMOUSE flag must be set in the Window structure. This flag can be set when the window is opened. The flag can also be modified after the window is open by calling ReportMouse(), however C programmers should avoid this function. ReportMouse() has problems due to historic confusion about the ordering of its C language arguments. Do not use ReportMouse() unless you are programming in assembler. C programmers should set the flag directly in the Window structure using an atomic operation.

Most compilers generate atomic code for operations such as mywindow->flags |= WFLG_REPORTMOUSE or mywindow->flags &= ~WFLG_REPORTMOUSE. If you are unsure of getting an atomic operation from your compiler, you may wish to do this operation in assembler, or bracket the code with a Forbid()/Permit() pair.

After the WFLG_REPORTMOUSE flag is set, whenever the window is active it will be sent an IDCMP_MOUSEMOVE messages each time the pointer position changes. The window must have the IDCMP flag IDCMP_MOUSEMOVE set to receive these messages.

Mouse movements can cause a very large number of messages to be sent to the IDCMP, the application should be prepared to handle them efficiently.

Messages about pointer movements may also be activated by setting the flag GACT_FOLLOWMOUSE in an application gadget structure. When this flag is set in a gadget, changes in the pointer position are reported as long as the gadget is selected by the user. These messages are also sent as IDCMP_MOUSEMOVE messages.

Custom Pointer

An application can set a custom pointer for a window to replace the default pointer. This custom pointer will be displayed whenever the window is the active one.

To place a custom pointer in a window, call SetPointer().

VOID SetPointer( struct Window *window, UWORD *pointer, LONG height,
                 LONG width, LONG xOffset, LONG yOffset );

Set the window argument to the address of the window that is to receive this custom pointer definition. The pointer argument is the address of the data that defines the custom pointer image. The format of this data is discussed in the next section, "The Sprite Data Structure".

The height and width specify the dimensions of the pointer sprite. There is no height restriction but the width of the sprite must be less than or equal to 16.

The xOffset and yOffset are used to offset the top left corner of the hardware sprite imagery from what Intuition regards as the current position of the pointer. Another way of describing this is the offset of the default Intuition pointer hot spot from the top left corner of the sprite.

For instance, by specifying offsets of (0,0), the top left corner of the sprite image will be placed at the pointer position. On the other hand, specifying an xOffset of -7 (remember, sprites are 16 pixels wide) will center the sprite over the pointer position. Specifying an xOffset of -15 will place the right edge of the sprite will be over the pointer position.

Specifying the Hot Spot
For compatibility, the application must specify that the "hot spot" of the pointer is one pixel to the left of the desired position. Changes to the pointer done by a program must compensate for this. The Preferences Pointer editor correctly handles this situation.
VOID ClearPointer( struct Window *window );

Set the window argument to the address of the window that is to have its custom pointer definition cleared. The pointer will be restored to the default Intuition pointer imagery

SetPointer() and ClearPointer() take effect immediately if the window is active, otherwise, the change will only be displayed when the window is made active.

The Sprite Data Structure

To define the pointer, set up a sprite data structure (sprites are one of the general purpose Amiga graphics structures). The sprite image data must be located in Chip memory, which is memory that can be accessed by the special Amiga hardware chips. Expansion, or Fast memory cannot be addressed by the custom chips. Ensure that data is in Chip memory by using the AllocMem() function with the MEMF_CHIP flag, and copying the data to the allocated space. Alternately, use the tools or flags provided by each compiler for this purpose. See the chapter entitled "Exec Memory Allocation" for more information.

A sprite data structure is made up of words of data. In a pointer sprite, the first two words and the last two words are reserved for the system and should be set to zero. All other words contain the sprite image data.

The pointer in the example, a standard busy pointer, is sixteen lines high and sixteen pixels wide. Currently, all sprites are two bit planes deep, with one word of data for each line of each plane. The example sprite image consists of 36 words (2 planes * 18 lines = 36 words). Add to this the four reserved words of control information for a total of 40 words of data. See the example below for the complete data definition.

The sprite data words are combined to determine which color will appear at each pixel position of each row of the sprite. The first two words of image data, 0x0400 and 0x07C0, represent the top line of the sprite. The numbers must be viewed as binary numbers and combined in a bit-wise fashion. The highest bit from each word are combined to form a two bit number representing the color register for the leftmost pixel. The next two bits represent the next pixel in the row, and so on, until the low order bits from each word represent the rightmost pixel in the row.

For example:

LibFig10-1.png

Pointer Color Ordering. The first word in a line gives the least significant bit of the color register and the second word gives the most significant bit.

Sprites get their color information from the color registers much like screens do. See the Amiga Hardware Reference Manual for more information on the assignment of color registers to sprites. Note that the color number given above is added to a base number to determine the actual hardware color register.

The colors of the Intuition pointer may be changed. The Intuition pointer is always sprite 0. To change the colors of sprite 0, call the graphics library routine SetRGB4().

Pointer Example

The program below shows how to set the pointer for a window. In this example, the pointer imagery is changed to a stopwatch symbol which could be used to indicate a busy period.


;/* custompointer.c - Execute me to compile me with SAS C 5.10
LC -b1 -cfistq -v -y -j73 custompointer.c
Blink FROM LIB:c.o,custompointer.o TO custompointer LIBRARY LIB:LC.lib,LIB:Amiga.lib
quit

** custompointer.c - Show the use of a custom busy pointer, as well as
** using a requester to block input to a window.
*/
#define INTUI_V36_NAMES_ONLY

#include <exec/types.h>
#include <exec/libraries.h>
#include <intuition/intuition.h>

#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/intuition_protos.h>

#ifdef LATTICE
int CXBRK(void)    { return(0); }  /* Disable Lattice CTRL/C handling */
int chkabort(void) { return(0); }  /* really */
#endif

struct Library *IntuitionBase;


UWORD __chip waitPointer[] =
    {
    0x0000, 0x0000,     /* reserved, must be NULL */

    0x0400, 0x07C0,
    0x0000, 0x07C0,
    0x0100, 0x0380,
    0x0000, 0x07E0,
    0x07C0, 0x1FF8,
    0x1FF0, 0x3FEC,
    0x3FF8, 0x7FDE,
    0x3FF8, 0x7FBE,
    0x7FFC, 0xFF7F,
    0x7EFC, 0xFFFF,
    0x7FFC, 0xFFFF,
    0x3FF8, 0x7FFE,
    0x3FF8, 0x7FFE,
    0x1FF0, 0x3FFC,
    0x07C0, 0x1FF8,
    0x0000, 0x07E0,

    0x0000, 0x0000,     /* reserved, must be NULL */
    };


/*
** The main() routine
*/
VOID main(int argc, char **argv)
{
struct Window *win;
struct Requester null_request;
extern UWORD __chip waitPointer[];

if (IntuitionBase = OpenLibrary("intuition.library",37))
    {
    /* the window is opened as active (WA_Activate) so that the busy
    ** pointer will be visible.  If the window was not active, the
    ** user would have to activate it to see the change in the pointer.
    */
    if (win = OpenWindowTags(NULL,
                             WA_Activate, TRUE,
                             TAG_END))
        {
        /* a NULL requester can be used to block input
        ** in a window without any imagery provided.
        */
        InitRequester(&null_request);

        Delay(50);  /* simulate activity in the program. */

        /* Put up the requester to block user input in the window,
        ** and set the pointer to the busy pointer.
        */
        if (Request(&null_request, win))
            {
            SetPointer(win, waitPointer, 16, 16, -6, 0);

            Delay(100);  /* simulate activity in the program. */

            /* clear the pointer (which resets the window to the default
            ** pointer) and remove the requester.
            */
            ClearPointer(win);
            EndRequest(&null_request, win);
            }

        Delay(100);  /* simulate activity in the program. */

        CloseWindow(win);
        }
    CloseLibrary(IntuitionBase);
    }
}

The Keyboard

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

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

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

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

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

RAWKEY Keymapping Example

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

;/* rawkey.c - Execute me to compile me with SAS C 5.10
LC -b1 -cfistq -v -y -j73 rawkey.c
Blink FROM LIB:c.o,rawkey.o TO rawkey LIBRARY LIB:LC.lib,LIB:Amiga.lib
quit

** rawkey.c - How to correctly convert from RAWKEY to keymapped ASCII
*/
#define INTUI_V36_NAMES_ONLY

#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <devices/inputevent.h>
#include <clib/exec_protos.h>
#include <clib/intuition_protos.h>
#include <clib/console_protos.h>
#include <stdio.h>

#ifdef LATTICE
int CXBRK(void)    { return(0); }  /* Disable Lattice CTRL/C handling */
int chkabort(void) { return(0); }  /* really */
#endif

/* our function prototypes */
LONG deadKeyConvert(struct IntuiMessage *msg, UBYTE *kbuffer,
                    LONG kbsize, struct KeyMap *kmap, struct InputEvent *ievent);
VOID print_qualifiers(ULONG qual);
BOOL doKeys(struct IntuiMessage *msg, struct InputEvent *ievent,
                    UBYTE **buffer, ULONG *bufsize);
VOID process_window(struct Window *win, struct InputEvent *ievent,
                    UBYTE **buffer, ULONG *bufsize);

/* A buffer is created for RawKeyConvert() to put its output. BUFSIZE is the size of
** the buffer in bytes.  NOTE that this program starts out with a buffer size of 2.
** This is only to show how the buffer is automatically increased in size  by this
** example!  In an application, start with a much larger buffer and you will probably
** never have to increase its size. 128 bytes or so should do the trick, but always
** be able to change the size if required.
*/
#define BUFSIZE (2)

struct Library *IntuitionBase, *ConsoleDevice;

/* main() - set-up everything used by program. */
VOID main(int argc, char **argv)
{
struct Window *win;
struct IOStdReq ioreq;
struct InputEvent *ievent;
UBYTE *buffer;
ULONG bufsize = BUFSIZE;

if(IntuitionBase = OpenLibrary("intuition.library",37)) {
    /* Open the console device just to do keymapping. (unit -1 means any unit) */
    if (0 == OpenDevice("console.device",-1,(struct IORequest *)&ioreq,0)) {
        ConsoleDevice = (struct Library *)ioreq.io_Device;

        /* Allocate the initial character buffer used by deadKeyConvert() and RawKeyConvert()
        ** for returning translated characters. If the characters generated by these routines
        ** cannot fit into the buffer, the application must pass a larger buffer.  This is
        ** done in this code by freeing the old buffer and allocating a new one.
        */
        if (buffer = AllocMem(bufsize,MEMF_CLEAR)) {
            if (ievent = AllocMem(sizeof(struct InputEvent),MEMF_CLEAR)) {
                if (win = OpenWindowTags(NULL,
                        WA_Width, 300,
                        WA_Height, 50,
                        WA_Flags, WFLG_DEPTHGADGET | WFLG_CLOSEGADGET | WFLG_ACTIVATE,
                        WA_IDCMP, IDCMP_CLOSEWINDOW | IDCMP_RAWKEY,
                        WA_Title, "Raw Key Example",
                        TAG_END)) {
                    printf("Press keyboard keys to see ASCII conversion from rawkey\n");
                    printf("Unprintable characters will be shown as %c\n\n",0x7f);
                    process_window(win,ievent,&buffer,&bufsize);
                    CloseWindow(win);
                    }
                FreeMem(ievent,sizeof(struct InputEvent));
                }
            /* Buffer can be freed elsewhere in the program so test first. */
            if (buffer != NULL)
                FreeMem(buffer,bufsize);
            }
        CloseDevice((struct IORequest *)&ioreq);
        }
    CloseLibrary(IntuitionBase);
    }
}

/* Convert RAWKEYs into VANILLAKEYs, also shows special keys like HELP, Cursor Keys,
** FKeys, etc.  It returns:
**   -2 if not a RAWKEY event.
**   -1 if not enough room in the buffer, try again with a bigger buffer.
**   otherwise, returns the number of characters placed in the buffer.
*/
LONG deadKeyConvert(struct IntuiMessage *msg, UBYTE *kbuffer,
    LONG kbsize, struct KeyMap *kmap, struct InputEvent *ievent)
{
if (msg->Class != IDCMP_RAWKEY) return(-2);
ievent->ie_Class = IECLASS_RAWKEY;
ievent->ie_Code = msg->Code;
ievent->ie_Qualifier = msg->Qualifier;
ievent->ie_position.ie_addr = *((APTR*)msg->IAddress);

return(RawKeyConvert(ievent,kbuffer,kbsize,kmap));
}

/* print_qualifiers() - print out the values found in the qualifier bits of
** the message. This will print out all of the qualifier bits set.
*/
VOID print_qualifiers(ULONG qual)
{
printf("Qual:");
if (qual & IEQUALIFIER_LSHIFT)         printf("LShft,");
if (qual & IEQUALIFIER_RSHIFT)         printf("RShft,");
if (qual & IEQUALIFIER_CAPSLOCK)       printf("CapLok,");
if (qual & IEQUALIFIER_CONTROL)        printf("Ctrl,");
if (qual & IEQUALIFIER_LALT)           printf("LAlt,");
if (qual & IEQUALIFIER_RALT)           printf("RAlt,");
if (qual & IEQUALIFIER_LCOMMAND)       printf("LCmd,");
if (qual & IEQUALIFIER_RCOMMAND)       printf("RCmd,");
if (qual & IEQUALIFIER_NUMERICPAD)     printf("NumPad,");
if (qual & IEQUALIFIER_REPEAT)         printf("Rpt,");
if (qual & IEQUALIFIER_INTERRUPT)      printf("Intrpt,");
if (qual & IEQUALIFIER_MULTIBROADCAST) printf("Multi Broadcast,");
if (qual & IEQUALIFIER_MIDBUTTON)      printf("MidBtn,");
if (qual & IEQUALIFIER_RBUTTON)        printf("RBtn,");
if (qual & IEQUALIFIER_LEFTBUTTON)     printf("LBtn,");
if (qual & IEQUALIFIER_RELATIVEMOUSE)  printf("RelMouse,");
}

/* doKeys() - Show what keys were pressed. */
BOOL doKeys(struct IntuiMessage *msg, struct InputEvent *ievent,
            UBYTE **buffer, ULONG *bufsize)
{
USHORT char_pos;
USHORT numchars;
BOOL   ret_code = TRUE;
UBYTE  realc, c;

/* deadKeyConvert() returns -1 if there was not enough space in the buffer to
** convert the string. Here, the routine increases the size of the buffer on the
** fly...Set the return code to FALSE on failure.
*/
numchars = deadKeyConvert(msg, *buffer, *bufsize - 1, NULL, ievent);
while ((numchars == -1) && (*buffer != NULL)) {
    /* conversion failed, buffer too small. try to double the size of the buffer. */
    FreeMem(*buffer, *bufsize);
    *bufsize = *bufsize << 1;
    printf("Increasing buffer size to %d\n", *bufsize);

    if (NULL == (*buffer = AllocMem(*bufsize, MEMF_CLEAR)))  ret_code = FALSE;
    else  numchars = deadKeyConvert(msg, *buffer, *bufsize - 1, NULL, ievent);
    }

/* numchars contains the number of characters placed within the buffer.  Key up events and   */
/* key sequences that do not generate any data for the program (like deadkeys) will return   */
/* zero.  Special keys (like HELP, the cursor keys, FKeys, etc.) return multiple characters  */
/* that have to then be parsed by the application.  Allocation failed above if buffer is NULL*/
if (*buffer != NULL) {
    /* if high bit set, then this is a key up otherwise this is a key down */
    if (msg->Code & 0x80)
          printf("Key Up:   ");
    else
          printf("Key Down: ");

    print_qualifiers(msg->Qualifier);
    printf(" rawkey #%d maps to %d ASCII character(s)\n", 0x7F & msg->Code, numchars);
    for (char_pos = 0; char_pos < numchars; char_pos++) {
        realc = c = (*buffer)[char_pos];
        if ((c <= 0x1F)||((c >= 0x80)&&(c < 0xa0)))
            c = 0x7f;
        printf("  %3d ($%02x) = %c\n", realc, realc, c);
        }
    }
return(ret_code);
}

/* process_window() - simple event loop.  Note that the message is not replied
** to until the end of the loop so that it may be used in the doKeys() call.
*/
VOID process_window(struct Window *win, struct InputEvent *ievent,
    UBYTE **buffer, ULONG *bufsize)
{
struct IntuiMessage *msg;
BOOL done;

done = FALSE;
while (done == FALSE) {
    Wait((1L<<win->UserPort->mp_SigBit));
    while ((done == FALSE) && (msg = (struct IntuiMessage *)GetMsg(win->UserPort))) {
        switch (msg->Class) {     /* handle our events */
            case IDCMP_CLOSEWINDOW:
                done = TRUE;
                break;
            case IDCMP_RAWKEY:
                if (FALSE == doKeys(msg,ievent,buffer,bufsize))
                    done = TRUE;
                break;
            }
        ReplyMsg((struct Message *)msg);
        }
    }
}

Keyboard Control of the Pointer

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

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

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

Intuition Keyboard Shortcuts

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

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

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

Intuition Keyboard Shortcuts

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

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

Menu Shortcuts

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

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

Amiga Qualifiers

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

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

Keyboard Qualifiers

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

Function Reference

The following are brief descriptions of the Intuition functions that relate to the use of the mouse and keyboard under Intuition. See the SDK for details on each function call.

Function Description
DoubleClick() Test two time values for double click status.
SetPointer() Change the Intuition pointer imagery for an open window.
ClearPointer() Restore the default Intuition pointer imagery.
SetMouseQueue() Change the mouse queue for an open window.
ReportMouse() A function C programmers should not use.