[Home] [Documentation] [Top]

    Stealth J-Mouse

    Developer's Guide, Version 1

    This paper is intended to provide a starting point for developers who wish to integrate the Stealth J-Mouse with their software. The Stealth J-Mouse is built on a Kensington Mouse board with an Agilent (formerly Hewlett-Packard) Optical Navigation engine. The mouse is PS/2 and USB compatible. It also contains a Thrustmaster USB Game Pad. The mouse requires two USB ports for installation.

    The information provided here in respect to Windows programming has not been verified by the author in all cases with actual examples. Please do your own investigations, beginning with the Internet links shown in section 4.0.

    1. The Stealth J-Mouse

    The device is an ergonomic, two-handed mouse with 10 buttons and two joysticks and a wheel which we call the Z-Wheel. It has an optical X, Y motion sensor, a high-resolution wheel, and is compatible with 3-button wheel mouse such as Microsoft Explorer mouse and Kensington Mouse-in-a-Box Optical Pro. It is delivered with Kensington Mouse software, which makes it compatible with Microsoft Windows 95, 98, NT 3.5, Windows Me, and Windows 2000.

    Please note that the terms Joystick, Game Port, and Game Pad are used interchangeably, depending on context. They are all read the same way by the software. We use a Thrustmaster FireStorm Dual Analog 2 Game Pad internally, which provides us with two joysticks and 7 buttons.

    Three of the 10 buttons (5, 6, and 7 in the diagram below) are connected to the mouse. The other 7 buttons are connected to the Game Pad interface. The joysticks themselves are buttons also. It is possible to press any combination of buttons. It is a good idea to use buttons 1 and 8 as shift keys, to give additional functions to the other 8 buttons, but that is strictly a programming decision.

    The Stealth 3D Mouse buttons are arranged like this:

    The buttons are arranged to make sense to a right-handed user. That is, buttons 5, 6, and 7 are in the same position as for a normal 3-button mouse. The remaining buttons are mapped to certain Game Pad buttons. The table below shows the button mapping.

    The following table is preliminary. Note that buttons 1, 8, 9, and 10 follow the general layout for a Game Pad, and will probably not change in the future. Buttons 2, 3, 4 however, are highly likely to change.

    Button

    Mapping

    1

    Game 9

    2

    Game 5

    3

    Game 6

    4

    Game 7

    5

    Mouse L

    6

    Mouse M

    7

    Mouse R

    8

    Game 10

    9

    Game 11

    10

    Game 12

     

    1.1 Kensington MouseWorks Software

    The Stealth 3D Mouse is delivered with Kensington MouseWorks Version 5.4.1.

    1.2 Microsoft IntelliPoint Software

    While not quite as flexible as Kensington MouseWorks, IntelliPoint 3.0 has some features which make its use more convenient in some ways. Specifically, there is no provision for different users to have their own settings.

    2. Reading XY Coordinates

     

    2.1 Reading XY Coordinates from Mouse

    XY Coordinates are read as with any mouse. There are some points that require different processing however.

    2.1.1 Jumping

    While the optical mouse has a high resolution, and works on almost any surface, it is still prone to jumping when the reflection of the surface is too great. It is quite easy to recognize and fix this problem. The typical jump is in the order of 10-20 counts.

    When the user is moving slowly, as in following a line, the mouse will never report a movement greater than one count in x and/or y. It is, therefore, easy to recognize any jumping in this situation and eliminate it.

    When scanning, however, the user may move more quickly, making it difficult or impossible to recognize a jump. After all, if the user is moving at a rate of 40 counts per cycle, and a jump of 10 occurs, it is insignificant.

    As a result, we need to eliminate all the jumps while moving slowly, without eliminating the intentional large movements. Here is the method I use to solve this problem:

    1. If the previous motion is greater that a test value (2 seems to work well), keep the current motion regardless of its size
    2. If the previous motion is less than the test value, and the current motion is greater than the test value, eliminate the current motion.

    The function can be seen in the sample C program in Appendix A.

    2.2 Reading XY Coordinates from the Joystick

    When reading the Game Pad, you must first establish that it is present, how many are present, and which one you wish to use. While it is possible to have several Game Pads present, and to differentiate between them, that is beyond the scope of this article. We assume here that there is only one Game Pad installed.

    Here are the Windows functions for talking to Joystick hardware that you might find useful. All these functions are in the winmm.dll.

    The following link points to the Microsoft documentation on these functions.

    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/mmfunc_494o.asp.

    MMRESULT joyGetDevCaps(UINT_PTR uJoyID,LPJOYCAPS pjc,UINT cbjc);

    UINT joyGetNumDevs(VOID);

    MMRESULT joyGetPosEx(UINT uJoyID, LPJOYINFOEX pji  );

    MMRESULT joySetCapture(HWND hwnd, UINT uJoyID, UINT uPeriod, BOOL

    fChanged );

    MMRESULT joyReleaseCapture( UINT uJoyID );

    MMRESULT joyGetThreshold( UINT uJoyID, LPUINT puThreshold);
    MMRESULT joySetThreshold( UINT uJoyID, UINT uThreshold);

    #define JOYSTICKID1 0

    #define JOYSTICKID2 1

    #define JOY_RETURNBUTTONS 0x80

    #define JOY_RETURNX 1 // axis 1

    #define JOY_RETURNY 2 // axis 2

    #define JOY_RETURNZ 4 // axis 3

    #define JOY_RETURNR 8 // axis 4

    #define JOY_RETURNU 0x10 // axis 5

    #define JOY_RETURNV 0x20 // axis 6

    #define JOY_RETURNPOV 0x40 // Point of View Hat

    #define JOY_RETURNALL (JOY_RETURNX | JOY_RETURNY |

    JOY_RETURNZ | JOY_RETURNR | JOY_RETURNU | JOY_RETURNV |

    JOY_RETURNPOV | JOY_RETURNBUTTONS); // return all data

    #define JOY_RETURN_XYZRB (JOY_RETURNX | JOY_RETURNY |

    JOY_RETURNZ | JOY_RETURNR | JOY_RETURNBUTTONS); // essential flags

    Typedef STRUCT {

      Long dwSize; // size of structure

      Long dwFlags; // flags to indicate what to return

      Long dwXpos; // x position

      Long dwYpos; // y position

      Long dwZpos; // z position

      Long dwRpos; // rudder/4th axis position

      Long dwUpos; // 5th axis position

      Long dwVpos; // 6th axis position

      Long dwButtons; // button states

      Long dwButtonNumber; // current button number pressed

      Long dwPOV; // point of view state

      Long dwReserved1; // reserved for communication between winmm driver

      Long dwReserved2; // reserved for future expansion

    } joyInfoEx;

    2.2.1 Verify that a Game port is installed

    joyInfoEx.dwSize = 52; //size of joyinfoex in bytes

    res = joyGetPosEx(JOYSTICKID1, joyInfoEx); // try to read joystick 1

    if (res == JOYERR_NOERROR) // JOYERR_NOERROR is 0

      {

      joyPresent = TRUE;

      }

    else

      {

      joyPresent = FALSE;

      // if there is no joystick installed, ok, otherwise there must be a problem

      // JOY_NOT_INSTALLED is 165

      if (res != JOY_NOT_INSTALLED)

      }

     

    2.2.2 Read Data from Joystick

    You can poll the joystick, or request Window messages. However, the messages are limited. You will only be notified of the first 4 buttons being pressed, and of changes to X, Y, and Z. If you are using other buttons or axis R, you must use the polling method.

    2.2.2.1 Polling the Joystick

    You can poll in two ways -- Continuous Loop, or Timer.

    The timer works well if the clock speed of the computer is fast enough. Unfortunately, most Windows computers have a clock with only 18.2 clock ticks per second. This is fast enough for rough motion, but not for smooth motion. If you want to go faster, you will need the Continuous Loop method.

    The Continuous Loop is easy to set up. After determining that a Game Pad is present, simply issue a user message called USER_JOY, for example, and when the message is processed, issue another such message. That way you can read the Game Pad data as fast as you wish. You will probably want to process the Game Pad data only every 50 or 100 times the message is issued, however, otherwise the joystick will be much too fast. You will probably also need a user defined value to determine how fast to read the Game Pad. You may also wish to give the user an opportunity to choose which joystick to use for XY motion.

    Regardless of the method of polling, you can choose which data to read (such as Button data, or X, Y values,) or you can just read all the data. This is done by setting the dwFlags parameter.

    joyInfoEx.dwFlags = JOY_RETURN_XYZRB; // flag the essential data

    res = joyGetPosEx(JOYSTICKID1, joyInfoEx); // read the data

    The left joystick data is contained in joyInfoEx.dwXpos and joyInfoEx.dwYpos. The data is a value between 0 and 65535, representing the position of the joystick. A value of about 32767 means that the joystick is nominally centered. Larger values of X result when moving the joystick to the right, and larger values of Y result when moving the joystick downward. Likewise, the data for the right joystick are found in joyInfoEx.dwRpos and joyInfoEx.dwZpos.

    Many users will wish to use R and Z for X, Y motion, and use –Y (negative Y) for Z motion. It is up to the programmer to decide what to do about this.

    Since the numbers are too big for general use, you will want to reduce them to screen pixels with a formula like this:

    X = (joyInfoEx.dwXpos – joy_initial_x) / 1500,

    Dividing by 1500 (for example) slows down the motion. It also creates a "dead" zone in the motion, so that the cursor does not move when there is only a slight offset to the joystick. It is necessary to capture the initial position of the joystick when starting the program. This value does not seem to be set by the calibration program, and can vary considerably from the nominal value.

    A further optional refinement is to apply an acceleration curve to the joystick. This is done today with all computer mice, and can just as easily be applied to the joystick. We suggest applying acceleration to all motions (the mouse wheel (Z) axis, and the X, Y, and Z joystick motions.) A simple table will serve to apply a 2x pseudo-log curve. Note that this curve has the advantage of making a finer slow motion, as well as a faster fast motion. It is so subtle as to be transparent to the user. This table is courtesy of IBM, and has probably already been applied to the mouse X, Y values:

     

    Input

    Output

    0

    0

    1

    1

    2

    1

    3

    3

    4

    6

    5

    9

    N (>5)

    2 x N

     

    One final thing. The resulting values are changes relative to the current screen position. If you do not know the current screen position, you can read it with GetCursorPos().

    GetCursorPos (Current); // find current cursor position

    SetCursorPos (Current.X + X, Current.Y + Y); // set new cursor position

    2.2.2.2 Messages

    If you wish to receive windows messages from the joystick, use joySetCapture to specify the time interval or change of value of X, Y, or Z. Set the timer value in milliseconds, and set the change parameter to False to use the timer.

    JoySetCapture (hwnd, JOUSTICKID1, 54, False); // send joystick data 18.4 times per sec

    To use a change threshold, rather than a timer value, first use joySetThreshold to set the change threshold before calling joySetCapture, and set the change parameter to True.

    JoySetThreshold ( JOYSTICKID1, 1000); // set change threshold to 1000

    JoySetCapture (hwnd, JOYSTICKID1, 54, TRUE); // send joystick data on xyz change

    When using the change criteria, you will not be notified when the R axis changes. Specifying a timer interval only will also not provide you with all the data. The R axis data is not provided by the messages. The only way to get all the data, is to use the polling method.

    Please be aware that with this method, you will not received messages when the R axis changes, or when buttons are pressed (with some exceptions.) Be sure to use joyReleaseCapture to release the joystick when finished.

    To process the data, use the same techniques shown in the previous section.

    2.3 Reading Button Data from the Game Pad

    At the same time you read the XY positions, either by polling or by capturing messages, you get the button data. Make sure the button flag is set when polling, because otherwise the button data is not provided. The data is returned in joyInfoEx.dwButtons variable. One bit is set for each button pressed, starting with bit 0 for Game Button 1. You may not receive all button data when using the Windows messages (not yet tested.)

    3. Reading the Mouse Wheel

    The wheel is quite easy to read in Windows 98, Windows 4, Windows Me, and Windows 2000. It is more difficult with the legacy system Windows 95. There is some support for Windows NT 3.51, but not for earlier versions, apparently. However, information on this is spotty and conflicting.

    Operating System

    Method for Reading Wheel

    Windows 95

    There is no built-in support for the wheel mouse. It is necessary to install special software that comes with the mouse, and create messages and hooks. Hook the message by registering MSH_MOUSEWHEEL message with the RegisterWindowMessage function. Then hook the message with the SetWindowHookEx function. The message MSH_MOUSEWHEEL provides zDelta in wParam. This value is always 120 times the actual movement.

    Windows 98, NT 4.0, Me, 2000

    Built-in support is provided via the message WM_MOUSEWHEEL. zDelta is the high word of wParam. ZDelta is always a multiple of WHEEL_DELTA. In future versions of Windows, WHEEL_DELTA may be a variable determinable by the application. At this time it is fixed at 120.

    Windows NT 3.0

    Not supported.

    Windows NT 3.51

    Support similar to Windows 95

    3.1 Reading Mouse Buttons

    Mouse Buttons are read with the standard mouse functions. All operating systems support the following messages for determining the change of state of the three-button mouse: WM_LBUTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDBLCLK, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_RBUTTONDBLCLK, WM_MBUTTONDOWN, WM_MBUTTONUP and WM_MBUTTONDBLCLK. It is possible to determine the current state of all three buttons when any of these are pressed, by interrogating wParam. With this information, the actual button pressed can be determined. One should add up all the button presses by Or-ing the values 1, 2 and 4 when the left, right or middle button are pressed. On the first button release, the total gives the function number.

    3.2 Programming Stealth 3D Mouse Button Functions

    Following is a list of functions that most programs will need. How these are mapped to the available functions of the mouse are up to each programmer.

     

    4.0 Messages, Masks, and References

    The following tables shows relevant Windows messages and their corresponding hex value for the wheel mouse and X buttons, and the masks for separating the various components from the parameters. The referenced web sites provide valuable information for programmers, including descriptions and examples.

     

    Message

    Hex ID

    Reference

    General

     

    http://msdn.microsoft.com/library/psdk/winui/mousinpt_2jas.htm

    http://msdn.microsoft.com/library/psdk/winui/mousinpt_9oyt.htm

    http://www.microsoft.com/Mouse/intellimouse/sdk/sdkmessaging.htm

    http://msdn.microsoft.com/library/techart/msdn_intmouse.htm

    MSH_MOUSEWHEEL

    Run time

    http://msdn.microsoft.com/library/psdk/winui/mousinpt_2jas.htm

    WM_MOUSEWHEEL

    20A

    http://msdn.microsoft.com/library/psdk/winui/mousinpt_5ir0.htm

    WM_APPCOMMAND

    319

    http://msdn.microsoft.com/library/psdk/winui/sdkmessaging.htm

    WM_TIMER

    113

    Issued when timer expires

    WM_LBUTTONDOWN

    201

     

    WM_LBUTTONUP

    202

     

    WM_LBUTTONDBLCLK

    203

     

    WM_RBUTTONDOWN

    204

     

    WM_RBUTTONUP

    205

     

    WM_RBUTTONDBLCLK

    206

     

    WM_MBUTTONDOWN

    207

     

    WM_MBUTTONUP

    208

     

    WM_MBUTTONDBLCLK

    209

     

     

    Mask

    Value

    Function

    FAPPCOMMAND_MOUSE

    10000000

    Lparam mask for use with WM_APPCOMMAND to find if user clicked a mouse button.

    MK_LBUTTON

    1

    Mask for L button down. Applies to lParam with appcommand, and to wParam for all button down/up messages

    MK_RBUTTON

    2

    Mask for R button down as above

    MK_MBUTTON

    10

    Mask for M button down as above

    MK_XBUTTON1

    1000

    Mask for X1 down. Applies to appcommand with lParam, and all button down/up messages under Windows 2000 only with wParam.

    MK_XBUTTON2

    2000

    Mask for X2 down. Applies to same as above.

     

    5. Windows Functions

    The following table shows relevant Windows functions which might be needed to read the wheel mouse and Game Pad. They are all standard Windows functions.

     

    Function

    Purpose

    GetSystemMetrics (75)

    Returns 1 if wheel mouse supported. SM_MOUSEWHEELPRESENT = 75

    RegisterWindowMessage

    Returns run-time identifier

    SetWindowHookEx

    Hooks window message to application program

    GetCursorPos

    Gets the cursor position in pixels from top left

    SetCursorPos

    Sets the cursor position in pixels from top left

    SetTimer

    Starts a timer

    KillTimer

    Stops a timer

    GetKeyStatus (key)

    Returns negative if virtual key is pressed (the Shift key is hex 10, Ctrl is hex 11, and Alt is hex 12)

     

    Please contact the author if you have any questions about programming or operation of the mouse, or if you have any constructive criticism of the Stealth J-Mouse. We want to know how to make it work better for you.

     

    3 July, 2002

    Timothy Roberts, Development Director

    ABC Software Developers

    Email: info@stealth3dmouse.com

     

     

    Appendix A – Sample Program

    There is no sample program showing the joystick interface at this time.

    See the Stealth 3D Mouse Development Guide for a sample of programming the standard mouse.

    [Home] [Documentation] [Top]