0e92c8ebabb5ad2716793c35d6ca27e4ea2b372b
[smouse] / src / unix / main.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <math.h>
4 #include <errno.h>
5 #include <unistd.h>
6 #include <sys/select.h>
7 #include <X11/Xlib.h>
8 #include <GL/gl.h>
9 #include <GL/glu.h>
10 #include <GL/glx.h>
11 #include "vmath.h"
12 #include "device.h"
13
14 int create_gfx(int xsz, int ysz);
15 void destroy_gfx(void);
16 void set_window_title(const char *title);
17 void redraw(void);
18 void draw_cube(void);
19 int handle_event(XEvent *xev);
20 int handle_dev_event(device_event *ev);
21
22 Display *dpy;
23 Atom wm_prot, wm_del_win;
24 GLXContext ctx;
25 Window win;
26
27 vec3_t pos = {0, 0, -6};
28 quat_t rot = {0, 0, 0, 1};      /* that's 1 + 0i + 0j + 0k */
29
30 int redisplay;
31
32 /* serial 6dof device */
33 struct device *dev;
34
35 int main(void)
36 {
37         int xsock, devfd, maxfd;
38
39         register_all();
40         set_port("/dev/ttyS0");
41
42         if(!(dev = dev_init(0))) {
43                 fprintf(stderr, "failed to initialize 6dof serial device at /dev/ttyS0\n");
44                 return 1;
45         }
46         if((devfd = dev->start()) == -1) {
47                 return 1;
48         }
49
50         if(!(dpy = XOpenDisplay(0))) {
51                 fprintf(stderr, "failed to connect to the X server\n");
52                 return 1;
53         }
54         xsock = ConnectionNumber(dpy);
55
56         if(create_gfx(512, 512) == -1) {
57                 return 1;
58         }
59
60         glEnable(GL_DEPTH_TEST);
61         glEnable(GL_CULL_FACE);
62
63         maxfd = xsock > devfd ? xsock : devfd;
64
65         for(;;) {
66                 fd_set rdset;
67
68                 FD_ZERO(&rdset);
69                 FD_SET(xsock, &rdset);
70                 FD_SET(devfd, &rdset);
71
72                 while(select(maxfd + 1, &rdset, 0, 0, 0) == -1 && errno == EINTR);
73
74                 if(FD_ISSET(xsock, &rdset)) {
75                         while(XPending(dpy)) {
76                                 XEvent xev;
77                                 XNextEvent(dpy, &xev);
78
79                                 if(handle_event(&xev) != 0) {
80                                         goto end;
81                                 }
82                         }
83                 }
84
85                 if(FD_ISSET(devfd, &rdset)) {
86                         device_event ev;
87
88                         while(dev->getevent(&ev)) {
89                                 handle_dev_event(&ev);
90                         }
91                 }
92
93                 if(redisplay) {
94                         redraw();
95                         redisplay = 0;
96                 }
97         }
98
99 end:
100         dev->stop();
101         dev_destroy();
102         destroy_gfx();
103         XCloseDisplay(dpy);
104         return 0;
105 }
106
107 int create_gfx(int xsz, int ysz)
108 {
109         int scr;
110         Window root;
111         XVisualInfo *vis;
112         XSetWindowAttributes xattr;
113         unsigned int events;
114         XClassHint class_hint;
115
116         int attr[] = {
117                 GLX_RGBA, GLX_DOUBLEBUFFER,
118                 GLX_RED_SIZE, 8,
119                 GLX_GREEN_SIZE, 8,
120                 GLX_BLUE_SIZE, 8,
121                 GLX_DEPTH_SIZE, 24,
122                 None
123         };
124
125         wm_prot = XInternAtom(dpy, "WM_PROTOCOLS", False);
126         wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
127
128         scr = DefaultScreen(dpy);
129         root = RootWindow(dpy, scr);
130
131         if(!(vis = glXChooseVisual(dpy, scr, attr))) {
132                 fprintf(stderr, "requested GLX visual is not available\n");
133                 return -1;
134         }
135
136         if(!(ctx = glXCreateContext(dpy, vis, 0, True))) {
137                 fprintf(stderr, "failed to create GLX context\n");
138                 XFree(vis);
139                 return -1;
140         }
141
142         xattr.background_pixel = xattr.border_pixel = BlackPixel(dpy, scr);
143         xattr.colormap = XCreateColormap(dpy, root, vis->visual, AllocNone);
144
145         if(!(win = XCreateWindow(dpy, root, 0, 0, xsz, ysz, 0, vis->depth, InputOutput,
146                                         vis->visual, CWColormap | CWBackPixel | CWBorderPixel, &xattr))) {
147                 fprintf(stderr, "failed to create X window\n");
148                 return -1;
149         }
150         XFree(vis);
151
152         /* set the window event mask */
153         events = ExposureMask | StructureNotifyMask | KeyPressMask | KeyReleaseMask |
154                 ButtonReleaseMask |     ButtonPressMask | PointerMotionMask;
155         XSelectInput(dpy, win, events);
156
157         XSetWMProtocols(dpy, win, &wm_del_win, 1);
158
159         set_window_title("spacemouse cube");
160
161         class_hint.res_name = "cube";
162         class_hint.res_class = "cube";
163         XSetClassHint(dpy, win, &class_hint);
164
165         if(glXMakeCurrent(dpy, win, ctx) == False) {
166                 fprintf(stderr, "glXMakeCurrent failed\n");
167                 glXDestroyContext(dpy, ctx);
168                 XDestroyWindow(dpy, win);
169                 return -1;
170         }
171
172         XMapWindow(dpy, win);
173         XFlush(dpy);
174
175         return 0;
176 }
177
178 void destroy_gfx(void)
179 {
180         glXDestroyContext(dpy, ctx);
181         XDestroyWindow(dpy, win);
182         glXMakeCurrent(dpy, None, 0);
183 }
184
185 void set_window_title(const char *title)
186 {
187         XTextProperty wm_name;
188
189         XStringListToTextProperty((char**)&title, 1, &wm_name);
190         XSetWMName(dpy, win, &wm_name);
191         XSetWMIconName(dpy, win, &wm_name);
192         XFree(wm_name.value);
193 }
194
195 void redraw(void)
196 {
197         mat4_t xform;
198
199         quat_to_mat(xform, rot);
200
201         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
202
203         glMatrixMode(GL_MODELVIEW);
204         glLoadIdentity();
205         glTranslatef(pos.x, pos.y, pos.z);
206         glMultTransposeMatrixf((float*)xform);
207
208         draw_cube();
209
210         glXSwapBuffers(dpy, win);
211 }
212
213 void draw_cube(void)
214 {
215         glBegin(GL_QUADS);
216         /* face +Z */
217         glNormal3f(0, 0, 1);
218         glColor3f(1, 0, 0);
219         glVertex3f(-1, -1, 1);
220         glVertex3f(1, -1, 1);
221         glVertex3f(1, 1, 1);
222         glVertex3f(-1, 1, 1);
223         /* face +X */
224         glNormal3f(1, 0, 0);
225         glColor3f(0, 1, 0);
226         glVertex3f(1, -1, 1);
227         glVertex3f(1, -1, -1);
228         glVertex3f(1, 1, -1);
229         glVertex3f(1, 1, 1);
230         /* face -Z */
231         glNormal3f(0, 0, -1);
232         glColor3f(0, 0, 1);
233         glVertex3f(1, -1, -1);
234         glVertex3f(-1, -1, -1);
235         glVertex3f(-1, 1, -1);
236         glVertex3f(1, 1, -1);
237         /* face -X */
238         glNormal3f(-1, 0, 0);
239         glColor3f(1, 1, 0);
240         glVertex3f(-1, -1, -1);
241         glVertex3f(-1, -1, 1);
242         glVertex3f(-1, 1, 1);
243         glVertex3f(-1, 1, -1);
244         /* face +Y */
245         glNormal3f(0, 1, 0);
246         glColor3f(0, 1, 1);
247         glVertex3f(-1, 1, 1);
248         glVertex3f(1, 1, 1);
249         glVertex3f(1, 1, -1);
250         glVertex3f(-1, 1, -1);
251         /* face -Y */
252         glNormal3f(0, -1, 0);
253         glColor3f(1, 0, 1);
254         glVertex3f(-1, -1, -1);
255         glVertex3f(1, -1, -1);
256         glVertex3f(1, -1, 1);
257         glVertex3f(-1, -1, 1);
258         glEnd();
259 }
260
261 int handle_event(XEvent *xev)
262 {
263         static int win_mapped;
264         KeySym sym;
265
266         switch(xev->type) {
267         case MapNotify:
268                 win_mapped = 1;
269                 break;
270
271         case UnmapNotify:
272                 win_mapped = 0;
273                 break;
274
275         case Expose:
276                 if(win_mapped && xev->xexpose.count == 0) {
277                         redraw();
278                 }
279                 break;
280
281         case KeyPress:
282                 sym = XLookupKeysym((XKeyEvent*)&xev->xkey, 0);
283                 if((sym & 0xff) == 27) {
284                         return 1;
285                 }
286                 break;
287
288         case ConfigureNotify:
289                 {
290                         int x = xev->xconfigure.width;
291                         int y = xev->xconfigure.height;
292
293                         glMatrixMode(GL_PROJECTION);
294                         glLoadIdentity();
295                         gluPerspective(45.0, (float)x / (float)y, 1.0, 1000.0);
296
297                         glViewport(0, 0, x, y);
298                 }
299                 break;
300
301         default:
302                 break;
303         }
304         return 0;
305 }
306
307 #define TX(ev)  ((ev)->motion.motion[0])
308 #define TY(ev)  ((ev)->motion.motion[1])
309 #define TZ(ev)  ((ev)->motion.motion[2])
310 #define RX(ev)  ((ev)->motion.motion[3])
311 #define RY(ev)  ((ev)->motion.motion[4])
312 #define RZ(ev)  ((ev)->motion.motion[5])
313
314 int handle_dev_event(device_event *ev)
315 {
316         switch(ev->type) {
317         case DEV_EV_MOTION:
318                 if(RX(ev) | RY(ev) | RZ(ev)) {
319                         float axis_len = sqrt(RX(ev) * RX(ev) + RY(ev) * RY(ev) + RZ(ev) * RZ(ev));
320                         if(axis_len != 0.0) {
321                                 rot = quat_rotate(rot, axis_len * 0.001, -RX(ev) / axis_len,
322                                                 -RY(ev) / axis_len, -RZ(ev) / axis_len);
323                         }
324                 }
325
326                 pos.x += TX(ev) * 0.001;
327                 pos.y += TY(ev) * 0.001;
328                 pos.z += TZ(ev) * 0.001;
329                 redisplay = 1;
330                 break;
331
332         case DEV_EV_BUTTON:
333                 if(ev->button.pressed) {
334                         pos = v3_cons(0, 0, -6);
335                         rot = quat_cons(1, 0, 0, 0);
336                         redisplay = 1;
337                 }
338                 break;
339         }
340
341         return 0;
342 }