Moving the source code files from 'src' to 'src/Common' as a first step towards separ...
[freeglut] / src / Common / freeglut_input_devices.c
1 /*\r
2  * freeglut_input_devices.c\r
3  *\r
4  * Handles miscellaneous input devices via direct serial-port access.\r
5  * Proper X11 XInput device support is not yet supported.\r
6  * Also lacks Mac support.\r
7  *\r
8  * Written by Joe Krahn <krahn@niehs.nih.gov> 2005\r
9  *\r
10  * Copyright (c) 2005 Stephen J. Baker. All Rights Reserved.\r
11  *\r
12  * Permission is hereby granted, free of charge, to any person obtaining a\r
13  * copy of this software and associated documentation files (the "Software"),\r
14  * to deal in the Software without restriction, including without limitation\r
15  * the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
16  * and/or sell copies of the Software, and to permit persons to whom the\r
17  * Software is furnished to do so, subject to the following conditions:\r
18  *\r
19  * The above copyright notice and this permission notice shall be included\r
20  * in all copies or substantial portions of the Software.\r
21  *\r
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS\r
23  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL\r
25  * PAWEL W. OLSZTA OR STEPHEN J. BAKER BE LIABLE FOR ANY CLAIM, DAMAGES OR\r
26  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\r
27  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\r
28  * DEALINGS IN THE SOFTWARE.\r
29  */\r
30 \r
31 #ifdef HAVE_CONFIG_H\r
32 #    include "config.h"\r
33 #endif\r
34 \r
35 #include <GL/freeglut.h>\r
36 #include "freeglut_internal.h"\r
37 \r
38 #if TARGET_HOST_POSIX_X11\r
39 #ifdef HAVE_ERRNO_H\r
40 #include <errno.h>\r
41 #endif\r
42 #include <sys/ioctl.h>\r
43 #include <stdio.h>\r
44 #include <stdlib.h>\r
45 #include <string.h>\r
46 #include <termios.h>\r
47 #include <fcntl.h>\r
48 \r
49 typedef struct {\r
50    int fd;\r
51    struct termios termio, termio_save;\r
52 } SERIALPORT;\r
53 \r
54 #elif TARGET_HOST_MS_WINDOWS\r
55 #include <sys/types.h>\r
56 #include <winbase.h>\r
57 typedef struct {\r
58    HANDLE fh;\r
59    COMMTIMEOUTS timeouts_save;\r
60    DCB dcb_save;\r
61 } SERIALPORT;\r
62 \r
63 #endif\r
64 \r
65 /********************* Dialbox definitions ***********************/\r
66 \r
67 #define DIAL_NUM_VALUATORS 8\r
68 \r
69 /* dial parser state machine states */\r
70 #define DIAL_NEW                (-1)\r
71 #define DIAL_WHICH_DEVICE       0\r
72 #define DIAL_VALUE_HIGH         1\r
73 #define DIAL_VALUE_LOW          2\r
74 \r
75 /* dial/button box commands */\r
76 #define DIAL_INITIALIZE                 0x20\r
77 #define DIAL_SET_LEDS                   0x75\r
78 #define DIAL_SET_TEXT                   0x61\r
79 #define DIAL_SET_AUTO_DIALS             0x50\r
80 #define DIAL_SET_AUTO_DELTA_DIALS       0x51\r
81 #define DIAL_SET_FILTER                 0x53\r
82 #define DIAL_SET_BUTTONS_MOM_TYPE       0x71\r
83 #define DIAL_SET_AUTO_MOM_BUTTONS       0x73\r
84 #define DIAL_SET_ALL_LEDS               0x4b\r
85 #define DIAL_CLEAR_ALL_LEDS             0x4c\r
86 \r
87 /* dial/button box replies and events */\r
88 #define DIAL_INITIALIZED        0x20\r
89 #define DIAL_BASE               0x30\r
90 #define DIAL_DELTA_BASE         0x40\r
91 #define DIAL_PRESS_BASE         0xc0\r
92 #define DIAL_RELEASE_BASE       0xe0\r
93 \r
94 /* macros to determine reply type */\r
95 #define IS_DIAL_EVENT(ch)       (((ch)>=DIAL_BASE)&&((ch)<DIAL_BASE+DIAL_NUM_VALUATORS))\r
96 #define IS_KEY_PRESS(ch)        (((ch)>=DIAL_PRESS_BASE)&&((ch)<DIAL_PRESS_BASE+DIAL_NUM_BUTTONS))\r
97 #define IS_KEY_RELEASE(ch)      (((ch)>=DIAL_RELEASE_BASE)&&((ch)<DIAL_RELEASE_BASE+DIAL_NUM_BUTTONS))\r
98 #define IS_INIT_EVENT(ch)       ((ch)==DIAL_INITIALIZED)\r
99 \r
100 /*****************************************************************/\r
101 \r
102 static SERIALPORT *serial_open ( const char *device );\r
103 static void serial_close ( SERIALPORT *port );\r
104 static int serial_getchar ( SERIALPORT *port );\r
105 static int serial_putchar ( SERIALPORT *port, unsigned char ch );\r
106 static void serial_flush ( SERIALPORT *port );\r
107 \r
108 static void send_dial_event(int dial, int value);\r
109 static void poll_dials(int id);\r
110 \r
111 /* local variables */\r
112 static SERIALPORT *dialbox_port=NULL;\r
113 \r
114 /*****************************************************************/\r
115 \r
116 /*\r
117  * Implementation for glutDeviceGet(GLUT_HAS_DIAL_AND_BUTTON_BOX)\r
118  */\r
119 int fgInputDeviceDetect( void )\r
120 {\r
121     fgInitialiseInputDevices ();\r
122 \r
123     if ( !dialbox_port )\r
124         return 0;\r
125 \r
126     if ( !fgState.InputDevsInitialised )\r
127         return 0;\r
128 \r
129     return 1;\r
130 }\r
131 \r
132 /*\r
133  * Try initializing the input device(s)\r
134  */\r
135 void fgInitialiseInputDevices ( void )\r
136 {\r
137     if( !fgState.InputDevsInitialised )\r
138     {\r
139         const char *dial_device=NULL;\r
140         dial_device = getenv ( "GLUT_DIALS_SERIAL" );\r
141 #if TARGET_HOST_MS_WINDOWS\r
142         if (!dial_device){\r
143             static char devname[256];\r
144             DWORD size=sizeof(devname);\r
145             DWORD type = REG_SZ;\r
146             HKEY key;\r
147             if (RegOpenKeyA(HKEY_LOCAL_MACHINE,"SOFTWARE\\FreeGLUT",&key)==ERROR_SUCCESS) {\r
148                 if (RegQueryValueExA(key,"DialboxSerialPort",NULL,&type,(LPBYTE)devname,&size)==ERROR_SUCCESS){\r
149                     dial_device=devname;\r
150                 }\r
151                 RegCloseKey(key);\r
152             }\r
153         }\r
154 #endif\r
155         if ( !dial_device ) return;\r
156         if ( !( dialbox_port = serial_open ( dial_device ) ) ) return;\r
157         serial_putchar(dialbox_port,DIAL_INITIALIZE);\r
158         glutTimerFunc ( 10, poll_dials, 0 );\r
159         fgState.InputDevsInitialised = GL_TRUE;\r
160     }\r
161 }\r
162 \r
163 /*\r
164  *\r
165  */\r
166 void fgInputDeviceClose( void )\r
167 {\r
168     if ( fgState.InputDevsInitialised )\r
169     {\r
170         serial_close ( dialbox_port );\r
171         dialbox_port = NULL;\r
172         fgState.InputDevsInitialised = GL_FALSE;\r
173     }\r
174 }\r
175 \r
176 /********************************************************************/\r
177 \r
178 /* Check all windows for dialbox callbacks */\r
179 static void fghcbEnumDialCallbacks ( SFG_Window *window, SFG_Enumerator *enumerator )\r
180 {\r
181     /* Built-in to INVOKE_WCB():  if window->Callbacks[CB_Dials] */\r
182     INVOKE_WCB ( *window,Dials, ( ((int*)enumerator->data)[0], ((int*)enumerator->data)[1]) );\r
183     fgEnumSubWindows ( window, fghcbEnumDialCallbacks, enumerator );\r
184 }\r
185 \r
186 static void send_dial_event ( int num, int value )\r
187 {\r
188     SFG_Enumerator enumerator;\r
189     int data[2];\r
190     data[0] = num;\r
191     data[1] = value;\r
192     enumerator.found = GL_FALSE;\r
193     enumerator.data  =  data;\r
194     fgEnumWindows ( fghcbEnumDialCallbacks, &enumerator );\r
195 }\r
196 \r
197 /********************************************************************/\r
198 static void poll_dials ( int id )\r
199 {\r
200     int data;\r
201     static int dial_state = DIAL_NEW;\r
202     static int dial_which;\r
203     static int dial_value;\r
204     static int dials[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };\r
205 \r
206     if ( !dialbox_port ) return;\r
207 \r
208     while ( (data=serial_getchar(dialbox_port)) != EOF )\r
209     {\r
210         if ( ( dial_state > DIAL_WHICH_DEVICE ) || IS_DIAL_EVENT ( data ) )\r
211         {\r
212             switch ( dial_state )\r
213             {\r
214             case DIAL_WHICH_DEVICE:\r
215                 dial_which = data - DIAL_BASE;\r
216                 dial_state++;\r
217                 break;\r
218             case DIAL_VALUE_HIGH:\r
219                 dial_value = ( data << 8 );\r
220                 dial_state++;\r
221                 break;\r
222             case DIAL_VALUE_LOW:\r
223                 dial_value |= data;\r
224                 if ( dial_value & 0x8000 ) dial_value -= 0x10000;\r
225                 dials[dial_which] = dial_value;\r
226                 send_dial_event ( dial_which + 1, dial_value * 360 / 256 );\r
227                 dial_state = DIAL_WHICH_DEVICE;\r
228                 break;\r
229             default:\r
230                 /* error: Impossible state value! */\r
231                 break;\r
232             }\r
233         }\r
234         else if ( data == DIAL_INITIALIZED )\r
235         {\r
236             fgState.InputDevsInitialised = GL_TRUE;\r
237             dial_state = DIAL_WHICH_DEVICE;\r
238             serial_putchar(dialbox_port,DIAL_SET_AUTO_DIALS);\r
239             serial_putchar(dialbox_port,0xff);\r
240             serial_putchar(dialbox_port,0xff);\r
241         }\r
242         else  /* Unknown data; try flushing. */\r
243             serial_flush(dialbox_port);\r
244     }\r
245 \r
246     glutTimerFunc ( 2, poll_dials, 0 );\r
247 }\r
248 \r
249 \r
250 /******** OS Specific Serial I/O routines *******/\r
251 #if TARGET_HOST_POSIX_X11 /* ==> Linux/BSD/UNIX POSIX serial I/O */\r
252 static SERIALPORT *serial_open ( const char *device )\r
253 {\r
254     int fd;\r
255     struct termios termio;\r
256     SERIALPORT *port;\r
257 \r
258     fd = open(device, O_RDWR | O_NONBLOCK );\r
259     if (fd <0) {\r
260         perror(device);\r
261         return NULL;\r
262     }\r
263 \r
264     port = malloc(sizeof(SERIALPORT));\r
265     memset(port, 0, sizeof(SERIALPORT));\r
266     port->fd = fd;\r
267 \r
268     /* save current port settings */\r
269     tcgetattr(fd,&port->termio_save);\r
270 \r
271     memset(&termio, 0, sizeof(termio));\r
272     termio.c_cflag = CS8 | CREAD | HUPCL ;\r
273     termio.c_iflag = IGNPAR | IGNBRK ;\r
274     termio.c_cc[VTIME]    = 0;   /* inter-character timer */\r
275     termio.c_cc[VMIN]     = 1;   /* block read until 1 chars received, when blocking I/O */\r
276 \r
277     cfsetispeed(&termio, B9600);\r
278     cfsetospeed(&termio, B9600);\r
279     tcsetattr(fd,TCSANOW,&termio);\r
280 \r
281     serial_flush(port);\r
282     return port;\r
283 }\r
284 \r
285 static void serial_close(SERIALPORT *port)\r
286 {\r
287     if (port)\r
288     {\r
289         /* restore old port settings */\r
290         tcsetattr(port->fd,TCSANOW,&port->termio_save);\r
291         close(port->fd);\r
292         free(port);\r
293     }\r
294 }\r
295 \r
296 static int serial_getchar(SERIALPORT *port)\r
297 {\r
298     unsigned char ch;\r
299     if (!port) return EOF;\r
300     if (read(port->fd,&ch,1)) return ch;\r
301     return EOF;\r
302 }\r
303 \r
304 static int serial_putchar(SERIALPORT *port, unsigned char ch){\r
305     if (!port) return 0;\r
306     return write(port->fd,&ch,1);\r
307 }\r
308 \r
309 static void serial_flush ( SERIALPORT *port )\r
310 {\r
311     tcflush ( port->fd, TCIOFLUSH );\r
312 }\r
313 \r
314 #elif TARGET_HOST_MS_WINDOWS\r
315 \r
316 static SERIALPORT *serial_open(const char *device){\r
317     HANDLE fh;\r
318     DCB dcb={sizeof(DCB)};\r
319     COMMTIMEOUTS timeouts;\r
320     SERIALPORT *port;\r
321 \r
322     fh = CreateFile(device,GENERIC_READ|GENERIC_WRITE,0,NULL,\r
323       OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);\r
324     if (!fh) return NULL;\r
325 \r
326     port = malloc(sizeof(SERIALPORT));\r
327     ZeroMemory(port, sizeof(SERIALPORT));\r
328     port->fh = fh;\r
329 \r
330     /* save current port settings */\r
331     GetCommState(fh,&port->dcb_save);\r
332     GetCommTimeouts(fh,&port->timeouts_save);\r
333 \r
334     dcb.DCBlength=sizeof(DCB);\r
335     BuildCommDCB("96,n,8,1",&dcb);\r
336     SetCommState(fh,&dcb);\r
337 \r
338     ZeroMemory(&timeouts,sizeof(timeouts));\r
339     timeouts.ReadTotalTimeoutConstant=1;\r
340     timeouts.WriteTotalTimeoutConstant=1;\r
341     SetCommTimeouts(fh,&timeouts);\r
342 \r
343     serial_flush(port);\r
344 \r
345     return port;\r
346 }\r
347 \r
348 static void serial_close(SERIALPORT *port){\r
349     if (port){\r
350         /* restore old port settings */\r
351         SetCommState(port->fh,&port->dcb_save);\r
352         SetCommTimeouts(port->fh,&port->timeouts_save);\r
353         CloseHandle(port->fh);\r
354         free(port);\r
355     }\r
356 }\r
357 \r
358 static int serial_getchar(SERIALPORT *port){\r
359     DWORD n;\r
360     unsigned char ch;\r
361     if (!port) return EOF;\r
362     if (!ReadFile(port->fh,&ch,1,&n,NULL)) return EOF;\r
363     if (n==1) return ch;\r
364     return EOF;\r
365 }\r
366 \r
367 static int serial_putchar(SERIALPORT *port, unsigned char ch){\r
368     DWORD n;\r
369     if (!port) return 0;\r
370     return WriteFile(port->fh,&ch,1,&n,NULL);\r
371 }\r
372 \r
373 static void serial_flush ( SERIALPORT *port )\r
374 {\r
375     FlushFileBuffers(port->fh);\r
376 }\r
377 \r
378 #endif\r