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
34typedef 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
74extern SERIALPORT *serial_open ( const char *device );
75extern void serial_close ( SERIALPORT *port );
76extern int serial_getchar ( SERIALPORT *port );
77extern int serial_putchar ( SERIALPORT *port, unsigned char ch );
78extern void serial_flush ( SERIALPORT *port );
79
80extern void fgPlatformRegisterDialDevice ( const char *dial_device );
81static void send_dial_event(int dial, int value);
82static void poll_dials(int id);
83
84/* local variables */
85static SERIALPORT *dialbox_port=NULL;
86
87/*****************************************************************/
88
89/*
90 * Implementation for glutDeviceGet(GLUT_HAS_DIAL_AND_BUTTON_BOX)
91 */
92int 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 */
108void 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 */
127void 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 */
140static 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
147static 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/********************************************************************/
159static 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