#ifndef OBJ_NAME #ifdef TESTMODE #pragma module VMSTESTJOY "X-1" #else #pragma module VMSJOYSTICK "X-1" #endif #else #pragma module OBJ_NAME "X-1" #endif /* ******************************************************************************* * ** * © Copyright 2005 Hewlett-Packard Development Company, L.P. ** * ** * Confidential computer software. Valid license from HP and/or ** * its subsidiaries required for possession, use, or copying. ** * ** * Consistent with FAR 12.211 and 12.212, Commercial Computer Software, ** * Computer Software Documentation, and Technical Data for Commercial ** * Items are licensed to the U.S. Government under vendor's standard ** * commercial license. ** * ** * Neither HP nor any of its subsidiaries shall be liable for technical ** * or editorial errors or omissions contained herein. The information ** * in this document is provided "as is" without warranty of any kind and ** * is subject to change without notice. The warranties for HP products ** * are set forth in the express limited warranty statements accompanying ** * such products. Nothing herein should be construed as constituting an ** * additional warranty. ** * ** ******************************************************************************* * * TITLE: * * VMSJOYSTICK * * FACILITY: * * [USB] Universal Serial Bus * [SYSHLP.EXAMPLES] Destined to be an example * * ABSTRACT: * * This module handles a USB Joystick. It is used as the joystick * code for OpenGLUT on OpenVMS Alpha and I64. It can also be built * as a standalone "test" application. Or it can be built as a * simple library object to talk to a USB Joystick. You don't need * this code to talk to the joystick, you can simply use the QIO * interface, but this code can be used both as a easy way to use * the joystick without a lot of programming, or at least as a way * to see how the QIOs are used. To build it: * * CC VMSJOYSTICK/DEFINE=TESTMODE - will build the test version * CC VMSJOYSTICK/DEFINE=VMS_GLUT - will build the GLUT object * CC VMSJOYSTICK - will build a generic object * * OK. The joystick and game pad for USB are pretty standard * HID (Human INterface Devices). They provide some number of * axis, buttons and hat switch. A minimum joystick is simply * a X and Y axis with perhaps a button or two. But they can * be as complex as a "spaceball" or contain multiple buttons, * hat switches, and axis - sliders, wheels, dials, etc. Low * end steering wheels also often show up as joysticks or * game pads. * * The device driver for the joystick/gamepad is AGDRIVER * (named for Andy G, who is a aircraft simulator hound and * did the original GLUT hack). AGDRIVER is capable of * describing and returning data for pretty much anything * that can describe itself as a joystick or gamepad to USB. * * Force Feedback is not implemented at this time. * * It can handle up to 32 (each) of the following axis types: * * X * Y * Z * RX (rotation around the X axis) * RY (rotation around the Y axis) * RZ (rotation around the Z axis) * VX (vector in the X direction) * VY (vector in the X direction) * VZ (vector in the X direction) * VBRX (relative vector in the X direction) * VBRY (relative vector in the X direction) * VBRZ (reltive vector in the X direction) * VNO (undirected vector) * SLIDER (A slider control) * DIAL (A knob) * WHEEL (Like a knob, but more like a thumbwheel ;-) * * In addition, up to 32 hat switches can be supported - a hat switch * is like a digital joystick that reports one of 8 values depending * on which way it is pressed: * * 8 1 2 * * 7 0 3 * * 6 5 4 * * Think: Atari Joystick. Nowadays they tend to be little joyticks * on a Playstation (in digital mode) or little thumbswitches on cool * joysticks. * * Up to 64 discrete buttons can be supported in addition to the * hat switch. A typical gaming joystick will have something like * 12 to 26 buttons and a hat switch (sometimes hat switches are * reported as normal buttons instead of hat switches). * * Using this code can be simple or complex. The most simple way * to use this code is to: * * JOY_CONTEXT *joy = 0; * * status = vmsOpenJoystick (&joy); * * vmsStartJoystick (&joy, JOYSTICK_ASYNC, input_callback); * * ... * * vmsStopJoystick (joy); * * Where input_callback looks like: * * void input_callback(unsigned int buttons, int x, int y, int, z, int rz, int throttle, JOY_CONTEXT *joy) * * This routine will be called every time a button or axis changes. * By default this will just return the logical, unaltered/unscaled data from the * joystick. * * You might also want to call: * * vmsQueryJoystick (&num_buttons, &num_axes, &valid_axes, &joy) * * To find out about the number of buttons, axes, and which axis are valid. * * You can pretty much count on X and Y. RZ is a twisting joystick. * The throttle is either a slider or sometimes is the "Z" axis. This * is really vendor specific (see below). * * The JOY_CONTEXT structure contains *all* of the data, plus all of the information * about each axis. So more sophisticated code can just mine this data * out directly. * * Data can be signed or unsigned, and the neutral center position * might be at zero, or at 1/2 the full range (unsigned data). This * information is available in the per-axis data for each (see VMSJOYSTICK.H or * AGDEF.H and the joyScale() code below). * * You can ask that the data be scaled (and can explicitly scale data * by calling vmsJoyScale). But a simpler way for the standard axes * is to enable scaling... * * status = vmsOpenJoystick (&joy); * * vmsJoySetScale(joy, JOYSTICK_SCALE_ENABLE | JOYSTICK_SCALE_CENTER, 1000, JOYSTICK_X_AXIS); * vmsJoySetScale(joy, JOYSTICK_SCALE_ENABLE | JOYSTICK_SCALE_CENTER, 1000, JOYSTICK_Y_AXIS); * vmsJoySetScale(joy, JOYSTICK_SCALE_ENABLE | JOYSTICK_SCALE_CENTER, 1000, JOYSTICK_Z_AXIS); * vmsJoySetScale(joy, JOYSTICK_SCALE_ENABLE | JOYSTICK_SCALE_CENTER, 1000, JOYSTICK_RZ_AXIS); * vmsJoySetScale(joy, JOYSTICK_SCALE_ENABLE, 1000, JOYSTICK_THROTTLE_AXIS); * * Says to enable scaling, and to center the data - so the axis will return * -1000 to 1000 with 0 at the center of travel. * * You can also invert the data with JOYSTICK_SCALE_INVERT, or have the data returned * as a positive 0 - nnnn range (just enable scaling but don't set CENTER). * * And that is pretty much all you need to know to do basic programming. * * For more in depth understanding, this module and VMSJOYSTICK.H are the * place to begin. * * Joystick Definition files (JOYSTICK.DAT): * * But wait. There is even more help for you here, vendor/device joystick data can be provided * so that code doesn't have to special case things too much. Here's the problem: You've * written a GLUT application to fly an airplane, but you don't know what joystick might * get plugged in... * * Joystick definition file. This file allows a joystick to be "normalized" against some * arbitrary "norm". In this case, the author of the code had a Saitek Cyborg EVO Wireless * joystick... so that is the "norm" for our purposes. By placing a definition for a * specific vendor/model of joystick, an application may not need any "special" logic for * joystick differences, or at least can use a standard way of falling back when a feature * (like an axis) is missing. * * A really good joystick (think: Saitek X45) has at least 5 axes: X, Y, Z, RZ and a throttle. * This is great, now you can turn, pitch, roll, yaw, and change speed like a real airplane. * But someone plugs in a really simple joystick - it just has X and Y axis. Well, you can't * do all the spiffy things above, but you might want Y to be substituted for the throttle * instead of pitch. That is where the joystick definition file comes in. In this file, you * specify which "real" axis you want to map to the X, Y, Z, RZ, Throttle data that is returned. * * So for the X45, you would do: * * X = X(0) * Y = Y(0) * Z = Z(0) * RZ = RZ(0) * THROTTLE = SLIDER(0) * * (Note the (0) means the first instance of the axis). * * For a dumb X/Y only device you might set: * * X = X(0) * Y = NONE * Z = NONE * RZ = NONE * THROTTLE = Y(0) * * It gets even more important for in-between devices. Say the Cyborg EVO, which is a 4 * axis joystick. It reports X, Y, RZ, and Z. Where Z is a seperate up/down lever on the * back of the joystick. You might decide that throttle is more important than Z (after * all you can use speed and pitch to change height - unless you are a helicopter), so you * might map this: * * X = X(0) * Y = Y(0) * Z = NONE * RZ = RZ(0) * THROTTLE = Z(0) * * We will try to apply some common sense fallbacks for undefined joysticks (like above) * but there is nothing like being able to be explicit. * * In addition, the other probem is buttons. Say you want a button that fires a missile - * but each joystick reports their buttons as simply a collection of buttons. You can also * in the joystick definition file, cause the buttons to be remapped. So if on one joystick * button 4 is the one you want, and on another button 8 is the one you want, you can have * them both mapped to (for example) button 1 - without vendor-specific logic in the code - * or your code itself knowing about it. * * The joystick definition file is first lokked for as a logical JOY$DEFINITION, then in * []joystick.dat, then sys$login:joystick.dat, and then sys$manager:joy$definition.dat * Failing that, there are a handful of known ones baked into the vmsjoystick.h file * * The details of joystick definitions is: * * General format: * * JOYSTICK_DEFINITION "Vendor Name", "Product Name"; * VENDOR_ID ; * PRODUCT_ID ; * FLAGS * X_AXIS []; * Y_AXIS []; * Z_AXIS []; * RZ_AXIS []; * THROTTLE_AXIS []; * BUTTON_MAP n, n, n, n, n, n, n, n, ... * END_DEFINITION; * * The flags are one of the following, and allow the axis to be inverted. If there are * no flags to set, the keyword can be ommitted. * * FLAGS X_INVERT | Y_INVERT | Z_INVERT | RZ_INVERT | THROTTLE_INVERT | FORCE_FULL_SCALE * * Note: FORCE_FULL_SCALE will cause any axis that does not have calibration data in the * driver to be forced to full scale based on the field width (that is, a 12-bit * number, or a 10-bit, or 8-bit or whatever is reported from the USB driver). * Some joysticks report bogus logical min/max values, and this is a quick way * to "fix" it. * * There are 5 standard axes: X, Y, Z, RZ, THROTTLE - but these may not be present, and in * the case of the THROTTLE - there is no standard axis. Each axis can be mapped to a specific * USB defined axis: X, Y, Z, RX, RY, RZ, VX, VY, VZ, VBRX, VBRY, VBRZ, VNO, SLIDER, DIAL, * or wheel. There can be multiple instances of an axis (although, I think Windows has trouble * with it - since I've not seen one yet) - so following the axis type is an instance number. * So, for a typical joystick that reports a single instance of SLIDER that you want to map to * the THROTTLE: * * THROTTLE_AXIS SLIDER[0]; * * If no USB axis will map to a standard axis, then NONE can be specified (or the axis * definition ommitted, which will default to NONE). * * Buttons: * * 28 buttons can be remapped. The first hat switch (when present) is in the upper 4 bits * of th button. The other 28-bits can be routed by using the flag BUTTON_MAP keyword. * Each value following the BUTTON_MAP keyword cooresponds to the input bit position, and * the value cooresponds to the final button bit position. If button mapping is enabled * *only* the buttons defined in the button map will appear in the button data * (along with the HAT). * * BUTTON_MAP 0, 5, 6, 3, 4, 2 * * will map button 0 to 0, 1 to 5, 3 to 6, 4 to 3, 5 to 4, 6 to 2. So a button input of * 1 will set bit 1, but a button input of 0x02 will result in 0x20 (that is, bit * position 1 (0x02) will become bit position 5 (0x20), etc. * * NORMS: * * Joystick normals: * * X Absolute data: Full Left 0, Full Right MAX, Center (MAX+1)/2 (ex: 0, 1023, 512) * Centered: Full Left +MAX, Full Right -MAX, Center 0 * * Y Absolute data: Full Forward 0, Full Back MAX, Center (MAX+1)/2 (ex: 0, 1023, 512) * Centered: Full Forward +MAX, Full Back -MAX, Center 0 * * Z Absolute data: Full Up 0, Full Down MAX, Center (MAX+1)/2 (ex: 0, 1023, 512) * Centered: Full Up +MAX, Full Down -MAX, Center 0 * * RZ Absolute data: Full Twist Left 0, Full Twist Right MAX, Center (MAX+1)/2 (ex: 0, 1023, 512) * Centered: Full Twist Left +MAX, Full Twist Right -MAX, Center 0 * * THROTTLE Absolute data: Full Forward 0, Full Back MAX, Center (MAX+1)/2 (ex: 0, 1023, 512) * Centered: Full Forward +MAX, Full Back -MAX, Center 0 * * The norms for buttons: * * TRIGGER Bit 0 * FIRE CENTER Bit 1 * FIRE LEFT Bit 2 * FIRE RIGHT Bit 3 * AUX FIRE LEFT Bit 4 * AUX FIRE RIGHT Bit 5 * F1 Bit 6 * F2 Bit 7 * F3 Bit 8 * F4 Bit 9 * * What does that mean in reality? Not much except for the "trigger" is standard. * * Lastly... * * In addition, this code supports calibration data. This data can be used * to compensate for inaccuracy in analog devices - where the neutral position * does not read zero, or where you cannot get the full range of data that * the logical range indicates (or where your scaling isn't integral, and you * want to "fudge" things to make sure you get full sale readings -- this can * happen when you try to scale things to non multiples/divisors of the input * range). * * This code looks for a calibration file (a logical name JOY$CALIB) and then * for calib.dat in the current directory, and then in the login directory, * and then in sys$manager:joy$calib.dat. * * // * // Define calibration for My Joystick * // * * Calibration records are of the format: * * calibration "My Joystick" { * * device_name = nnn; * vendor_id = nnn; * product_id = nnn; * * axis NAME [nn] { * center = nnn; * cmin = nnn; * cmax = nnn; * }; * * axis NAME [nn] { * center = nnn; * cmin = nnn; * cmax = nnn; * }; * } end_calibration; * * A lot of this is just making it pretty. You can use C-like comments * in it, and a C-like structure definition (C-like, not valid-C). It can * also be written: * * CALIBRATION; * DEVICE_NAME nnn; * VENDOR_ID nnn; * PRODUCT_ID nnn; * AXIS NAME nn; * CENTER nnn; * CMIN nnn; * CMAX nnn; * AXIS NAME nn; * CENTER nnn; * CMIN nnn; * CMAX nnn; * END_CALIBRATION; * * This is the actual simple syntax. Command followed by parameters, and a * closing terminator. The ; terminator is needed to terminate each command * (the "{" can also be used) so that commands can span lines. So... * * AXIS SLIDER * 1; * * will work just fine. * * Since it is possible to have multiple instances of an axis (like multiple sliders) * the [nn] says which one - typically this will just be a single instance, and 0. * The valid axis NAME's are: X, Y, Z, RX, RY, RZ, VX, VY, VZ, VXBR, VXBY, VXBZ, VNO, * SLIDER, DIAL, WHEEL. * * The vendor/device is the USB ID's as returned by the joystick. The device name * is the VMS device - like AGA0. This allows the code to match the vendor/product * and device name against the current device - so you can calibrate multiple sticks, * or multiple types. * * All numbers are decimal. * * The values are written to the device driver in a SETMODE call, and will be * persistant until the joystick is hot plugged or the system is rebooted, or * they are overwritten by another SETMODE to set calibration. * * AUTHOR: * * Fred Kleinsorge, July 2005 * (somewhat cribbed from the original version done for GLUT -- Andy Goldstein) * * REVISIONS: * * X-1 FGK Fred Kleinsorge 30-July-2005 * Initial version * */ /* Include files */ #include /* String descriptors */ #include /* VMS I/O function codes */ #include /* C signals */ #include /* System error codes */ #include /* System routines */ #include /* Stand I/O routines */ #include /* Stand UNIX routines */ #include /* String functions */ #include #ifndef _toupper #define _toupper(c) (((c) >= 'a' && (c) <= 'z') \ ? (c) & 0xDF : (c)) #endif #ifdef VMS_GLUT #include "glutint.h" #endif #include "vmsjoystick.h" /* * * vmsJoyDebug -- causes some print statement used when debugging * vmsJoyVerbose -- causes some informational messages * */ static vmsJoyDebug = 0x0; #ifndef VMS_GLUT static vmsJoyVerbose = 0x1; #else static vmsJoyVerbose = 0x0; #endif /* * Structures and commands used for the CALIBRATION * file parse * */ typedef struct command_table_entry { int value; char *name; int match; char *description; struct command_table_entry *sub_commands; } _COMMAND_TABLE_ENTRY; enum { calibSTART = 0x000001, calibDEVICE = 0x000002, calibVENDOR = 0x000004, calibPRODUCT = 0x000008, calibAXIS = 0x000010, calibCENTER = 0x000020, calibCMIN = 0x000040, calibCMAX = 0x000080, calibEND = 0x000100, calibEXIT = 0x000200, calibEOT = -1 }; static _COMMAND_TABLE_ENTRY calib_commands[] = { calibSTART, "CALIBRATION_DATA", 3, "Start of a calibration record", 0, calibDEVICE, "DEVICE_NAME", 3, "VMS device name", 0, calibVENDOR, "VENDOR_ID", 3, "Vendor ID", 0, calibPRODUCT, "PRODUCT_ID", 3, "Product ID", 0, calibAXIS, "AXIS", 3, "Axis TYPE [n] definition", 0, calibCENTER, "CENTER_OFFSET", 3, "Center offset", 0, calibCMIN, "CMIN", 3, "Calibrated minimum", 0, calibCMAX, "CMAX", 3, "Calibrated maximum", 0, calibEND, "END_CALIBRATION", 3, "End of calibration record", 0, calibEXIT, "EXIT_CALIBRATION", 3, "End of calibration file", 0, calibEOT, "", 0, 0, 0 }; enum { joydefSTART = 0x000001, joydefVENDOR_ID = 0x000004, joydefPRODUCT_ID = 0x000008, joydefFLAGS = 0x000010, joydefX = 0x000020, joydefY = 0x000040, joydefZ = 0x000080, joydefRZ = 0x000100, joydefTHROTTLE = 0x000200, joydefBUTTON = 0x000400, joydefEND = 0x001000, joydefEXIT = 0x010000, joydefEOT = -1 }; static _COMMAND_TABLE_ENTRY joydef_commands[] = { joydefSTART, "JOYSTICK_DEFINITION", 3, "Start of a joystick definition record", 0, joydefVENDOR_ID, "VENDOR_ID", 3, "Vendor ID", 0, joydefPRODUCT_ID, "PRODUCT_ID", 3, "Product ID", 0, joydefFLAGS, "FLAGS", 3, "Device flags", 0, joydefX, "X_AXIS", 1, "X Axis definition", 0, joydefY, "Y_AXIS", 1, "X Axis definition", 0, joydefZ, "Z_AXIS", 2, "Z Axis definition", 0, joydefRZ, "RZ_AXIS", 2, "RZ Axis definition", 0, joydefTHROTTLE, "THROTTLE_AXIS", 2, "Throttle Axis definition", 0, joydefBUTTON, "BUTTON_MAP", 3, "Button Map", 0, joydefEND, "END_DEFINITION", 3, "End of definition", 0, joydefEXIT, "EXIT_DEFINITION", 3, "End of definition file", 0, joydefEOT, "", 0, 0, 0 }; static _COMMAND_TABLE_ENTRY joydef_axis_flags[] = { JOYSTICK_NO_AXIS, "NONE", 3, "No Axis", 0, JOYSTICK_X_AXIS, "X_AXIS", 1, "X Axis", 0, JOYSTICK_Y_AXIS, "Y_AXIS", 1, "Y Axis", 0, JOYSTICK_Z_AXIS, "Z_AXIS", 1, "Z Axis", 0, JOYSTICK_RX_AXIS, "RX_AXIS", 2, "RX Axis", 0, JOYSTICK_RY_AXIS, "RY_AXIS", 2, "RY Axis", 0, JOYSTICK_RZ_AXIS, "RZ_AXIS", 2, "RZ Axis", 0, JOYSTICK_RX_AXIS, "VX_AXIS", 2, "RX Axis", 0, JOYSTICK_RY_AXIS, "VY_AXIS", 2, "RY Axis", 0, JOYSTICK_RZ_AXIS, "VZ_AXIS", 2, "RZ Axis", 0, JOYSTICK_VBRX_AXIS, "VBRX_AXIS", 4, "VBRX Axis", 0, JOYSTICK_VBRY_AXIS, "VBRY_AXIS", 4, "VBRY Axis", 0, JOYSTICK_VBRZ_AXIS, "VBRZ_AXIS", 4, "VBRZ Axis", 0, JOYSTICK_VNO_AXIS, "VNO_AXIS", 3, "VNO Axis", 0, JOYSTICK_SLIDER_AXIS, "SLIDER_AXIS", 3, "Slider Axis", 0, JOYSTICK_WHEEL_AXIS, "WHEEL_AXIS", 3, "Wheel Axis", 0, JOYSTICK_DIAL_AXIS, "DIAL_AXIS", 3, "DIal Axis", 0, joydefEOT, "", 0, 0, 0 }; static _COMMAND_TABLE_ENTRY joydef_flags[] = { JOYSTICK_DEF_INVERT_X, "X_INVERT", 3, "Invert X Axis", 0, JOYSTICK_DEF_INVERT_Y, "Y_INVERT", 3, "Invert X Axis", 0, JOYSTICK_DEF_INVERT_Z, "Z_INVERT", 3, "Invert X Axis", 0, JOYSTICK_DEF_INVERT_RZ, "RZ_INVERT", 3, "Invert X Axis", 0, JOYSTICK_DEF_INVERT_THROTTLE,"THROTTLE_INVERT", 3, "Invert Throttle", 0, JOYSTICK_DEF_BUTTON_MAP, "BUTTON_MAP", 3, "Use Button Map", 0, JOYSTICK_DEF_FULL_SCALE, "FULL_SCALE", 3, "Force Full Scale", 0, joydefEOT, "", 0, 0, 0 }; #define STRING_MAX 8192 #define UPCASE 0x0001 #define STRIP_LEAD_WHITESPACE 0x0002 #define STRIP_TRAIL_SPACES 0x0004 #define STRIP_QUOTES 0x0008 #define COMPRESS_SPACES 0x0010 #define COMPRESS_TABS 0x0020 #define STRIP_COMMENTS 0x0040 #define STRIP_C_COMMENTS 0x0080 #define STRIP_EQUALS 0x0100 #define STRIP_CLOSE_BRACKETS 0x0200 #define STRIP_OPEN_BRACKETS 0x0400 #define STRIP_SQUARE_BRACKETS 0x0800 #define STRIP_COMMA 0x1000 #define STRIP_SEMI_COMMENTS 0x2000 #define STRIP_COLON 0x4000 #define STRIP_OR 0x8000 /* * Routine: edit_string - static scope * * static int edit_string (char *new_string, char *old_string, const int max_destination, int *comment, int flags) * * Abstract: * * This is a utility routine to format a input string. Formatting * is driven by the flags input: * * STRIP_LEAD_WHITESPACE - Removes all leading spaces and tabs * STRIP_TRAIL_SPACES - Removes all trailing spaces and tabs * STRIP_QUOTES - Removes any quotes * STRIP_COMMENTS - Terminates string on the "!" character * STRIP_SEMI_COMMENTS - Same as STRIP_COMMENTS, but use ";" as comment character * STRIP_C_COMMENTS - Strip C-like comments - "//", also "#" preprocessor, and /* .... */ /* COMPRESS_TABS - Converts the TAB character to SPACE * COMPRESS_SPACES - Converts multiple SPACEs into a single SPACE * UPCASE - Convert all unquoted strings to upper case * STRIP_EQUALS - Strip "=" * STRIP_OPEN_BRACKETS - Strip "{" * STRIP_CLOSE_BRACKETS - Strip "}" * STRIP_SQUARE_BRACKETS - Strip "[" and "]" * STRIP_COLON - Strip ":" * STRIP_COMMA - Strip "," * * The double quote character (") is used to prevent conversion of all * or part of the string. When the first (") is encountered, all conversion * of the string is turned off until the next (") character, or the end * of string is reached. The STRIP_QUOTE flag allows the quotes to also be * removed (but they still have the same effect, they just are not copied * to the destination string). * * Any control character terminates the string, as does the DEL character. * * The destination string is zero terminated, and it's length is returned. * * Note that a "/*" sets return value in *comment so that subsequent calls will continue * to throw away data until we see a end comment * * Inputs: * * new_string - address of a buffer for the new string * old_string - address of the input string * max_destination - Maximum destination size * comment - State of comment (in/out 1/0) * flags - flag for stripping * * Outputs: * * The new string is written. * * Side Effects: * * none * * Returns: * * The length of the new string. * */ static int edit_string(char *new_string, char *old_string, const int max_destination, int *comment, int flags) { #define xTAB 0x09 #define xDELETE 0xFF char data, prev = 0, *in_string = old_string; int length = 0, i, in_quotes, spx = 0; /* * Initial destination is null */ *new_string = 0; /* * Strip leading whitespace */ if (flags & STRIP_LEAD_WHITESPACE) { for (i = 0; i < STRING_MAX-1; i += 1) { data = *old_string; if ((data == ' ') || (data == xTAB)) old_string++; else break; } } in_quotes = 0; data = 0; for (; i < STRING_MAX-1; i += 1) { prev = data; data = *old_string++; /* * Anything in the control range except TAB terminates * the string... so does the DELETE character. */ if (((!(data & 0x60)) && (data != xTAB)) || (data == xDELETE)) break; /* * If we are stripping C-like comments /* .... then * we just eat characters until we see once we see a /* * we need to save the context across calls however! */ if (flags & STRIP_C_COMMENTS) { if (*comment == 1) { if ((data == '/') && (prev == '*')) { *comment = 0; } continue; } } /* * Double quote flips the quote state. And if we are * stripping the quotes, then we just remove them here. */ if (data == 0x22) { if (in_quotes) in_quotes = 0; else in_quotes = 1; if (flags & STRIP_QUOTES) continue; } /* * If we are in quotes, then just eat the data without * checking it. */ if (in_quotes) { *new_string++ = data; length += 1; if (length == max_destination-1) { *new_string++ = 0; return (length); } spx = 0; } else { /* * If it's a tab, see if we want to make it a space */ if (data == xTAB) if (flags & COMPRESS_TABS) data = ' '; /* * Strip "//" comments */ if (data == '/') { if (prev == '/') { if (flags & STRIP_C_COMMENTS) break; } } /* * Strip "/*" comments */ if (data == '*') { if (prev == '/') { if (flags & STRIP_C_COMMENTS) { data = prev = 0; length -= 1; *new_string--; *comment = 1; continue; } } } /* * Strip "#" C preprocessor stuff */ if (data == '#') if (flags & STRIP_C_COMMENTS) break; /* * Strip "!" comments */ if (data == '!') if (flags & STRIP_COMMENTS) break; /* * Strip ";" comments */ if (data == ';') if (flags & STRIP_SEMI_COMMENTS) break; /* * Strip "}" */ if (data == '}') if (flags & STRIP_CLOSE_BRACKETS) continue; /* * Strip "{" */ if (data == '{') if (flags & STRIP_OPEN_BRACKETS) continue; /* * Strip ":" */ if (data == ':') if (flags & STRIP_COLON) continue; /* * Strip "[" (replace with space) */ if (data == '[') if (flags & STRIP_SQUARE_BRACKETS) data = ' '; /* * Strip "|" (replace with space) */ if (data == '|') if (flags & STRIP_OR) data = ' '; /* * Strip "]" (replace with space) */ if (data == ']') if (flags & STRIP_SQUARE_BRACKETS) data = ' '; /* * Strip "=" (replace with space) */ if (data == '=') if (flags & STRIP_EQUALS) data = ' '; /* * Strip "," (replace with space) */ if (data == ',') if (flags & STRIP_COMMA) data = ' '; /* * Do space compression */ if (data == ' ') { if (spx) { if (flags & COMPRESS_SPACES) continue; } spx += 1; } else spx = 0; /* * And finally, upcase it. */ if (flags & UPCASE) *new_string++ = _toupper(data); else *new_string++ = data; length += 1; if (length == max_destination-1) { *new_string++ = 0; return (length); } } } /* * Null string, return */ if (length == 0) return 0; /* * Back off trailing spaces. */ if (flags & STRIP_TRAIL_SPACES) { while (spx) { *new_string--; spx -= 1; length -= 1; } } /* * Null terminate the string. */ *new_string++ = 0; /* * Return the string length */ return (length); } /* * Routine: tokenize_input - static scope * * static int tokenize_input (char *input_string, char **tokens, int max_tokens, int *seperators) * * Abstract: * * This routine takes a input string, and returns an array of pointers * into the string. Each component of the string is null terminated. * * The parse is simple... It looks for whitespace as seperators. Each * item seperated by whitespace is a new token. * * A list of seperators can be passed in a integer array. These are * typically things like comma, paren, equals, etc. When one of these * characters are found, they act as whitespace, and a token will be * created pointing to the seperator in the list (not in the string). * This is done so that spaces need not be included (and the resulting * lack of a way to terminate the token). By passing the seperators in * an integer array, it allow both the seperator and terminating null to * be pointed to. * * We return the non-space seperators so that the caller can do validation * on the input. * * The double quote (") starts a string that won't be checked for whitespace * or special characters. Only another double quote or the end of the * string will terminate it. * * Typically the string will be preprocessed prior to calling this routine * to compress spaces, convert tabs to spaces, trim comments, upcase, etc. * * An example token parse using seperators ")", "(", and "=": * * Each is a /token =(fred) "This is a single token" * * token[0] = "Each" * token[1] = "is" * token[2] = "a" * token[3] = "/token" * token[4] = "=" ** * token[5] = "(" ** * token[6] = "fred" * token[7] = ")" ** * token[8] = "This is a single token" * * ** Indicates pointers to the seperator array entries, all others * point to the input substring addresses. If the actual input * string were examined, each of the tokens is terminated by a * null - i.e. (using the % character to represent a null...): * * Each%is%a%/token%=(fred% "This is a single token% * ! ! ! ! ! ! * 1 2 3 4 6 8 * * Inputs: * * input_string - address of a buffer of the string * tokens - address of an array of character pointers * max_tokens - max token count * seperators - an array of seperators * * Outputs: * * The array is loaded, the input string has nulls in it, the count is returned * * Side Effects: * * none * * Returns: * * The number of tokens found. * */ static int tokenize_input(char *input_string, char **tokens, int max_tokens, int *seperators) { __int64 length, i, j, in_quotes = 0, found = 0, in_token = 0, token_start = 0, current_token = 0; /* * Get the length of the input string */ length = strlen(input_string); for (i = 0; i < length; i += 1) { /* * Handle quoted strings. Only a double quote terminates */ if (in_quotes) { if (input_string[i] == '"') { in_token = 0; in_quotes = 0; tokens[current_token++] = &input_string[token_start]; input_string[i] = 0; } } else { /* * Find non-space seperators... */ found = 0; j = 0; while (seperators[j]) { if (input_string[i] == seperators[j]) { if (in_token) { tokens[current_token++] = &input_string[token_start]; } /* * Each seperator is contained in a integer element, * which also allows the seperator to be null terminated */ tokens[current_token++] = (char *) &seperators[j]; input_string[i] = 0; in_token = 0; found = 1; break; } j += 1; } if (!found) { switch (input_string[i]) { /* * Whitespace is stripped, and terminates a token */ case ' ': if (in_token) { tokens[current_token++] = &input_string[token_start]; input_string[i] = 0; in_token = 0; } break; /* * Double quotes starts a quoted string */ case '"': in_quotes = 1; token_start = i+1; break; /* * Anything else just goes into the token... */ default: if (!in_token) { in_token = 1; token_start = i; } break; } } } } /* * Terminate any current token, and return the count */ if (in_token) { tokens[current_token++] = &input_string[token_start]; } return (current_token); } /* * Routine: get_next_item - static scope * * static int get_next_item ( FILE *in, char *buffer_in ) * * Abstract: * * This routine reads from the input file and filters the input * data. It terminates when it reaches a ";" or a "{" * * Inputs: * * in A FILE pointer * buffer_in The address of string where the output * is returned * * Outputs: * * Success 1 * End of File 0 * Unterminated end of file -1 * buffer_in Result (command, can be multi-line) * * Side Effects: * * none * * Returns: * */ static int get_next_item( FILE *in, char *buffer_in ) { char buffer_tmp[STRING_MAX]; int i = 0, j, comment = 0, len = STRING_MAX-1; char *s; while (1) { s = fgets(&buffer_tmp[0], len, in); if (!s) { if (vmsJoyDebug & 0x08) printf(" fgets returns END_OF_FILE\n"); if (i == 0) return 0; else return -1; } j = edit_string(&buffer_in[i], buffer_tmp, len, &comment, STRIP_LEAD_WHITESPACE | STRIP_C_COMMENTS | STRIP_CLOSE_BRACKETS | STRIP_COLON | STRIP_SQUARE_BRACKETS | STRIP_EQUALS | STRIP_COMMA | STRIP_OR | COMPRESS_TABS | COMPRESS_SPACES | UPCASE); if (vmsJoyDebug & 0x8) printf(" edit_string returns <%s>\n", &buffer_in[i]); if (!j) continue; for (j = i; j < STRING_MAX; j += 1) { if ((buffer_in[j] == ';') || (buffer_in[j] == '{')) { if (vmsJoyDebug & 0x08) printf(" Found end of item <%c>\n", buffer_in[j]); buffer_in[j] = 0; if (vmsJoyDebug & 0x8) printf(" item is <%s>\n", &buffer_in[0]); return j; } if (buffer_in[j] == 0) { i = j; len = (STRING_MAX-1) - j; if (len <= 0) { if (vmsJoyVerbose & 1) printf("Command buffer too small\n"); return -1; } break; } } } } /* * Routine: calib_actions - static scope * * static int calib_actions (JOYSTICK_LIMIT_DATA *calib, char **tokens, int num_tokens, int *found, * int vendor, int product, char *devname, int new) * * * Abstract: * * This parses a command extracted by get_next_item. The * format then it gets here is a tokenized form where each * item in the command is in the tokens[] array. The first * element is the command, followed by the parameters fo * the command. * * The command is looked up and then dispatched. * * The parse here is for a CALIBRATION record, of the form: * * calibration { * device_name = nnn; * vendor_id = nnn; * product_id = nnn; * axis NAME [nn] { * center = nnn; * cmin = nnn; * cmax = nnn; * }; * axis NAME [nn] { * center = nnn; * cmin = nnn; * cmax = nnn; * }; * } END_CALIBRATION; * * This will be sent to us a command at a time as * tokens: * * CALIBRATION * DEVICE_NAME nnn * VENDOR_ID nnn * PRODUCT_ID nnn * AXIS NAME nn * CENTER nnn * CMIN nnn * CMAX nnn * AXIS NAME nn * CENTER nnn * CMIN nnn * CMAX nnn * END_CALIBRATION * * We keep some static data for context, since this is * a collection that pertains to the DEVICE/VENDOR/PRODUCT * and we have to match the inputs of those in the call to * those in the file. * * The file can contain as many of these definitions as * someone wants to put in the file - duplicates of the same * DEVICE/VENDOR/PRODUCT will overwrite previous data. * * A EXIT_CALIBRATION command will terminate the file * even if we are not at the end of file. * * Inputs: * * calib - A LIMIT data structure for the results * tokens - The token array * num_tokens - The number of tokens in the array * found - Returns a 1 if a calibration record was found for the DEVICE/VENDOR/PRODUCT * vendor - USB Vendor ID * product - USB Product ID * devname - Device name (like AGA0 or AGA1) * new - Forces an init of the static data * * Outputs: * * calib data filled in * found is set to 1 if a VENDOR/DEVICE/PRODUCT is matched * * Side Effects: * * none * * Returns: * * Continue = 1 * End_Calibration Seen = 0 * */ static int calib_actions(JOYSTICK_LIMIT_DATA *calib, char **tokens, int num_tokens, int *found, int vendor, int product, char *devname, int new) { int j, val; char *dummy; static JOYSTICK_AXIS_INSTANCE *s_axis = 0; static int s_vendor = 0, s_product = 0, s_have_device; static char s_device_name[256] = {0}; if (new) { s_vendor = 0; s_product = 0; s_device_name[0] = 0; s_axis = 0; s_have_device = 0; } j = 0; while (calib_commands[j].value != calibEOT) { if (strncmp(tokens[0], calib_commands[j].name, calib_commands[j].match) == 0) { break; } j += 1; } if (calib_commands[j].value == calibEOT) { if (vmsJoyVerbose & 1) printf("Unknown command %s\n", tokens[0]); return 0; } if (vmsJoyDebug & 0x8) printf("keyword %s\n", calib_commands[j].name); if ((vendor == 0) && (product == 0) && (devname == 0)) { s_have_device = 1; if ((vmsJoyDebug & 0x08) && (calib_commands[j].value == calibSTART)) printf("- Widlcard Search. New record.\n"); } if ((vendor == s_vendor) && (product == s_product) && (strcmp(devname, s_device_name) == 0) ) { s_have_device = 1; if ((vmsJoyDebug & 0x08) && (calib_commands[j].value == calibSTART)) printf("- Matched DEVICE/VENDOR/PRODUCT\n"); } switch (calib_commands[j].value) { case calibSTART: if (vmsJoyDebug & 0x08) printf("Process %s\n", tokens[0]); /* Reset the current vendor, product and device name */ s_vendor = 0; s_product = 0; s_device_name[0] = 0; s_axis = 0; s_have_device = 0; if (vmsJoyDebug & 0x08) printf(" - Calibration axis state is reset\n"); break; case calibVENDOR: if (vmsJoyDebug & 0x08) printf("Process %s\n", tokens[0]); if (num_tokens < 2) { if (vmsJoyVerbose & 1) printf("%s does not have enough arguments\n", tokens[0]); return 0; } val = strtol(tokens[1], &dummy, 10); if (vmsJoyDebug & 0x08) printf("%s value %s (%d)\n", tokens[0], tokens[1], val); s_vendor = val; break; case calibPRODUCT: if (vmsJoyDebug & 0x08) printf("Process %s\n", tokens[0]); if (num_tokens < 2) { if (vmsJoyVerbose & 1) printf("%s does not have enough arguments\n", tokens[0]); return 0; } val = strtol(tokens[1], &dummy, 10); if (vmsJoyDebug & 0x08) printf("%s value %s (%d)\n", tokens[0], tokens[1], val); s_product = val; break; case calibDEVICE: if (vmsJoyDebug & 0x08) printf("Process %s\n", tokens[0]); if (num_tokens < 2) { if (vmsJoyVerbose & 1) printf("%s does not have enough arguments\n", tokens[0]); return 0; } strcpy(&s_device_name[0], tokens[1]); if (vmsJoyDebug & 0x08) printf("%s value %s (%s)\n", tokens[0], tokens[1], s_device_name); break; case calibAXIS: if (vmsJoyDebug & 0x08) printf("Process %s\n", tokens[0]); if (num_tokens < 3) { if (vmsJoyVerbose & 1) printf("%s does not have enough arguments\n", tokens[0]); return 0; } if (vmsJoyDebug & 0x08) printf("%s type %s number %s\n", tokens[0], tokens[1], tokens[2]); /* * AXIS NAME NUMBER - example: "axis slider [1]" (is stripped down to "AXIS SLIDER 1") * *If we have a device, we will load this axis in static storage for the subsequent * calibration data */ if (s_have_device) { s_axis = 0; val = strtol(tokens[2], &dummy, 10); if (val >= MAX_JOYSTICK_XYZH) { if (vmsJoyVerbose & 1) printf("Axes type %s for axis %d out of range\n", tokens[1], val); } else { if (vmsJoyDebug & 8) printf("Match axis type %s\n", tokens[1]); if (strncmp(tokens[1], "X", 1) == 0) s_axis = &calib->x.axis[val]; if (strncmp(tokens[1], "Y", 1) == 0) s_axis = &calib->y.axis[val]; if (strncmp(tokens[1], "Z", 1) == 0) s_axis = &calib->z.axis[val]; if (strncmp(tokens[1], "RX", 2) == 0) s_axis = &calib->rx.axis[val]; if (strncmp(tokens[1], "RY", 2) == 0) s_axis = &calib->ry.axis[val]; if (strncmp(tokens[1], "RZ", 2) == 0) s_axis = &calib->rz.axis[val]; if (strncmp(tokens[1], "VX", 2) == 0) s_axis = &calib->vx.axis[val]; if (strncmp(tokens[1], "VY", 2) == 0) s_axis = &calib->vy.axis[val]; if (strncmp(tokens[1], "VZ", 2) == 0) s_axis = &calib->vz.axis[val]; if (strncmp(tokens[1], "VNO", 3) == 0) s_axis = &calib->vno.axis[val]; if (strncmp(tokens[1], "VBRX", 4) == 0) s_axis = &calib->vbrx.axis[val]; if (strncmp(tokens[1], "VBRY", 4) == 0) s_axis = &calib->vbry.axis[val]; if (strncmp(tokens[1], "VBRZ", 4) == 0) s_axis = &calib->vbrz.axis[val]; if (strncmp(tokens[1], "DIAL", 4) == 0) s_axis = &calib->dial.axis[val]; if (strncmp(tokens[1], "SLIDER", 6) == 0) s_axis = &calib->slider.axis[val]; if (strncmp(tokens[1], "WHEEL", 5) == 0) s_axis = &calib->wheel.axis[val]; if (s_axis == 0) { if (vmsJoyVerbose & 1) printf("Unknown axis type %s for axis %d\n", tokens[1], val); } else { if (vmsJoyDebug & 8) printf("Matched axis type - s_axis now valid\n"); } } } break; case calibCENTER: if (vmsJoyDebug & 0x08) printf("Process %s\n", tokens[0]); if (num_tokens < 2) { if (vmsJoyVerbose & 1) printf("%s does not have enough arguments\n", tokens[0]); return 0; } if (s_have_device) { val = strtol(tokens[1], &dummy, 10); if (vmsJoyDebug & 0x08) printf("%s value %s, int 0x%X\n", tokens[0], tokens[1], val); if (s_axis) { if (vmsJoyDebug & 0x08) printf("Store %d to CENTER\n", val); s_axis->flags |= JOYSTICK_AXIS_CENTER_OFF_VALID; s_axis->center_off = val; } else { if (vmsJoyVerbose & 1) printf("%s keyword found without preceding AXIS\n", tokens[0]); } } break; case calibCMIN: if (vmsJoyDebug & 0x08) printf("Process %s\n", tokens[0]); if (num_tokens < 2) { if (vmsJoyVerbose & 1) printf("%s does not have enough arguments\n", tokens[0]); return 0; } if (s_have_device) { val = strtol(tokens[1], &dummy, 10); if (vmsJoyDebug & 0x08) printf("%s value %s, int 0x%X\n", tokens[0], tokens[1], val); if (s_axis) { if (vmsJoyDebug & 0x08) printf("Store %d to CMIN\n", val); s_axis->flags |= JOYSTICK_AXIS_CMIN_VALID; s_axis->cmin = val; } else { if (vmsJoyVerbose & 1) printf("%s keyword found without preceding AXIS\n", tokens[0]); } } break; case calibCMAX: if (vmsJoyDebug & 0x08) printf("Process %s\n", tokens[0]); if (num_tokens < 2) { if (vmsJoyVerbose & 1) printf("%s does not have enough arguments\n", tokens[0]); return 0; } if (s_have_device) { val = strtol(tokens[1], &dummy, 10); if (vmsJoyDebug & 0x08) printf("%s value %s, int 0x%X\n", tokens[0], tokens[1], val); if (s_axis) { if (vmsJoyDebug & 0x08) printf("Store %d to CMAX\n", val); s_axis->flags |= JOYSTICK_AXIS_CMAX_VALID; s_axis->cmax = val; } else { if (vmsJoyVerbose & 1) printf("%s keyword found without preceding AXIS\n", tokens[0]); } } break; case calibEND: if (vmsJoyDebug & 0x08) printf("End_Calibration\n"); /* * Set the return values only if we have * located a record. */ if (s_have_device) { *found = 1; } s_vendor = 0; s_product = 0; s_device_name[0] = 0; s_axis = 0; s_have_device = 0; return 1; break; case calibEXIT: if (vmsJoyDebug & 0x08) printf("Exit_Calibration\n"); /* * Set the return values only if we have * located a record. */ if (s_have_device) { *found = 1; } s_vendor = 0; s_product = 0; s_device_name[0] = 0; s_axis = 0; s_have_device = 0; return 0; break; } return 1; } /* * Routine: vmsJoyGetCalibrationData - external scope * * int vmsJoyGetCalibrationData ( char *calib_file, JOYSTICK_LIMIT_DATA *calib, int vendor, int product, char *devname) * * Abstract: * * This is the main entry point to read a calibration file. It will open and * parse the file and fill in calibration data for the specific combination of * DEVICE/VENDOR/PRODUCT which allows a file to contain calibration data for * more than one device. * * Calibration data is applied to the logical inputs when using the vmsJoyScale * routine. The most common use is to "zero" an analog joystick or steering wheel * that does not report zero (or it's midpoint) when in the neutral position. * It also can set the measured range of motion to override the reported logical * min/max values and allow the end consumer to get full range outputs. * * Inputs: * * calib_file - The name of a calibration file * calib - a JOYSTICK_LIMIT_DATA record to be used to hold calibration data * vendor - USB Vendor ID * product - USB Product ID * devname - Device name (like AGA0 or AGA1) * * Outputs: * * calib structure is filled in with any matching calibration data * * Side Effects: * * none * * Returns: * * Found file and matched DEVICE/VENDOR/PRODUCT = 1 * Did not find file, or no match found = 0; * */ int vmsJoyGetCalibrationData( char *calib_file, JOYSTICK_LIMIT_DATA *calib, int vendor, int product, char *devname) { FILE *in; char buffer_in[STRING_MAX]; char *tokens[1024]; int status = 1, seps = 0, num_tokens, found = 0, new = 1; #ifdef PRINTWHERE printf("vmsJoyGetCalibrationData\n"); #endif in = fopen(calib_file, "r"); if (!in) { if (vmsJoyVerbose & 2) printf("Could not open calibration file %s\n", calib_file); return 0; } /* * This parses each line int the file. It looks for calibration definitions * and matches the vendor and product IDs and device name. If there are multiple * records that match, the FIRST one is used. */ while (status > 0) { status = get_next_item( in, buffer_in); if (status > 0) { num_tokens = tokenize_input(buffer_in, &tokens[0], 1024, &seps); status = calib_actions(calib, &tokens[0], num_tokens, &found, vendor, product, devname, new); if (found) break; new = 0; } } fclose(in); return found; } /* * Routine: joydef_actions - static scope * * static int joydef_actions (JOYSTICK_DEFINITION *joydef, char **tokens, int num_tokens, int *found, int vendor, int product) * * Abstract: * * Parser actions for calibration data file * * Inputs: * * empty joydef, vendor ID, product ID, token list from edit/tokenize * * Outputs: * * found = 1 if a match occured * joydef filled in * * Side Effects: * * none * * Returns: * * 0 = done * 1 = mode */ static int joydef_actions(JOYSTICK_DEFINITION *joydef, char **tokens, int num_tokens, int *found, int vendor, int product) { int j, k, val; char *dummy; j = 0; while (joydef_commands[j].value != joydefEOT) { if (strncmp(tokens[0], joydef_commands[j].name, joydef_commands[j].match) == 0) { break; } j += 1; } if (joydef_commands[j].value == joydefEOT) { if (vmsJoyVerbose & 1) printf("Unknown command %s\n", tokens[0]); return 0; } if (vmsJoyDebug & 0x8) printf("keyword %s\n", joydef_commands[j].name); switch (joydef_commands[j].value) { case joydefSTART: if (num_tokens < 3) { if (vmsJoyVerbose & 1) printf("%s does not have enough arguments - need vendor name and product name\n", tokens[0]); return 0; } if (vmsJoyDebug & 0x08) printf("Process %s\n", tokens[0]); strncpy(&joydef->vendor_name[0], tokens[1], 80); strncpy(&joydef->product_name[0], tokens[2], 80); if (vmsJoyDebug & 0x08) printf("Vendor %s, Product %s\n", tokens[1], tokens[2]); /* Reset the current data */ joydef->vendor_id = 0; joydef->product_id = 0; joydef->flags = 0; joydef->x_map[0] = 0; joydef->x_map[1] = 0; joydef->y_map[0] = 0; joydef->y_map[1] = 0; joydef->z_map[0] = 0; joydef->z_map[1] = 0; joydef->rz_map[0] = 0; joydef->rz_map[1] = 0; joydef->throttle_map[0] = 0; joydef->throttle_map[1] = 0; joydef->button_map_count = 0; for (j = 0; j < 32; j += 1) { joydef->button_map[j] = 0; } break; case joydefVENDOR_ID: if (vmsJoyDebug & 0x08) printf("Process %s\n", tokens[0]); if (num_tokens < 2) { if (vmsJoyVerbose & 1) printf("%s does not have enough arguments\n", tokens[0]); return 0; } val = strtol(tokens[1], &dummy, 10); if (vmsJoyDebug & 0x08) printf("%s value %s (%d)\n", tokens[0], tokens[1], val); joydef->vendor_id = val; break; case joydefPRODUCT_ID: if (vmsJoyDebug & 0x08) printf("Process %s\n", tokens[0]); if (num_tokens < 2) { if (vmsJoyVerbose & 1) printf("%s does not have enough arguments\n", tokens[0]); return 0; } val = strtol(tokens[1], &dummy, 10); if (vmsJoyDebug & 0x08) printf("%s value %s (%d)\n", tokens[0], tokens[1], val); joydef->product_id = val; break; case joydefFLAGS: /* Snag and OR all the flag bits */ for (k = 1; k < num_tokens; k += 1) { /* * Get the flag value from it's name */ j = 0; while (joydef_flags[j].value != joydefEOT) { if (strncmp(tokens[k], joydef_flags[j].name, joydef_flags[j].match) == 0) { break; } j += 1; } if (joydef_flags[j].value == joydefEOT) { if (vmsJoyVerbose & 1) printf("Unknown Flag %s\n", tokens[1]); return 0; } joydef->flags |= joydef_flags[j].value; } break; case joydefX: if (vmsJoyDebug & 0x08) printf("Process %s\n", tokens[0]); if (num_tokens < 2) { if (vmsJoyVerbose & 1) printf("%s does not have enough arguments\n", tokens[0]); return 0; } /* * Get the Axis value from it's name */ j = 0; while (joydef_axis_flags[j].value != joydefEOT) { if (strncmp(tokens[1], joydef_axis_flags[j].name, joydef_axis_flags[j].match) == 0) { break; } j += 1; } if (joydef_axis_flags[j].value == joydefEOT) { if (vmsJoyVerbose & 1) printf("Unknown Axis %s\n", tokens[1]); return 0; } joydef->x_map[0] = joydef_axis_flags[j].value; if (joydef_axis_flags[j].value) { if (num_tokens < 3) { if (vmsJoyVerbose & 1) printf("%s does not have enough arguments\n", tokens[0]); return 0; } joydef->x_map[1] = strtol(tokens[2], &dummy, 10); if (vmsJoyDebug & 0x08) printf("%s flag %s (%8.8X, value %s (%d)\n", tokens[0], tokens[1], joydef->x_map[0], tokens[2], joydef->x_map[1]); } break; case joydefY: if (vmsJoyDebug & 0x08) printf("Process %s\n", tokens[0]); if (num_tokens < 2) { if (vmsJoyVerbose & 1) printf("%s does not have enough arguments\n", tokens[0]); return 0; } /* * Get the Axis value from it's name */ j = 0; while (joydef_axis_flags[j].value != joydefEOT) { if (strncmp(tokens[1], joydef_axis_flags[j].name, joydef_axis_flags[j].match) == 0) { break; } j += 1; } if (joydef_axis_flags[j].value == joydefEOT) { if (vmsJoyVerbose & 1) printf("Unknown Axis %s\n", tokens[1]); return 0; } joydef->y_map[0] = joydef_axis_flags[j].value; if (joydef_axis_flags[j].value) { if (num_tokens < 3) { if (vmsJoyVerbose & 1) printf("%s does not have enough arguments\n", tokens[0]); return 0; } joydef->y_map[1] = strtol(tokens[2], &dummy, 10); if (vmsJoyDebug & 0x08) printf("%s flag %s (%8.8X, value %s (%d)\n", tokens[0], tokens[1], joydef->y_map[0], tokens[2], joydef->y_map[1]); } break; case joydefZ: if (vmsJoyDebug & 0x08) printf("Process %s\n", tokens[0]); if (num_tokens < 2) { if (vmsJoyVerbose & 1) printf("%s does not have enough arguments\n", tokens[0]); return 0; } /* * Get the Axis value from it's name */ j = 0; while (joydef_axis_flags[j].value != joydefEOT) { if (strncmp(tokens[1], joydef_axis_flags[j].name, joydef_axis_flags[j].match) == 0) { break; } j += 1; } if (joydef_axis_flags[j].value == joydefEOT) { if (vmsJoyVerbose & 1) printf("Unknown Axis %s\n", tokens[1]); return 0; } joydef->z_map[0] = joydef_axis_flags[j].value; if (joydef_axis_flags[j].value) { if (num_tokens < 3) { if (vmsJoyVerbose & 1) printf("%s does not have enough arguments\n", tokens[0]); return 0; } joydef->z_map[1] = strtol(tokens[2], &dummy, 10); if (vmsJoyDebug & 0x08) printf("%s flag %s (%8.8X, value %s (%d)\n", tokens[0], tokens[1], joydef->z_map[0], tokens[2], joydef->z_map[1]); } break; case joydefRZ: if (vmsJoyDebug & 0x08) printf("Process %s\n", tokens[0]); if (num_tokens < 2) { if (vmsJoyVerbose & 1) printf("%s does not have enough arguments\n", tokens[0]); return 0; } /* * Get the Axis value from it's name */ j = 0; while (joydef_axis_flags[j].value != joydefEOT) { if (strncmp(tokens[1], joydef_axis_flags[j].name, joydef_axis_flags[j].match) == 0) { break; } j += 1; } if (joydef_axis_flags[j].value == joydefEOT) { if (vmsJoyVerbose & 1) printf("Unknown Axis %s\n", tokens[1]); return 0; } joydef->rz_map[0] = joydef_axis_flags[j].value; if (joydef_axis_flags[j].value) { if (num_tokens < 3) { if (vmsJoyVerbose & 1) printf("%s does not have enough arguments\n", tokens[0]); return 0; } joydef->rz_map[1] = strtol(tokens[2], &dummy, 10); if (vmsJoyDebug & 0x08) printf("%s flag %s (%8.8X, value %s (%d)\n", tokens[0], tokens[1], joydef->rz_map[0], tokens[2], joydef->rz_map[1]); } break; case joydefTHROTTLE: if (vmsJoyDebug & 0x08) printf("Process %s\n", tokens[0]); if (num_tokens < 2) { if (vmsJoyVerbose & 1) printf("%s does not have enough arguments\n", tokens[0]); return 0; } /* * Get the Axis value from it's name */ j = 0; while (joydef_axis_flags[j].value != joydefEOT) { if (strncmp(tokens[1], joydef_axis_flags[j].name, joydef_axis_flags[j].match) == 0) { break; } j += 1; } if (joydef_axis_flags[j].value == joydefEOT) { if (vmsJoyVerbose & 1) printf("Unknown Axis %s\n", tokens[1]); return 0; } joydef->throttle_map[0] = joydef_axis_flags[j].value; if (joydef_axis_flags[j].value) { if (num_tokens < 3) { if (vmsJoyVerbose & 1) printf("%s does not have enough arguments\n", tokens[0]); return 0; } joydef->throttle_map[1] = strtol(tokens[2], &dummy, 10); if (vmsJoyDebug & 0x08) printf("%s flag %s (%8.8X, value %s (%d)\n", tokens[0], tokens[1], joydef->throttle_map[0], tokens[2], joydef->throttle_map[1]); } break; case joydefBUTTON: if (vmsJoyDebug & 0x08) printf("Process %s\n", tokens[0]); if (num_tokens < 2) { if (vmsJoyVerbose & 1) printf("%s does not have enough arguments\n", tokens[0]); return 0; } if (vmsJoyDebug & 0x08) printf("Process %s\n", tokens[0]); joydef->flags |= JOYSTICK_DEF_BUTTON_MAP; for (j = 1; j < num_tokens; j += 1) { joydef->button_map_count += 1; joydef->button_map[j-1] = strtol(tokens[j], &dummy, 10); if (joydef->button_map[j-1] > 27) { if (vmsJoyVerbose & 1) printf("Button Map index %d is %d - a bit offset > 27, setting to 27\n", j-1, joydef->button_map[j-1]); joydef->button_map[j-1] = 27; } } break; case joydefEND: if ((vendor == 0) && (product == 0)) { *found = 1; if (vmsJoyDebug & 0x08) printf("- Wildcard Joystick Definition Search. New record.\n"); } else { if ((vendor == joydef->vendor_id) && (product == joydef->product_id)) { *found = 1; if (vmsJoyDebug & 0x08) printf("- Matched DEVICE/VENDOR Joystick definition\n"); } } break; } return 1; } /* * Routine: vmsJoyGetJoystickDefinition - external scope * * int vmsJoyGetJoystickDefinition ( JOY_CONTEXT *joy, char *joydef_file, int vendor, int product) * * Abstract: * * Inputs: * * Outputs: * * Side Effects: * * none * * Returns: * * 1 = Found and loaded file * 0 = No file, or error parsing file * */ int vmsJoyGetJoystickDefinition ( JOY_CONTEXT *joy, char *joydef_file, JOYSTICK_DEFINITION **rjoydef, int vendor, int product) { FILE *in; char buffer_in[STRING_MAX]; char *tokens[1024]; int status = 1, seps = 0, num_tokens, found = 0; JOYSTICK_DEFINITION *joydef; #ifdef PRINTWHERE printf("vmsJoyGetJoystickDefinition \n"); #endif in = fopen( joydef_file, "r"); if (!in) { if (vmsJoyVerbose & 2) printf("Could not open joydef file %s\n", joydef_file); return 0; } joydef = (JOYSTICK_DEFINITION *) calloc (1, sizeof(JOYSTICK_DEFINITION)); joydef->dynamic = JOYSTICK_DEF_DYNAMIC; /* * vendor & product = 0 -- build full list (read whole file) * vendor & product != 0 -- Just read until this one is found * */ while (status > 0) { status = get_next_item( in, buffer_in); if (status > 0) { num_tokens = tokenize_input(buffer_in, &tokens[0], 1024, &seps); status = joydef_actions(joydef, &tokens[0], num_tokens, &found, vendor, product); if (found) { /* Add the new definition to the list, and allocate a new block */ /* But first search the current list and replace any duplicates */ *rjoydef = joydef; } } } fclose(in); return found; } /* * Routine: vmsSetJoystickCalibration - external scope * * int vmsSetJoystickCalibration (JOY_CONTEXT *joy, JOYSTICK_LIMIT_DATA *limit) * * Abstract: * * This routine makes the QIO call to set calibration data in the drivers * limit data. Once there, it will be maintained until the joystick goes * offline (as in a unplug event). * * Inputs: * * joy - a joystick context block (contains the channel) * limit - the limit data containing the calibration data * * Outputs: * * Side Effects: * * none * * Returns: * * VMS status from the QIO */ int vmsSetJoystickCalibration (JOY_CONTEXT *joy, JOYSTICK_LIMIT_DATA *limit) { int status; struct _iosb { short int status; short int msg_len; int unused; } iosb; #ifdef PRINTWHERE printf("vmsStartJoystick\n"); #endif if (!joy) return 0; if (!limit) return 0; status = sys$qiow (0, joy->channel, IO$_SETMODE, &iosb, 0, 0, JOYSTICK_IO_SET_CALIBRATION_DATA, /* P1 */ sizeof(JOYSTICK_LIMIT_DATA), /* P2 (Length of Data) */ (int) limit, /* P3 (Calibration Data)*/ 0, /* P4 (not used) */ 0, /* P5 (not used) */ 0); /* P6 (not used) */ if ((!(status & 1)) && (!(iosb.status & 1))) { printf("Error doing SETMODE for async notification 0x%X\n", status); } /* Need to re-sense the limit data now */ status = sys$qiow (0, joy->channel, IO$_SENSEMODE, &iosb, 0, 0, JOYSTICK_IO_SENSE_LIMIT_DATA, /* P1 */ &joy->info, /* P2 */ sizeof(JOYSTICK_LIMIT_DATA), /* P3 */ 0, /* P4 */ 0, /* P5 */ 0); /* P6 */ return (status & 1); } /* * Routine: vmsJoySetScale - external scope * * int vmsJoySetScale(JOY_CONTEXT *joy, int flags, int range, int axis_type) * * Abstract: * * This routine takes as input a input value, an AXIS instance, and a scaling structure * It then handles the conversion of the input based on the description in the AXIS * structure and scales it as requested. * * Note that if you choose to scale to some non-multiple of the input range, then the * result may not be full range on output. -511 to 511 scaled to -1000 to 1000 will * probably get you -1000 to 997 or something along those lines. * * Inputs: * * in - The input value * scale - A pointer to a scaling structure * axis - A pointer to a JOYSTICK_AXIS_INFO structure * * Outputs: * * The value is scaled and adjusted according to the information in the axis * and scaling records. * * Side Effects: * * none * * Returns: * * Scaled value * */ int vmsJoySetScale(JOY_CONTEXT *joy, int flags, int range, int axis_type) { JOYSTICK_SCALE *scale; JOYSTICK_AXIS_INSTANCE *axis; JOYSTICK_DEFINITION *joydef = joy->joydef; int invert; switch (axis_type) { case JOYSTICK_X_AXIS: scale = &joy->x_scale; axis = joy->x_axis; invert = joydef->flags & JOYSTICK_DEF_INVERT_X; break; case JOYSTICK_Y_AXIS: scale = &joy->y_scale; axis = joy->y_axis; invert = joydef->flags & JOYSTICK_DEF_INVERT_Y; break; case JOYSTICK_Z_AXIS: scale = &joy->z_scale; axis = joy->z_axis; invert = joydef->flags & JOYSTICK_DEF_INVERT_Z; break; case JOYSTICK_RZ_AXIS: scale = &joy->rz_scale; axis = joy->rz_axis; invert = joydef->flags & JOYSTICK_DEF_INVERT_RZ; break; case JOYSTICK_THROTTLE_AXIS: scale = &joy->throttle_scale; axis = joy->throttle_axis; invert = joydef->flags & JOYSTICK_DEF_INVERT_THROTTLE; break; } /* If the axis doesn't exist, ignore */ if (axis == 0) return 0; scale->flags = flags; scale->range = range; scale->out_range = range; scale->in_range = range; /* * Pre-compute scaling values */ if (flags & JOYSTICK_SCALE_ENABLE) { /* Handle calibrated min/max when scaling */ if (axis->flags & JOYSTICK_AXIS_CMIN_VALID) scale->min = axis->cmin; else scale->min = axis->min; if (axis->flags & JOYSTICK_AXIS_CMAX_VALID) scale->max = axis->cmax; else scale->max = axis->max; /* * If we are centering the data (that is, zero at half the * range, we need to do some manipulation */ if (scale->flags & JOYSTICK_SCALE_CENTER) { scale->out_range = range * 2; if (axis->flags & JOYSTICK_AXIS_SIGNED) { scale->in_range = (-(scale->min+1) + (scale->max+1)) / 2; } else { scale->in_range = (scale->max+1) - scale->min; } } else { if (axis->flags & JOYSTICK_AXIS_SIGNED) { scale->in_range = (-(scale->min+1) + (scale->max+1)) / 2; } else { scale->in_range = (scale->max+1) - scale->min; } } /* * Figure out if we need to invert. The joystick definition for a * normalized joystick AND the scaling inversion need to be looked * at since they might cancel each other out. */ scale->invert = 0; if ((scale->flags & JOYSTICK_SCALE_INVERT) || invert) { if (!((scale->flags & JOYSTICK_SCALE_INVERT) && invert)) { scale->invert = 1; } } } return 1; } /* * Routine: vmsJoyScale - external scope * * int vmsJoyScale (int in, JOYSTICK_SCALE *scale, JOYSTICK_AXIS_INSTANCE *axis) * * Abstract: * * This routine takes as input a input value, an AXIS instance, and a scaling structure * It then handles the conversion of the input based on the description in the AXIS * structure and scales it as requested. * * Note that if you choose to scale to some non-multiple of the input range, then the * result may not be full range on output. -511 to 511 scaled to -1000 to 1000 will * probably get you -1000 to 997 or something along those lines. * * Inputs: * * in - The input value * scale - A pointer to a scaling structure * axis - A pointer to a JOYSTICK_AXIS_INFO structure * * Outputs: * * The value is scaled and adjusted according to the information in the axis * and scaling records. * * Side Effects: * * none * * Returns: * * Scaled value * */ int vmsJoyScale(int in, JOYSTICK_SCALE *scale, JOYSTICK_AXIS_INSTANCE *axis) { int out, range, out_range, min, max; #ifdef PRINTWHERE printf("vmsJoyScale\n"); #endif /* Before we do anything, deal with calibration centering data */ if (axis->flags & JOYSTICK_AXIS_CENTER_OFF_VALID) { in += axis->center_off; } /* See if scaling is enabled */ if (!(scale->flags & JOYSTICK_SCALE_ENABLE)) return in; if (scale->flags & JOYSTICK_SCALE_CENTER) { if (axis->flags & JOYSTICK_AXIS_SIGNED) { out = in; } else { out = ((scale->max+1) / 2) - in; } } else { if (axis->flags & JOYSTICK_AXIS_SIGNED) { out = in + (scale->in_range / 2); } else { out = in; } } /* Done if zero */ if (out == 0) return 0; /* Scale the output */ out = ((out * scale->out_range) / scale->in_range); /* See if we want to flip the direction */ if (scale->invert) { if (scale->flags & JOYSTICK_SCALE_CENTER) { out = -out; } else out = scale->max - out; } /* Clamp the output */ if (out > scale->range) return scale->range ; else if (out < -scale->range ) return (-scale->range ); else return out; } /* * Routine: char * vmsGetVendorName - external scope * * char * vmsGetVendorName(int id) * * Abstract: * * Simple table lookup to get a vendor name string from a vendor ID * If it isn't found "UNKNOWN" is returned. * * Inputs: * * id - USB vendor id * * Outputs: * * String name of vendor * * Side Effects: * * none * * Returns: * */ char * vmsJoyGetVendorName(JOY_CONTEXT *joy, int vendor_id) { int i = 0; JOYSTICK_DEF_ARRAY *joy_array; JOYSTICK_DEFINITION *joydef; #ifdef PRINTWHERE printf("vmsGetVendorName\n"); #endif joy_array = joy->joydef_array; for (i = 0; i < joy_array->count; i += 1) { joydef = joy_array->info[i]; if (joydef) { if (joydef->vendor_id == vendor_id) break; } } joydef = joy_array->info[i]; return ( (joydef && (joydef->vendor_name[0] != 0)) ? &joydef->vendor_name[0] : "Unknown"); } /* * Routine: char * vmsGetProductName - external scope * * char * vmsGetVendorName(int vendor_id, int product_id) * * Abstract: * * Simple table lookup to get a product name string from a vendor * and product ID * * If it isn't found "UNKNOWN" is returned. * * Inputs: * * id - USB vendor id * * Outputs: * * String name of vendor * * Side Effects: * * none * * Returns: * */ char * vmsJoyGetProductName(JOY_CONTEXT *joy, int vendor_id, int product_id) { int i = 0; JOYSTICK_DEF_ARRAY *joy_array; JOYSTICK_DEFINITION *joydef; #ifdef PRINTWHERE printf("vmsGetProductName\n"); #endif joy_array = joy->joydef_array; for (i = 0; i < joy_array->count; i += 1) { joydef = joy_array->info[i]; if (joydef) { if ((joydef->vendor_id == vendor_id) && (joydef->product_id == product_id)) break; } } joydef = joy_array->info[i]; return ( (joydef && (joydef->product_name[0] != 0)) ? &joydef->product_name[0] : "Unknown"); } /* * Routine: vmsAssignJoystick - external scope * * int vmsAssignJoystick (JOY_CONTEXT *joy) * * Abstract: * * Assign a channel to a joystick, and fill in * the channel number, and do a sensemode of the * limit data for the device. * * Inputs: * * joy - JOYSTICK_CONTEXT structure * * Outputs: * * Channel Open * Limit data filled in * * Side Effects: * * none * * Returns: * * VMS status * 0 if passed a null joy pointer * */ int vmsAssignJoystick (JOY_CONTEXT *joy) { int status; struct _iosb { short int status; short int msg_len; int unused; } iosb; #ifdef PRINTWHERE printf("vmsAssignJoystick\n"); #endif if (!joy) return 0; if (joy->channel == 0) { char buffer[80]; struct dsc$descriptor logname; struct dsc$descriptor devname; logname.dsc$b_dtype = 0; logname.dsc$b_class = 0; logname.dsc$a_pointer = "HID$JOYSTICK_DEVICE"; logname.dsc$w_length = strlen ("HID$JOYSTICK_DEVICE"); devname.dsc$b_dtype = 0; devname.dsc$b_class = 0; devname.dsc$a_pointer = &buffer[0]; devname.dsc$w_length = 80; if (!(LIB$GET_LOGICAL (&logname, &devname, &devname.dsc$w_length, 0, 0, 0, 0, 0)&1)) { devname.dsc$a_pointer = "AGA0:"; devname.dsc$w_length = strlen ("AGA0:"); } strncpy(&joy->device_name[0], devname.dsc$a_pointer, devname.dsc$w_length); if (joy->device_name[devname.dsc$w_length-1] == ':') { joy->device_name[devname.dsc$w_length-1] = 0; } if (!(sys$assign (&devname, &joy->channel, 0, 0, 0) & 1)) joy->channel = 0; } if (joy->channel) { status = sys$qiow (0, joy->channel, IO$_SENSEMODE, &iosb, 0, 0, JOYSTICK_IO_SENSE_LIMIT_DATA, /* P1 */ &joy->info, /* P2 */ sizeof(JOYSTICK_LIMIT_DATA), /* P3 */ 0, /* P4 */ 0, /* P5 */ 0); /* P6 */ if ((status & 1) && (iosb.status & 1)) { if (joy->info.version != JOYSTICK_LIMIT_DATA_VERSION) { printf("Incompatable LIMIT DATA version. Expected %d, got %d\n", JOYSTICK_LIMIT_DATA_VERSION, joy->info.version); sys$dassgn (joy->channel); joy->channel = 0; return 0; } } else { if (status == SS$_UNSUPPORTED) printf("SENSEMODE of limit data returns UNSUPPORTED. Driver/Library mismatch\n"); else { if (status == SS$_DEVOFFLINE) {if (vmsJoyVerbose) printf("Joystick is offline/unplugged\n");} else printf("Can't sense limit data, status 0x%X (iosb 0x%X)\n", status, iosb.status); } sys$dassgn (joy->channel); joy->channel = 0; } } else { if (vmsJoyVerbose & 2) printf("No joystick found\n"); } return (status); } /* * Routine: vmsDeassignJoystick - extern scope * * void vmsDeassignJoystick (JOY_CONTEXT *joy) * * Abstract: * * Deassign the channel to the joystick and zero the * channel in the joystick structure * * Inputs: * * joy - Joystick context structure * * Outputs: * * Side Effects: * * none * * Returns: * */ void vmsDeassignJoystick (JOY_CONTEXT *joy) { #ifdef PRINTWHERE printf("vmsDeassignJoystick\n"); #endif if (!joy) return; if (joy->channel != 0) { sys$dassgn (joy->channel); joy->channel = 0; } } /* * Routine: vmsReadQIOJoystick - eternal scope * * int vmsReadQIOJoystick (JOY_CONTEXT *joy) * * Abstract: * * Issue the QIO read for th Joystick data. It will also * call the scaling logic to fill in the "standard" values * used by GLUT. * * Note that in general joysticks have no standard "throttle" * vector. So either the Z axis or Slider axis is used. * * Inputs: * * joy - JOYSTICK_CONTEXT * * Outputs: * * Context is filled in with new data (raw) and scaled data * * Side Effects: * * none * * Returns: * * VMS status */ int vmsReadQIOJoystick (JOY_CONTEXT *joy) { JOYSTICK_DEFINITION *joydef; int status, button, buttons, i; struct _iosb { short int status; short int msg_len; int unused; } read_iosb; #ifdef PRINTWHERE printf("vmsReadQIOJoystick\n"); #endif status = sys$qiow (0, joy->channel, IO$_READVBLK, &read_iosb, 0, 0, &joy->data, /* P1 - address to write frame data */ sizeof(JOYSTICK_INPUT_FRAME), /* P2 - Length */ 0, /* P3 - not used */ 0, /* P4 - not used */ 0, /* P5 - not used */ 0); /* P6 - not used */ if (status & 1) status = read_iosb.status; if (status & 1) { if (joy->data.version != JOYSTICK_INPUT_FRAME_VERSION) { printf("Incompatable INPUT FRAME version. Expected %d, got %d\n", JOYSTICK_INPUT_FRAME_VERSION, joy->data.version); return 0; } joydef = joy->joydef; if (joy->x_axis) joy->x = vmsJoyScale(*joy->x_data, &joy->x_scale, joy->x_axis); if (joy->y_axis) joy->y = vmsJoyScale(*joy->y_data, &joy->y_scale, joy->y_axis); if (joy->z_axis) joy->z = vmsJoyScale(*joy->z_data, &joy->z_scale, joy->z_axis); if (joy->rz_axis) joy->rz = vmsJoyScale(*joy->rz_data, &joy->rz_scale, joy->rz_axis); if (joy->throttle_axis) joy->throttle = vmsJoyScale(*joy->throttle_data, &joy->throttle_scale, joy->throttle_axis); if (joydef->button_map_count) { buttons = 0; button = 1; /* * The index into the button map is the bit offset into * the button input. If the bit is set, then we will * use the value at the button map offset as the bit * number for the destination. */ for (i = 0; i < joydef->button_map_count; i += 1) { if (button & joy->data.button_state) buttons |= (1 << joydef->button_map[i]); button <<= 1; } } else { buttons = joy->data.button_state; } joy->hat = joy->data.hat[0]; joy->buttons = (buttons & 0x0FFFFFFF) | ((joy->hat & 0xF) << 28); } else { joy->buttons = 0; joy->hat = 0; joy->x = 0; joy->y = 0; joy->z = 0; joy->rz = 0; joy->throttle = 0; printf("Error from joystick read 0x%X\n", status); } return (status); } /* * Routine: vmsPollJoystick - exern scope * * void vmsPollJoystick (int astprm) * * Abstract: * * This is where either a timer event (true polling) or a event * notification AST is delivered. The astprm is a pointer to a * joystick context * * The code will call the ReadQIO routine and then make any callback * to application code that is setup. * * If we are doing true "polling" then we will requeue a timer AST * * Inputs: * * astprm - joystick context structure address * * Outputs: * * Side Effects: * * none * * Returns: * */ void vmsPollJoystick (int astprm) { int status = 1; JOY_CONTEXT *joy = (JOY_CONTEXT *) astprm; #ifdef PRINTWHERE printf("vmsPollJoystick\n"); #endif if (!joy) { printf("Danger will Robinson! ASTPRM isn't a joystick, or we have been turned off\n"); vmsDeassignJoystick (joy); free(joy); return; } if ((status = vmsReadQIOJoystick(joy)) & 1) { if ((joy->old_buttons != joy->buttons) || (joy->old_hat != joy->hat) || (joy->old_x != joy->x) || (joy->old_z != joy->z) || (joy->old_y != joy->y) || (joy->old_rz != joy->rz) || (joy->old_throttle != joy->throttle)) { #ifdef VMS_GLUT if (__glutCurrentWindow->joystick) { if (__glutCurrentWindow->joystick) __glutCurrentWindow->joystick ((unsigned int)joy->buttons, joy->x, joy->y, joy->z, joy->rz, joy->throttle, joy); } #else if (joy->callback_address) { if (joy->callback_address) (*joy->callback_address) ((unsigned int)joy->buttons, joy->x, joy->y, joy->z, joy->rz, joy->throttle, joy); } #endif /* * Copy old data */ joy->old_buttons = joy->buttons; joy->old_hat = joy->hat; joy->old_x = joy->x; joy->old_y = joy->y; joy->old_z = joy->z; joy->old_rz = joy->rz; joy->old_throttle = joy->throttle; } if (joy->polling == JOYSTICK_POLLED) { unsigned int wait_time[2]; #ifdef VMS_GLUT wait_time[0] = -10000 * __glutCurrentWindow->joyPollInterval; #else wait_time[0] = -10000 * joy->poll_interval; #endif wait_time[1] = -1; status = sys$setimr(17, &wait_time, vmsPollJoystick, (int) joy, 0); if (!status & 1) { printf("Error from settimr 0x%X\n", status); #ifdef TESTMODE exit (status); #endif } return; } } else { printf("Error reading joystick 0x%X\n", status); } } /* * Routine: vmsCloseJoystick - extern scope * * int vmsCloseJoystick (JOY_CONTEXT *joy) * * Abstract: * * Turn off any AST's and deassign the channel * * Inputs: * * joy - joystick context pointer * * Outputs: * * Side Effects: * * none * * Returns: * */ int vmsCloseJoystick (JOY_CONTEXT *joy) { int status; struct _iosb { short int status; short int msg_len; int unused; } iosb; #ifdef PRINTWHERE printf("vmsCloseJoystick\n"); #endif if (joy->polling == JOYSTICK_POLLED) joy->polling = 0; else { if (joy->polling == JOYSTICK_ASYNC) { joy->polling = 0; status = sys$qiow (0, joy->channel, IO$_SETMODE, &iosb, 0, 0, JOYSTICK_IO_CLEAR_AST_NOTIFY, /* P0 */ 0, /* P1 */ 0, /* P2 */ 0, /* P3 */ 0, /* P4 */ 0); /* P5 */ } vmsDeassignJoystick (joy); } return status; } /* * Routine: vmsStopJoystick - external scope * * void vmsStopJoystick (JOY_CONTEXT *joy) * * Abstract: * * Same as vmsCloseJoystick but also free's the joystick * context structure. Uses vmsStopJoystick. * * Inputs: * * joy - joystick context structure * * Outputs: * * Side Effects: * * context structure is released, so caller better not use it after * they call this. * * none * * Returns: * */ void vmsStopJoystick (JOY_CONTEXT *joy) { #ifdef PRINTWHERE printf("vmsStopJoystick\n"); #endif vmsCloseJoystick(joy); free(joy); } /* * Routine: getAxisData - local scope * * static int getAxisData(JOY_CONTEXT *joy, int map, int offset, JOYSTICK_AXIS_INSTANCE **axis, int **data) * * Abstract: * * Returns pointers to the axis and data for the axis defined by (map) for instance * (offset). This is a utility routine to take joystick mapping and get the right * source for it. * * Inputs: * * joy - joystick context structure * map - Axis value from the MAP command * offset - instance of the axis (usually 0) * * Outputs: * * axis - pointer to axis instance * data - pointer to integer where scaled output is returned during reads * * Side Effects: * * none * * Returns: * * 1 = Valid axis * 0 = No axis * */ static int getAxisData(JOY_CONTEXT *joy, int map, int offset, JOYSTICK_AXIS_INSTANCE **axis, int **data) { *axis = 0; *data = 0; if (map == JOYSTICK_NO_AXIS) return SS$_NORMAL; switch(map) { case JOYSTICK_X_AXIS: *axis = &joy->info.x.axis[offset]; *data = &joy->data.x[offset]; break; case JOYSTICK_Y_AXIS: *axis = &joy->info.y.axis[offset]; *data = &joy->data.y[offset]; break; case JOYSTICK_Z_AXIS: *axis = &joy->info.z.axis[offset]; *data = &joy->data.z[offset]; break; case JOYSTICK_RX_AXIS: *axis = &joy->info.rx.axis[offset]; *data = &joy->data.rx[offset]; break; case JOYSTICK_RY_AXIS: *axis = &joy->info.ry.axis[offset]; *data = &joy->data.ry[offset]; break; case JOYSTICK_RZ_AXIS: *axis = &joy->info.rz.axis[offset]; *data = &joy->data.rz[offset]; break; case JOYSTICK_SLIDER_AXIS: *axis = &joy->info.slider.axis[offset]; *data = &joy->data.slider[offset]; break; case JOYSTICK_VX_AXIS: *axis = &joy->info.vx.axis[offset]; *data = &joy->data.vx[offset]; break; case JOYSTICK_VY_AXIS: *axis = &joy->info.vy.axis[offset]; *data = &joy->data.vy[offset]; break; case JOYSTICK_VZ_AXIS: *axis = &joy->info.vz.axis[offset]; *data = &joy->data.vz[offset]; break; case JOYSTICK_VBRX_AXIS: *axis = &joy->info.vbrx.axis[offset]; *data = &joy->data.vbrx[offset]; break; case JOYSTICK_VBRY_AXIS: *axis = &joy->info.vbry.axis[offset]; *data = &joy->data.vbry[offset]; break; case JOYSTICK_VBRZ_AXIS: *axis = &joy->info.vbrz.axis[offset]; *data = &joy->data.vbrz[offset]; break; case JOYSTICK_VNO_AXIS: *axis = &joy->info.vno.axis[offset]; *data = &joy->data.vno[offset]; break; case JOYSTICK_DIAL_AXIS: *axis = &joy->info.dial.axis[offset]; *data = &joy->data.dial[offset]; break; case JOYSTICK_WHEEL_AXIS: *axis = &joy->info.wheel.axis[offset]; *data = &joy->data.wheel[offset]; break; default: if (vmsJoyVerbose & 1) printf("Illegal Axis Map value 0x%X\n", map); return 0; break; } return SS$_NORMAL; } /* * Routine: forced_full_scale - local scope * * static void forced_full_scale(JOYSTICK_AXIS_INSTANCE *axis) * * Abstract: * * Used to force calibration data to full scale. If the driver did * not return explicit calibration set by the user (usually from a * calibration file), and if the joystick definition has the FORCE * FULL SCALE flag set - then we will force the calibration to be * the full range of the field size. This is used because some * joysticks apparantly return unreliable logical min/max values. * * Inputs: * * axis - pointer to axis instance * * Outputs: * * If no calibration data was read from the driver, the calibration for the * axis is forced to full scale based on the field size. * * Side Effects: * * none * * Returns: * * none */ static void forced_full_scale(JOYSTICK_AXIS_INSTANCE *axis) { if (!axis) return; if (!(axis->flags & JOYSTICK_AXIS_CMIN_VALID)) { axis->flags |= JOYSTICK_AXIS_CMIN_VALID; if (axis->flags & JOYSTICK_AXIS_SIGNED) axis->cmin = - ((1 << (axis->size-1)) - 1); else axis->cmin = 0; } if (!(axis->flags & JOYSTICK_AXIS_CMAX_VALID)) { axis->flags |= JOYSTICK_AXIS_CMAX_VALID; if (axis->flags & JOYSTICK_AXIS_SIGNED) axis->cmax = ((1 << (axis->size-1)) - 1); else axis->cmax = (1 << axis->size) - 1; } } /* * Routine: vmsOpenJoystick - external scope * * int vmsOpenJoystick (JOY_CONTEXT **rjoy) * * Abstract: * * Allocate a joystick context structure and assign a channel * to it. It also calls the logic to read any calibration data * files that are available. * * Inputs: * * rjoy - Address to return joystick context to. * * Outputs: * * Side Effects: * * The joystick structure will be filled in with the limit data, * and if there is a calibration file found - it too will be * read and loaded into the limit data. * * Returns: * * VMS QIO status */ int vmsOpenJoystick (JOY_CONTEXT **rjoy) { /* * Need a clean limit data structure (can't use the one in the * joystick context, or you will combine it with data already in * the driver. */ JOYSTICK_LIMIT_DATA limit = {0}; /* * Test to see if we have a joystick present. If so, return button * and axis count. */ int status = SS$_NORMAL, found, i; JOYSTICK_DEF_ARRAY *joy_array; JOYSTICK_DEFINITION *joydef, *j2def; JOY_CONTEXT *joy = *rjoy; #ifdef PRINTWHERE printf("vmsOpenJoystick\n"); #endif /* * Allocate a joystick structure and open a * channel. If we already have both, then * just return success. */ if (!joy) joy = calloc (1, sizeof(JOY_CONTEXT)); if (!joy) return 0; if (joy->channel != 0) return SS$_NORMAL; /* * Open the channel */ status = vmsAssignJoystick (joy); if (!(status & 1)) return (status); /* * Read the calibration data... First try the logical JOY$CALIB * Then try the file calib.dat current directory, * then the login directory, then joy$calib.dat the system manager directory */ found = vmsJoyGetCalibrationData( "JOY$CALIB", &limit, joy->info.vendor_id, joy->info.product_id, joy->device_name); if (!found) found = vmsJoyGetCalibrationData( "calib.dat", &limit, joy->info.vendor_id, joy->info.product_id, joy->device_name); if (!found) found = vmsJoyGetCalibrationData( "sys$login:calib.dat", &limit, joy->info.vendor_id, joy->info.product_id, joy->device_name); if (!found) found = vmsJoyGetCalibrationData( "sys$manager:joy$calib.dat", &limit, joy->info.vendor_id, joy->info.product_id, joy->device_name); if (found) { vmsSetJoystickCalibration (joy, &limit); } /* Load built in joystick definition table */ if (!joy->joydef_array) { joy_array = joy->joydef_array = calloc (1, sizeof(JOYSTICK_DEF_ARRAY) + (256 * sizeof(void *))); joy_array->max = 256; joy_array->count = 6; joy_array->info[0] = &saitek_evo_record; joy_array->info[1] = &saitek_x45_record; joy_array->info[2] = &logitech_thrustmaster_record; joy_array->info[3] = &logitech_steering_wheel_record; joy_array->info[4] = &gravis_gamepad_record; joy_array->info[5] = µsoft_sidewinder_record; } /* Try to read and load a joystick definition file - just search for our record - don't build a table */ found = vmsJoyGetJoystickDefinition ( joy, "JOY$DEFINITION", &joy->joydef, joy->info.vendor_id, joy->info.product_id); if (!found) found = vmsJoyGetJoystickDefinition ( joy, "joystick.dat", &joy->joydef, joy->info.vendor_id, joy->info.product_id); if (!found) found = vmsJoyGetJoystickDefinition ( joy, "sys$login:joystick.dat", &joy->joydef, joy->info.vendor_id, joy->info.product_id); if (!found) found = vmsJoyGetJoystickDefinition ( joy, "sys$manager:joy$joystick.dat", &joy->joydef, joy->info.vendor_id, joy->info.product_id); joydef = joy->joydef; if (vmsJoyDebug & 1) { if (!found) printf("No joystick definition found\n"); else { printf("%s definition found\n", joydef->product_name); } } /* * Found a match. * First insert our definition into the list */ if (found && joydef) { joy_array = joy->joydef_array; for (i = 0; i < joy_array->count; i += 1) { j2def = joy_array->info[i]; /* * Replace the pointer if this duplicates a built in */ if ((j2def->vendor_id == joydef->vendor_id) && (j2def->product_id == joydef->product_id)) { joy_array->info[i] = joydef; break; } } /* * Add it if it's new */ if (i == joy_array->count) { joy_array->info[i] = joydef; joy_array->count += 1; } /* * Now set the axis information */ getAxisData(joy, joydef->x_map[0], joydef->x_map[1], &joy->x_axis, &joy->x_data); getAxisData(joy, joydef->y_map[0], joydef->y_map[1], &joy->y_axis, &joy->y_data); getAxisData(joy, joydef->z_map[0], joydef->z_map[1], &joy->z_axis, &joy->z_data); getAxisData(joy, joydef->rz_map[0], joydef->rz_map[1], &joy->rz_axis, &joy->rz_data); getAxisData(joy, joydef->throttle_map[0], joydef->throttle_map[1], &joy->throttle_axis, &joy->throttle_data); } else { /* * Look for a built-in version if it wasn't loaded from a file */ joy_array = joy->joydef_array; for (i = 0; i < joy_array->count; i += 1) { j2def = joy_array->info[i]; /* * Replace the pointer if this duplicates a built in */ if (j2def) { if ((j2def->vendor_id == joy->info.vendor_id) && (j2def->product_id == joy->info.product_id)) { /* Found it! */ if (vmsJoyDebug & 0x08) printf("Found %s in built in definitions\n", j2def->product_name); joy->joydef = joydef = joy_array->info[i]; /* * Now set the axis information */ getAxisData(joy, joydef->x_map[0], joydef->x_map[1], &joy->x_axis, &joy->x_data); getAxisData(joy, joydef->y_map[0], joydef->y_map[1], &joy->y_axis, &joy->y_data); getAxisData(joy, joydef->z_map[0], joydef->z_map[1], &joy->z_axis, &joy->z_data); getAxisData(joy, joydef->rz_map[0], joydef->rz_map[1], &joy->rz_axis, &joy->rz_data); getAxisData(joy, joydef->throttle_map[0], joydef->throttle_map[1], &joy->throttle_axis, &joy->throttle_data); break; } } } } /* * None found, apply fallbacks based purely on the number of axes * and a little on what axes are present. The possibilities are too * complex to do "everything" - but this will cover most of the * usual cases. */ if (!(joydef = joy->joydef)) { if (vmsJoyDebug & 0x08) printf("No joystick definition found, apply fallbacks for %d axes\n", joy->info.num_axes); switch (joy->info.num_axes) { case 9: case 8: case 7: case 6: case 5: joy->joydef = &unknown_5_axis_joystick_record; joy->x_axis = &joy->info.x.axis[0]; joy->y_axis = &joy->info.y.axis[0]; joy->z_axis = &joy->info.z.axis[0]; joy->rz_axis = &joy->info.rz.axis[0]; joy->throttle_axis = &joy->info.slider.axis[0]; joy->x_data = &joy->data.x[0]; joy->y_data = &joy->data.y[0]; joy->z_data = &joy->data.z[0]; joy->rz_data = &joy->data.rz[0]; joy->throttle_data = &joy->data.slider[0]; break; case 4: joy->joydef = &unknown_4_axis_joystick_record; joy->x_axis = &joy->info.x.axis[0]; joy->y_axis = &joy->info.y.axis[0]; joy->z_axis = 0; joy->rz_axis = &joy->info.rz.axis[0]; joy->x_data = &joy->data.x[0]; joy->y_data = &joy->data.y[0]; joy->z_data = 0; joy->rz_data = &joy->data.rz[0]; if (joy->info.slider.count) { unknown_4_axis_joystick_record.throttle_map[0] = JOYSTICK_SLIDER_AXIS; joy->throttle_axis = &joy->info.slider.axis[0]; joy->throttle_data = &joy->data.slider[0]; } else { if (joy->info.z.count) { unknown_4_axis_joystick_record.throttle_map[0] = JOYSTICK_Z_AXIS; joy->throttle_axis = &joy->info.z.axis[0]; joy->throttle_data = &joy->data.z[0]; } else { unknown_4_axis_joystick_record.throttle_map[0] = JOYSTICK_NO_AXIS; joy->throttle_axis = 0; joy->throttle_data = 0; } } break; case 3: joy->joydef = &unknown_3_axis_joystick_record; joy->x_axis = &joy->info.x.axis[0]; joy->y_axis = &joy->info.y.axis[0]; joy->z_axis = 0; joy->rz_axis = 0; joy->x_data = &joy->data.x[0]; joy->y_data = &joy->data.y[0]; joy->z_data = 0; joy->rz_data = 0; if (joy->info.slider.count) { unknown_4_axis_joystick_record.throttle_map[0] = JOYSTICK_SLIDER_AXIS; joy->throttle_axis = &joy->info.slider.axis[0]; joy->throttle_data = &joy->data.slider[0]; } else { if (joy->info.z.count) { unknown_4_axis_joystick_record.throttle_map[0] = JOYSTICK_Z_AXIS; joy->throttle_axis = &joy->info.z.axis[0]; joy->throttle_data = &joy->data.z[0]; } else { unknown_4_axis_joystick_record.throttle_map[0] = JOYSTICK_NO_AXIS; joy->throttle_axis = 0; joy->throttle_data = 0; } } break; case 2: default: joy->joydef = &unknown_2_axis_joystick_record; joy->x_axis = &joy->info.x.axis[0]; joy->y_axis = &joy->info.y.axis[0]; joy->z_axis = 0; joy->rz_axis = 0; joy->throttle_axis = 0; joy->x_data = &joy->data.x[0]; joy->y_data = &joy->data.y[0]; joy->z_data = 0; joy->rz_data = 0; joy->throttle_data = 0; break; } joydef = joy->joydef; } /* * Some joysticks send unreliable min/max values. * If there is no calibration data, this flag will * cause us to make the min/max the full range of * the field width */ if (joydef->flags & JOYSTICK_DEF_FULL_SCALE) { if (vmsJoyDebug & 0x08) printf("Forced Full Scale set 0x%X\n", joydef->flags ); forced_full_scale(joy->x_axis); forced_full_scale(joy->y_axis); forced_full_scale(joy->z_axis); forced_full_scale(joy->rz_axis); forced_full_scale(joy->throttle_axis); } /* Tell the users which axes are valid */ joy->axes_valid = 0; if (joydef->x_map[0]) joy->axes_valid |= JOYSTICK_X_AXIS; if (joydef->y_map[0]) joy->axes_valid |= JOYSTICK_Y_AXIS; if (joydef->z_map[0]) joy->axes_valid |= JOYSTICK_Z_AXIS; if (joydef->rz_map[0]) joy->axes_valid |= JOYSTICK_RZ_AXIS; if (joydef->throttle_map[0]) joy->axes_valid |= JOYSTICK_THROTTLE_AXIS; if (*rjoy == 0) *rjoy = joy; return (status); } /* * Routine: vmsReadJoystick - external scope * * void vmsReadJoystick (JOY_CONTEXT **rjoy, int *buttons, int *x, int *y, int *z, int *rz, int *throttle) * * Abstract: * * This routine is used to read data from the joystick, and return the data * to the user. It can optionally call a callback routine to deliver the * data as well. * * If there is no joystick context open yet, one is created. If the channel * isn't opened, we will do it for the consumer. * * This is generally called by the GLUT code to explicitly get joystick data - * and the joystick may or may not have already been opened. * * Inputs: * * rjoy - A pointer to a pointer to a joystick context structure. If this points to a zero, * then a joystick context will be created and returned here * buttons - Massaged buttons (hat is stuck in upper 8 bits of the 32-bit int) * x - Scaled X * y - Scaled Y * z - Scaled Z * rz - Scaled RZ * throttle - Scaled throttle * * Outputs: * * Scaled outputs and buttons, optional callback routine is called. * * Side Effects: * * none * * Returns: * */ void vmsReadJoystick (JOY_CONTEXT **rjoy, int *buttons, int *x, int *y, int *z, int *rz, int *throttle) { int status; JOY_CONTEXT *joy = *rjoy; #ifdef PRINTWHERE printf("vmsReadJoystick\n"); #endif /* * Open the channel */ if ((!joy) || (joy->channel == 0)) { status = vmsOpenJoystick (&joy); } *rjoy = joy; if (status & 1) { vmsReadQIOJoystick(joy); } else { return; } #if VMS_GLUT if (__glutCurrentWindow->joystick) { #else if (joy->callback_address) { #endif /* * Copy old data */ joy->old_buttons = joy->buttons; joy->old_hat = joy->hat; joy->old_x = joy->x; joy->old_y = joy->y; joy->old_z = joy->z; joy->old_rz = joy->rz; joy->old_throttle = joy->throttle; if (x) *x = joy->x; if (y) *y = joy->y; if (z) *z = joy->z; if (rz) *rz = joy->rz; if (throttle) *throttle = joy->throttle; if (buttons) *buttons = joy->buttons; #ifdef VMS_GLUT if (__glutCurrentWindow->joystick) __glutCurrentWindow->joystick ((unsigned int)joy->buttons, joy->x, joy->y, joy->z, joy->rz, joy->throttle, joy); #else if (joy->callback_address) (*joy->callback_address) ((unsigned int)joy->buttons, joy->x, joy->y, joy->z, joy->rz, joy->throttle, joy); #endif } } /* * Routine: vmsStartJoystick - external scope * * int vmsStartJoystick (JOY_CONTEXT **rjoy, int type) * * Abstract: * * Start async or polled input from the joystick * * If there is no joystick context open yet, one is created. If the channel * isn't opened, we will do it for the consumer. * * This is generally called by the GLUT code to start async joystick input - * and the joystick may or may not have already been opened. * * Inputs: * * rjoy - A pointer to a pointer to a joystick context structure. If this points to a zero, * then a joystick context will be created and returned here * * type - JOYSTICK_POLLED (timer based polling) * JOYSTICK_ASYNC (async AST driver notification) * * Outputs: * * Side Effects: * * none * * Returns: * * 1 = Success * 0 = Fail * */ int vmsStartJoystick (JOY_CONTEXT **rjoy, int type, void (*callback_address)(unsigned int buttons, int x, int y, int z, int rz, int throttle, #ifdef VMS_GLUT void *ret_joy)) { #else JOY_CONTEXT *ret_joy)) { #endif int status; JOY_CONTEXT *joy = *rjoy; struct _iosb { short int status; short int msg_len; int unused; } iosb; #ifdef PRINTWHERE printf("vmsStartJoystick\n"); #endif /* * Open the channel */ if ((!joy) || (joy->channel == 0)) { status = vmsOpenJoystick (&joy); } if ((!status & 1) || (!joy)) return 0; if (status) { /* joy->data = (JOYSTICK_INPUT_FRAME) {0}; */ joy->polling = type; joy->buttons = 0; joy->hat = 0; joy->x = 0; joy->y = 0; joy->z = 0; joy->rz = 0; joy->throttle = 0; vmsPollJoystick((int)joy); if (type == JOYSTICK_ASYNC) { status = sys$qiow (0, joy->channel, IO$_SETMODE, &iosb, 0, 0, JOYSTICK_IO_SET_AST_NOTIFY, /* P1 */ 0, /* P2 (not used) */ vmsPollJoystick, /* P3 (AST routine) */ (int) joy, /* P4 (AST Parameter) */ 0, /* P5 (mode, default = PSL$C_USER) */ 0); /* P6 (not used) */ if ((!(status & 1)) && (!(iosb.status & 1))) { printf("Error doing SETMODE for async notification %X\n", status); } } } joy->callback_address = callback_address; if (*rjoy == 0) *rjoy = joy; return (status & 1); } /* * Routine: vmsQueryJoystick - external scope * * int vmsQueryJoystick (int *num_buttons, int *num_axes, int *valid_axes, JOY_CONTEXT **rjoy) * * Abstract: * * Used by GLUT to detect the presence of a joystick. If this is called with * an existing joystick structure, it is used. If not, one will be created. * If the channel is not open when this is called, then it is closed on exit. * * Inputs: * * rjoy - Address to return joystick context structure * * Outputs: * * rjoy - joystick context structure * num_axes - Number of axes * valid_axes - Bitmask of valid axis * num_buttons - number of buttons * * Side Effects: * * none * * Returns: * * 1 = success, 0 = fail * */ int vmsQueryJoystick (int *num_buttons, int *num_axes, int *valid_axes, JOY_CONTEXT **rjoy) { /* * Test to see if we have a joystick present. If so, return button * and axis count. */ int status = 1, deassign = 0; JOY_CONTEXT *joy = *rjoy; if (num_buttons) *num_buttons = 0; if (num_axes) *num_axes = 0; if (valid_axes) *valid_axes = 0; #ifdef PRINTWHERE printf("vmsQueryJoystick\n"); #endif /* * If the channel is open, then leave alone. If it is closed, * open it and close it when we're done */ if ((!joy) || (joy->channel == 0)) { status = vmsOpenJoystick (&joy); deassign = 1; } if (!(status & 1)) return 0; /* * Note, in our context - we are not talking about the * *actual* number of axis, but how many of the standard * 5 we are returning. */ if (num_axes) { if (joy->axes_valid & JOYSTICK_X_AXIS) *num_axes += 1; if (joy->axes_valid & JOYSTICK_Y_AXIS) *num_axes += 1; if (joy->axes_valid & JOYSTICK_Z_AXIS) *num_axes += 1; if (joy->axes_valid & JOYSTICK_RZ_AXIS) *num_axes += 1; if (joy->axes_valid & JOYSTICK_THROTTLE_AXIS) *num_axes += 1; } if (valid_axes) *valid_axes = joy->axes_valid; if (num_buttons) *num_buttons = joy->info.button_count; if (deassign) vmsDeassignJoystick (joy); if (*rjoy == 0) *rjoy = joy; return (status & 1); } #ifdef TESTMODE /* * The following routines are only built when TESTMODE is defined * when built. It creates a main routine and routines to handle * aysnc input and display information about the joystick and the * input data */ /* * Global context for the testmode application to control the loop count */ volatile int done = 0; /* * Routine prototypes for TESTMODE * */ static void got_input(unsigned int buttons, int x, int y, int z, int rz, int throttle, JOY_CONTEXT *joy); static void show_axes(JOYSTICK_AXIS_INFO *axes, char *name); /* * Routine: got_input - static scope for TESTMODE * * static void got_input (unsigned int buttons, int x, int y, int z, int rz, int throttle, JOY_CONTEXT *get_joy) * * Abstract: * * Async callback routine for joystick input * * Inputs: * * Scaled x, y, z, rz, throttle, and the buttons, as well as the joystick context * * Outputs: * * Displays the joystick data * * Side Effects: * * Has a loop count to exit after 1000 times. This is just example code! * * Returns: * */ static void got_input(unsigned int buttons, int x, int y, int z, int rz, int throttle, JOY_CONTEXT *joy) { #ifndef TESTMODE_LOOP_COUNT #define TESTMODE_LOOP_COUNT 1000 #endif int i = 0; char buffer[256], blanks[80] = " "; static int times_done = TESTMODE_LOOP_COUNT; if (joy->axes_valid & JOYSTICK_X_AXIS) i += sprintf(&buffer[i], "x: %d (%d) ", x, *joy->x_data); if (joy->axes_valid & JOYSTICK_Y_AXIS) i += sprintf(&buffer[i], ", y: %d (%d) ", y, *joy->y_data); if (joy->axes_valid & JOYSTICK_Z_AXIS) i += sprintf(&buffer[i], ", z: %d (%d) ", z, *joy->z_data); if (joy->axes_valid & JOYSTICK_RZ_AXIS) i += sprintf(&buffer[i], ", rz: %d (%d) ", rz, *joy->rz_data); if (joy->axes_valid & JOYSTICK_THROTTLE_AXIS) i += sprintf(&buffer[i], ", throttle: %d (%d) ", throttle, *joy->throttle_data); i += sprintf(&buffer[i], "buttons 0x%X", buttons); if (i > 130) { buffer[130] = '\r'; buffer[131] = 0; } else { sprintf(buffer,"%-s%*s\r", buffer, 78-i, blanks); } printf(buffer); times_done -= 1; if (times_done <= 0) done = 1; } /* * Routine: show_axes - static scope for TESTMODE * * static void show_axes (JOYSTICK_AXIS_INFO *axes, char *name) * * Abstract: * * Utility print routine to display axis data * * Inputs: * * Outputs: * * Side Effects: * * none * * Returns: * */ static void show_axes(JOYSTICK_AXIS_INFO *axes, char *name) { int i, slen = 17 - strlen(name); char spacer[] = " .............................."; JOYSTICK_AXIS_INSTANCE *axis; if (axes->count) { printf(" Axis:%8s Count: %d\n", name, axes->count); } else { printf(" Axis:%8s NONE FOUND\n", name); } for (i = 0; i < axes->count; i += 1) { axis = &axes->axis[i]; printf(" - Instance: %d Logical Input %4d to %4d, Size %2d bits, %s\n", i, axis->min, axis->max, axis->size, (axis->flags & JOYSTICK_AXIS_SIGNED) ? "Signed" : "Unsigned"); if (axis->flags & (JOYSTICK_AXIS_PMIN_VALID | JOYSTICK_AXIS_PMAX_VALID)) { printf(" Physical min/max %4d to %4d\n", axis->pmin, axis->pmax); } else { printf(" Physical limits not available\n"); } if (axis->flags & (JOYSTICK_AXIS_UNIT_VALID | JOYSTICK_AXIS_UNIT_VALID)) { printf(" Unit Code %2d\n", axis->unit); } else { printf(" Unit Code not available\n"); } if (axis->flags & (JOYSTICK_AXIS_UNIT_VALID | JOYSTICK_AXIS_UNIT_VALID)) { printf(" Unit Exponent %2d\n", axis->unit_e); } else { printf(" Unit Exponent not available\n"); } if (axis->flags & (JOYSTICK_AXIS_CENTER_OFF_VALID | JOYSTICK_AXIS_CMIN_VALID | JOYSTICK_AXIS_CMAX_VALID)) { if (axis->flags & JOYSTICK_AXIS_CENTER_OFF_VALID) { printf(" Calibration Center %4d\n", axis->center_off); } if (axis->flags & (JOYSTICK_AXIS_CMIN_VALID | JOYSTICK_AXIS_CMAX_VALID)) { printf(" Calibration min/max %4d to %4d\n", axis->cmin, axis->cmax); } } else { printf(" Calibration data not available\n"); } } printf("\n"); } /* * Routine: main - main entry point for TESTMODE * * main () * * Abstract: * * Opens the joystick and displays the limit/calibration information, * then starts async polling for a 1000 inputs. Then closes things * and exits. This is just an example to test the functionality. * * Inputs: * * Outputs: * * Side Effects: * * none * * Returns: * */ main () { int status, i; int x, y, z, rz, throttle, buttons, valid_axes; JOY_CONTEXT *joy = 0; /* Test calibration */ /* Call open instead of Query to leave the channel open */ status = vmsOpenJoystick (&joy); if (!(status & 1)) exit (0); /* Now sense the limit data (not really needed, but it tests the call) */ status = vmsQueryJoystick (0, 0, &valid_axes, &joy); if (!(status & 1)) exit (0); /* * Enable scaling for the standard values */ vmsJoySetScale(joy, JOYSTICK_SCALE_ENABLE | JOYSTICK_SCALE_CENTER, 1000, JOYSTICK_X_AXIS); vmsJoySetScale(joy, JOYSTICK_SCALE_ENABLE | JOYSTICK_SCALE_CENTER, 1000, JOYSTICK_Y_AXIS); vmsJoySetScale(joy, JOYSTICK_SCALE_ENABLE | JOYSTICK_SCALE_CENTER, 1000, JOYSTICK_Z_AXIS); vmsJoySetScale(joy, JOYSTICK_SCALE_ENABLE | JOYSTICK_SCALE_CENTER, 1000, JOYSTICK_RZ_AXIS); vmsJoySetScale(joy, JOYSTICK_SCALE_ENABLE | JOYSTICK_SCALE_CENTER, 1000, JOYSTICK_THROTTLE_AXIS); if (status & 1) { printf("\nJoystick info:\n"); printf("\n"); printf("Info Structure Version: %d.%d\n", joy->info.version, joy->info.minor); printf("\n"); printf(" vendor_id: 0x%4.4X (%5d) - %s\n", joy->info.vendor_id, joy->info.vendor_id, vmsJoyGetVendorName(joy, joy->info.vendor_id)); printf(" product_id: 0x%4.4X (%5d) - %s\n", joy->info.product_id, joy->info.product_id, vmsJoyGetProductName(joy, joy->info.vendor_id, joy->info.product_id)); printf("\n"); printf(" buttons: %d\n", joy->info.button_count); printf(" button mask: 0x%llX\n", joy->info.button_valid); /* Display the raw information, not the synthetic */ printf(" axes: %d\n", joy->info.num_axes); printf("\n"); show_axes( &joy->info.x, "X"); show_axes( &joy->info.y, "Y"); show_axes( &joy->info.z, "Z"); show_axes( &joy->info.rx, "RX"); show_axes( &joy->info.ry, "RY"); show_axes( &joy->info.rz, "RZ"); show_axes( &joy->info.vx, "VX"); show_axes( &joy->info.vy, "VY"); show_axes( &joy->info.vz, "VZ"); show_axes( &joy->info.wheel, "Wheel"); show_axes( &joy->info.slider, "Slider"); show_axes( &joy->info.dial, "Dial"); show_axes( &joy->info.hat, "Hat"); if (joy->info.output_available) { printf(" output_avail: %d\n", joy->info.output_available); printf(" max_bytes: %d\n", joy->info.output_max_bytes); printf(" usage_page: %d\n", joy->info.output_item_usage_page); } printf("\n"); } /* * Read the data to get a valid report back so we can see the structure version */ vmsReadJoystick (&joy, &buttons, &x, &y, &z, &rz, &throttle); printf("Standard returns in: "); if (joy->axes_valid & JOYSTICK_X_AXIS) printf("X"); if (joy->axes_valid & JOYSTICK_Y_AXIS) printf(", Y"); if (joy->axes_valid & JOYSTICK_Z_AXIS) printf(", Z"); if (joy->axes_valid & JOYSTICK_RZ_AXIS) printf(", RZ"); if (joy->axes_valid & JOYSTICK_THROTTLE_AXIS) printf(", Throttle"); printf("\n\n"); printf("Start Async polling\n\n"); /* * if we were doing JOYSTICK_POLLED mode, we would load joy->poll_interval with * a time in ms like 10. But ASYNC mode is much more efficient and responsive */ vmsStartJoystick (&joy, JOYSTICK_ASYNC, got_input); printf("\n"); printf("Data Structure Version: %d.%d\n", joy->data.version, joy->data.minor); printf("\n"); while (!done) { sleep(1); } printf("\n\nStop polling\n"); vmsStopJoystick (joy); joy = 0; exit(0); } #endif