1 | /* |
2 | * fg_input_devices.c |
3 | * |
4 | * Handles miscellaneous input devices via direct serial-port access. |
5 | * Proper X11 XInput device support is not yet supported. |
6 | * Also lacks Mac support. |
7 | * |
8 | * Written by Joe Krahn <krahn@niehs.nih.gov> 2005 |
9 | * |
10 | * Copyright (c) 2005 Stephen J. Baker. All Rights Reserved. |
11 | * |
12 | * Permission is hereby granted, free of charge, to any person obtaining a |
13 | * copy of this software and associated documentation files (the "Software"), |
14 | * to deal in the Software without restriction, including without limitation |
15 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
16 | * and/or sell copies of the Software, and to permit persons to whom the |
17 | * Software is furnished to do so, subject to the following conditions: |
18 | * |
19 | * The above copyright notice and this permission notice shall be included |
20 | * in all copies or substantial portions of the Software. |
21 | * |
22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
23 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
25 | * PAWEL W. OLSZTA OR STEPHEN J. BAKER BE LIABLE FOR ANY CLAIM, DAMAGES OR |
26 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
27 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
28 | * DEALINGS IN THE SOFTWARE. |
29 | */ |
30 | |
31 | #include <GL/freeglut.h> |
32 | #include "fg_internal.h" |
33 | |
34 | typedef struct _serialport SERIALPORT; |
35 | |
36 | |
37 | /********************* Dialbox definitions ***********************/ |
38 | |
39 | #define DIAL_NUM_VALUATORS 8 |
40 | |
41 | /* dial parser state machine states */ |
42 | #define DIAL_NEW (-1) |
43 | #define DIAL_WHICH_DEVICE 0 |
44 | #define DIAL_VALUE_HIGH 1 |
45 | #define DIAL_VALUE_LOW 2 |
46 | |
47 | /* dial/button box commands */ |
48 | #define DIAL_INITIALIZE 0x20 |
49 | #define DIAL_SET_LEDS 0x75 |
50 | #define DIAL_SET_TEXT 0x61 |
51 | #define DIAL_SET_AUTO_DIALS 0x50 |
52 | #define DIAL_SET_AUTO_DELTA_DIALS 0x51 |
53 | #define DIAL_SET_FILTER 0x53 |
54 | #define DIAL_SET_BUTTONS_MOM_TYPE 0x71 |
55 | #define DIAL_SET_AUTO_MOM_BUTTONS 0x73 |
56 | #define DIAL_SET_ALL_LEDS 0x4b |
57 | #define DIAL_CLEAR_ALL_LEDS 0x4c |
58 | |
59 | /* dial/button box replies and events */ |
60 | #define DIAL_INITIALIZED 0x20 |
61 | #define DIAL_BASE 0x30 |
62 | #define DIAL_DELTA_BASE 0x40 |
63 | #define DIAL_PRESS_BASE 0xc0 |
64 | #define DIAL_RELEASE_BASE 0xe0 |
65 | |
66 | /* macros to determine reply type */ |
67 | #define IS_DIAL_EVENT(ch) (((ch)>=DIAL_BASE)&&((ch)<DIAL_BASE+DIAL_NUM_VALUATORS)) |
68 | #define IS_KEY_PRESS(ch) (((ch)>=DIAL_PRESS_BASE)&&((ch)<DIAL_PRESS_BASE+DIAL_NUM_BUTTONS)) |
69 | #define IS_KEY_RELEASE(ch) (((ch)>=DIAL_RELEASE_BASE)&&((ch)<DIAL_RELEASE_BASE+DIAL_NUM_BUTTONS)) |
70 | #define IS_INIT_EVENT(ch) ((ch)==DIAL_INITIALIZED) |
71 | |
72 | /*****************************************************************/ |
73 | |
74 | extern SERIALPORT *serial_open ( const char *device ); |
75 | extern void serial_close ( SERIALPORT *port ); |
76 | extern int serial_getchar ( SERIALPORT *port ); |
77 | extern int serial_putchar ( SERIALPORT *port, unsigned char ch ); |
78 | extern void serial_flush ( SERIALPORT *port ); |
79 | |
80 | extern void fgPlatformRegisterDialDevice ( const char *dial_device ); |
81 | static void send_dial_event(int dial, int value); |
82 | static void poll_dials(int id); |
83 | |
84 | /* local variables */ |
85 | static SERIALPORT *dialbox_port=NULL; |
86 | |
87 | /*****************************************************************/ |
88 | |
89 | /* |
90 | * Implementation for glutDeviceGet(GLUT_HAS_DIAL_AND_BUTTON_BOX) |
91 | */ |
92 | int fgInputDeviceDetect( void ) |
93 | { |
94 | fgInitialiseInputDevices (); |
95 | |
96 | if ( !dialbox_port ) |
97 | return 0; |
98 | |
99 | if ( !fgState.InputDevsInitialised ) |
100 | return 0; |
101 | |
102 | return 1; |
103 | } |
104 | |
105 | /* |
106 | * Try initializing the input device(s) |
107 | */ |
108 | void fgInitialiseInputDevices ( void ) |
109 | { |
110 | if( !fgState.InputDevsInitialised ) |
111 | { |
112 | const char *dial_device=NULL; |
113 | dial_device = getenv ( "GLUT_DIALS_SERIAL" ); |
114 | fgPlatformRegisterDialDevice ( dial_device ); |
115 | |
116 | if ( !dial_device ) return; |
117 | if ( !( dialbox_port = serial_open ( dial_device ) ) ) return; |
118 | serial_putchar(dialbox_port,DIAL_INITIALIZE); |
119 | glutTimerFunc ( 10, poll_dials, 0 ); |
120 | fgState.InputDevsInitialised = GL_TRUE; |
121 | } |
122 | } |
123 | |
124 | /* |
125 | * |
126 | */ |
127 | void fgInputDeviceClose( void ) |
128 | { |
129 | if ( fgState.InputDevsInitialised ) |
130 | { |
131 | serial_close ( dialbox_port ); |
132 | dialbox_port = NULL; |
133 | fgState.InputDevsInitialised = GL_FALSE; |
134 | } |
135 | } |
136 | |
137 | /********************************************************************/ |
138 | |
139 | /* Check all windows for dialbox callbacks */ |
140 | static void fghcbEnumDialCallbacks ( SFG_Window *window, SFG_Enumerator *enumerator ) |
141 | { |
142 | /* Built-in to INVOKE_WCB(): if window->Callbacks[CB_Dials] */ |
143 | INVOKE_WCB ( *window,Dials, ( ((int*)enumerator->data)[0], ((int*)enumerator->data)[1]) ); |
144 | fgEnumSubWindows ( window, fghcbEnumDialCallbacks, enumerator ); |
145 | } |
146 | |
147 | static void send_dial_event ( int num, int value ) |
148 | { |
149 | SFG_Enumerator enumerator; |
150 | int data[2]; |
151 | data[0] = num; |
152 | data[1] = value; |
153 | enumerator.found = GL_FALSE; |
154 | enumerator.data = data; |
155 | fgEnumWindows ( fghcbEnumDialCallbacks, &enumerator ); |
156 | } |
157 | |
158 | /********************************************************************/ |
159 | static void poll_dials ( int id ) |
160 | { |
161 | int data; |
162 | static int dial_state = DIAL_NEW; |
163 | static int dial_which; |
164 | static int dial_value; |
165 | |
166 | if ( !dialbox_port ) return; |
167 | |
168 | while ( (data=serial_getchar(dialbox_port)) != EOF ) |
169 | { |
170 | if ( ( dial_state > DIAL_WHICH_DEVICE ) || IS_DIAL_EVENT ( data ) ) |
171 | { |
172 | switch ( dial_state ) |
173 | { |
174 | case DIAL_WHICH_DEVICE: |
175 | dial_which = data - DIAL_BASE; |
176 | dial_state++; |
177 | break; |
178 | case DIAL_VALUE_HIGH: |
179 | dial_value = ( data << 8 ); |
180 | dial_state++; |
181 | break; |
182 | case DIAL_VALUE_LOW: |
183 | dial_value |= data; |
184 | if ( dial_value & 0x8000 ) dial_value -= 0x10000; |
185 | send_dial_event ( dial_which + 1, dial_value * 360 / 256 ); |
186 | dial_state = DIAL_WHICH_DEVICE; |
187 | break; |
188 | default: |
189 | /* error: Impossible state value! */ |
190 | break; |
191 | } |
192 | } |
193 | else if ( data == DIAL_INITIALIZED ) |
194 | { |
195 | fgState.InputDevsInitialised = GL_TRUE; |
196 | dial_state = DIAL_WHICH_DEVICE; |
197 | serial_putchar(dialbox_port,DIAL_SET_AUTO_DIALS); |
198 | serial_putchar(dialbox_port,0xff); |
199 | serial_putchar(dialbox_port,0xff); |
200 | } |
201 | else /* Unknown data; try flushing. */ |
202 | serial_flush(dialbox_port); |
203 | } |
204 | |
205 | glutTimerFunc ( 2, poll_dials, 0 ); |
206 | } |
207 | |
208 | |