See e-mail from Martin Payne dated 11/30/09 at 5:51 AM. These changes include changi...
[freeglut] / src / freeglut_input_devices.c
1 /*
2  * freeglut_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 #ifdef HAVE_CONFIG_H
32 #    include "config.h"
33 #endif
34
35 #include <GL/freeglut.h>
36 #include "freeglut_internal.h"
37
38 #if TARGET_HOST_POSIX_X11
39 #if HAVE_ERRNO_H
40 #include <errno.h>
41 #endif
42 #include <sys/ioctl.h>
43 #include <sys/time.h>
44 #include <time.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <unistd.h>
48 #include <string.h>
49 #include <termios.h>
50 #include <fcntl.h>
51 #include <sys/types.h>
52
53 typedef struct {
54    int fd;
55    struct termios termio, termio_save;
56 } SERIALPORT;
57
58 #elif TARGET_HOST_MS_WINDOWS
59 #include <sys/types.h>
60 #include <winbase.h>
61 typedef struct {
62    HANDLE fh;
63    COMMTIMEOUTS timeouts_save;
64    DCB dcb_save;
65 } SERIALPORT;
66
67 #endif
68
69 /********************* Dialbox definitions ***********************/
70
71 #define DIAL_NUM_VALUATORS 8
72
73 /* dial parser state machine states */
74 #define DIAL_NEW                (-1)
75 #define DIAL_WHICH_DEVICE       0
76 #define DIAL_VALUE_HIGH         1
77 #define DIAL_VALUE_LOW          2
78
79 /* dial/button box commands */
80 #define DIAL_INITIALIZE                 0x20
81 #define DIAL_SET_LEDS                   0x75
82 #define DIAL_SET_TEXT                   0x61
83 #define DIAL_SET_AUTO_DIALS             0x50
84 #define DIAL_SET_AUTO_DELTA_DIALS       0x51
85 #define DIAL_SET_FILTER                 0x53
86 #define DIAL_SET_BUTTONS_MOM_TYPE       0x71
87 #define DIAL_SET_AUTO_MOM_BUTTONS       0x73
88 #define DIAL_SET_ALL_LEDS               0x4b
89 #define DIAL_CLEAR_ALL_LEDS             0x4c
90
91 /* dial/button box replies and events */
92 #define DIAL_INITIALIZED        0x20
93 #define DIAL_BASE               0x30
94 #define DIAL_DELTA_BASE         0x40
95 #define DIAL_PRESS_BASE         0xc0
96 #define DIAL_RELEASE_BASE       0xe0
97
98 /* macros to determine reply type */
99 #define IS_DIAL_EVENT(ch)       (((ch)>=DIAL_BASE)&&((ch)<DIAL_BASE+DIAL_NUM_VALUATORS))
100 #define IS_KEY_PRESS(ch)        (((ch)>=DIAL_PRESS_BASE)&&((ch)<DIAL_PRESS_BASE+DIAL_NUM_BUTTONS))
101 #define IS_KEY_RELEASE(ch)      (((ch)>=DIAL_RELEASE_BASE)&&((ch)<DIAL_RELEASE_BASE+DIAL_NUM_BUTTONS))
102 #define IS_INIT_EVENT(ch)       ((ch)==DIAL_INITIALIZED)
103
104 /*****************************************************************/
105
106 static SERIALPORT *serial_open ( const char *device );
107 static void serial_close ( SERIALPORT *port );
108 static int serial_getchar ( SERIALPORT *port );
109 static int serial_putchar ( SERIALPORT *port, unsigned char ch );
110 static void serial_flush ( SERIALPORT *port );
111
112 static void send_dial_event(int dial, int value);
113 static void poll_dials(int id);
114
115 /* local variables */
116 static SERIALPORT *dialbox_port=NULL;
117
118 /*****************************************************************/
119
120 /*
121  * Implementation for glutDeviceGet(GLUT_HAS_DIAL_AND_BUTTON_BOX)
122  */
123 int fgInputDeviceDetect( void )
124 {
125     fgInitialiseInputDevices ();
126
127     if ( !dialbox_port )
128         return 0;
129
130     if ( !fgState.InputDevsInitialised )
131         return 0;
132
133     return 1;
134 }
135
136 /*
137  * Try initializing the input device(s)
138  */
139 void fgInitialiseInputDevices ( void )
140 {
141     if( !fgState.InputDevsInitialised )
142     {
143         const char *dial_device=NULL;
144         dial_device = getenv ( "GLUT_DIALS_SERIAL" );
145 #if TARGET_HOST_MS_WINDOWS
146         if (!dial_device){
147             static char devname[256];
148             DWORD size=sizeof(devname);
149             DWORD type = REG_SZ;
150             HKEY key;
151             if (RegOpenKeyA(HKEY_LOCAL_MACHINE,"SOFTWARE\\FreeGLUT",&key)==ERROR_SUCCESS) {
152                 if (RegQueryValueExA(key,"DialboxSerialPort",NULL,&type,(LPBYTE)devname,&size)==ERROR_SUCCESS){
153                     dial_device=devname;
154                 }
155                 RegCloseKey(key);
156             }
157         }
158 #endif
159         if ( !dial_device ) return;
160         if ( !( dialbox_port = serial_open ( dial_device ) ) ) return;
161         serial_putchar(dialbox_port,DIAL_INITIALIZE);
162         glutTimerFunc ( 10, poll_dials, 0 );
163         fgState.InputDevsInitialised = GL_TRUE;
164     }
165 }
166
167 /*
168  *
169  */
170 void fgInputDeviceClose( void )
171 {
172     if ( fgState.InputDevsInitialised )
173     {
174         serial_close ( dialbox_port );
175         dialbox_port = NULL;
176         fgState.InputDevsInitialised = GL_FALSE;
177     }
178 }
179
180 /********************************************************************/
181
182 /* Check all windows for dialbox callbacks */
183 static void fghcbEnumDialCallbacks ( SFG_Window *window, SFG_Enumerator *enumerator )
184 {
185     /* Built-in to INVOKE_WCB():  if window->Callbacks[CB_Dials] */
186     INVOKE_WCB ( *window,Dials, ( ((int*)enumerator->data)[0], ((int*)enumerator->data)[1]) );
187     fgEnumSubWindows ( window, fghcbEnumDialCallbacks, enumerator );
188 }
189
190 static void send_dial_event ( int num, int value )
191 {
192     SFG_Enumerator enumerator;
193     int data[2];
194     data[0] = num;
195     data[1] = value;
196     enumerator.found = GL_FALSE;
197     enumerator.data  =  data;
198     fgEnumWindows ( fghcbEnumDialCallbacks, &enumerator );
199 }
200
201 /********************************************************************/
202 static void poll_dials ( int id )
203 {
204     int data;
205     static int dial_state = DIAL_NEW;
206     static int dial_which;
207     static int dial_value;
208     static int dials[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
209
210     if ( !dialbox_port ) return;
211
212     while ( (data=serial_getchar(dialbox_port)) != EOF )
213     {
214         if ( ( dial_state > DIAL_WHICH_DEVICE ) || IS_DIAL_EVENT ( data ) )
215         {
216             switch ( dial_state )
217             {
218             case DIAL_WHICH_DEVICE:
219                 dial_which = data - DIAL_BASE;
220                 dial_state++;
221                 break;
222             case DIAL_VALUE_HIGH:
223                 dial_value = ( data << 8 );
224                 dial_state++;
225                 break;
226             case DIAL_VALUE_LOW:
227                 dial_value |= data;
228                 if ( dial_value & 0x8000 ) dial_value -= 0x10000;
229                 dials[dial_which] = dial_value;
230                 send_dial_event ( dial_which + 1, dial_value * 360 / 256 );
231                 dial_state = DIAL_WHICH_DEVICE;
232                 break;
233             default:
234                 /* error: Impossible state value! */
235                 break;
236             }
237         }
238         else if ( data == DIAL_INITIALIZED )
239         {
240             fgState.InputDevsInitialised = GL_TRUE;
241             dial_state = DIAL_WHICH_DEVICE;
242             serial_putchar(dialbox_port,DIAL_SET_AUTO_DIALS);
243             serial_putchar(dialbox_port,0xff);
244             serial_putchar(dialbox_port,0xff);
245         }
246         else  /* Unknown data; try flushing. */
247             serial_flush(dialbox_port);
248     }
249
250     glutTimerFunc ( 2, poll_dials, 0 );
251 }
252
253
254 /******** OS Specific Serial I/O routines *******/
255 #if TARGET_HOST_POSIX_X11 /* ==> Linux/BSD/UNIX POSIX serial I/O */
256 static SERIALPORT *serial_open ( const char *device )
257 {
258     int fd;
259     struct termios termio;
260     SERIALPORT *port;
261
262     fd = open(device, O_RDWR | O_NONBLOCK );
263     if (fd <0) {
264         perror(device);
265         return NULL;
266     }
267
268     port = malloc(sizeof(SERIALPORT));
269     memset(port, 0, sizeof(SERIALPORT));
270     port->fd = fd;
271
272     /* save current port settings */
273     tcgetattr(fd,&port->termio_save);
274
275     memset(&termio, 0, sizeof(termio));
276     termio.c_cflag = CS8 | CREAD | HUPCL ;
277     termio.c_iflag = IGNPAR | IGNBRK ;
278     termio.c_cc[VTIME]    = 0;   /* inter-character timer */
279     termio.c_cc[VMIN]     = 1;   /* block read until 1 chars received, when blocking I/O */
280
281     cfsetispeed(&termio, B9600);
282     cfsetospeed(&termio, B9600);
283     tcsetattr(fd,TCSANOW,&termio);
284
285     serial_flush(port);
286     return port;
287 }
288
289 static void serial_close(SERIALPORT *port)
290 {
291     if (port)
292     {
293         /* restore old port settings */
294         tcsetattr(port->fd,TCSANOW,&port->termio_save);
295         close(port->fd);
296         free(port);
297     }
298 }
299
300 static int serial_getchar(SERIALPORT *port)
301 {
302     unsigned char ch;
303     if (!port) return EOF;
304     if (read(port->fd,&ch,1)) return ch;
305     return EOF;
306 }
307
308 static int serial_putchar(SERIALPORT *port, unsigned char ch){
309     if (!port) return 0;
310     return write(port->fd,&ch,1);
311 }
312
313 static void serial_flush ( SERIALPORT *port )
314 {
315     tcflush ( port->fd, TCIOFLUSH );
316 }
317
318 #elif TARGET_HOST_MS_WINDOWS
319
320 static SERIALPORT *serial_open(const char *device){
321     HANDLE fh;
322     DCB dcb={sizeof(DCB)};
323     COMMTIMEOUTS timeouts;
324     SERIALPORT *port;
325
326     fh = CreateFile(device,GENERIC_READ|GENERIC_WRITE,0,NULL,
327       OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
328     if (!fh) return NULL;
329
330     port = malloc(sizeof(SERIALPORT));
331     ZeroMemory(port, sizeof(SERIALPORT));
332     port->fh = fh;
333
334     /* save current port settings */
335     GetCommState(fh,&port->dcb_save);
336     GetCommTimeouts(fh,&port->timeouts_save);
337
338     dcb.DCBlength=sizeof(DCB);
339     BuildCommDCB("96,n,8,1",&dcb);
340     SetCommState(fh,&dcb);
341
342     ZeroMemory(&timeouts,sizeof(timeouts));
343     timeouts.ReadTotalTimeoutConstant=1;
344     timeouts.WriteTotalTimeoutConstant=1;
345     SetCommTimeouts(fh,&timeouts);
346
347     serial_flush(port);
348
349     return port;
350 }
351
352 static void serial_close(SERIALPORT *port){
353     if (port){
354         /* restore old port settings */
355         SetCommState(port->fh,&port->dcb_save);
356         SetCommTimeouts(port->fh,&port->timeouts_save);
357         CloseHandle(port->fh);
358         free(port);
359     }
360 }
361
362 static int serial_getchar(SERIALPORT *port){
363     DWORD n;
364     unsigned char ch;
365     if (!port) return EOF;
366     if (!ReadFile(port->fh,&ch,1,&n,NULL)) return EOF;
367     if (n==1) return ch;
368     return EOF;
369 }
370
371 static int serial_putchar(SERIALPORT *port, unsigned char ch){
372     DWORD n;
373     if (!port) return 0;
374     return WriteFile(port->fh,&ch,1,&n,NULL);
375 }
376
377 static void serial_flush ( SERIALPORT *port )
378 {
379     FlushFileBuffers(port->fh);
380 }
381
382 #endif