a936a74ed76fd03f05a812abbbb5b3c9b22ff71a
[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         destroy_gfx();
101         XCloseDisplay(dpy);
102         return 0;
103 }
104
105 int create_gfx(int xsz, int ysz)
106 {
107         int scr;
108         Window root;
109         XVisualInfo *vis;
110         XSetWindowAttributes xattr;
111         unsigned int events;
112         XClassHint class_hint;
113
114         int attr[] = {
115                 GLX_RGBA, GLX_DOUBLEBUFFER,
116                 GLX_RED_SIZE, 8,
117                 GLX_GREEN_SIZE, 8,
118                 GLX_BLUE_SIZE, 8,
119                 GLX_DEPTH_SIZE, 24,
120                 None
121         };
122
123         wm_prot = XInternAtom(dpy, "WM_PROTOCOLS", False);
124         wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
125
126         scr = DefaultScreen(dpy);
127         root = RootWindow(dpy, scr);
128
129         if(!(vis = glXChooseVisual(dpy, scr, attr))) {
130                 fprintf(stderr, "requested GLX visual is not available\n");
131                 return -1;
132         }
133
134         if(!(ctx = glXCreateContext(dpy, vis, 0, True))) {
135                 fprintf(stderr, "failed to create GLX context\n");
136                 XFree(vis);
137                 return -1;
138         }
139
140         xattr.background_pixel = xattr.border_pixel = BlackPixel(dpy, scr);
141         xattr.colormap = XCreateColormap(dpy, root, vis->visual, AllocNone);
142
143         if(!(win = XCreateWindow(dpy, root, 0, 0, xsz, ysz, 0, vis->depth, InputOutput,
144                                         vis->visual, CWColormap | CWBackPixel | CWBorderPixel, &xattr))) {
145                 fprintf(stderr, "failed to create X window\n");
146                 return -1;
147         }
148         XFree(vis);
149
150         /* set the window event mask */
151         events = ExposureMask | StructureNotifyMask | KeyPressMask | KeyReleaseMask |
152                 ButtonReleaseMask |     ButtonPressMask | PointerMotionMask;
153         XSelectInput(dpy, win, events);
154
155         XSetWMProtocols(dpy, win, &wm_del_win, 1);
156
157         set_window_title("libspnav cube");
158
159         class_hint.res_name = "cube";
160         class_hint.res_class = "cube";
161         XSetClassHint(dpy, win, &class_hint);
162
163         if(glXMakeCurrent(dpy, win, ctx) == False) {
164                 fprintf(stderr, "glXMakeCurrent failed\n");
165                 glXDestroyContext(dpy, ctx);
166                 XDestroyWindow(dpy, win);
167                 return -1;
168         }
169
170         XMapWindow(dpy, win);
171         XFlush(dpy);
172
173         return 0;
174 }
175
176 void destroy_gfx(void)
177 {
178         glXDestroyContext(dpy, ctx);
179         XDestroyWindow(dpy, win);
180         glXMakeCurrent(dpy, None, 0);
181 }
182
183 void set_window_title(const char *title)
184 {
185         XTextProperty wm_name;
186
187         XStringListToTextProperty((char**)&title, 1, &wm_name);
188         XSetWMName(dpy, win, &wm_name);
189         XSetWMIconName(dpy, win, &wm_name);
190         XFree(wm_name.value);
191 }
192
193 void redraw(void)
194 {
195         mat4_t xform;
196
197         quat_to_mat(xform, rot);
198
199         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
200
201         glMatrixMode(GL_MODELVIEW);
202         glLoadIdentity();
203         glTranslatef(pos.x, pos.y, pos.z);
204         glMultTransposeMatrixf((float*)xform);
205
206         draw_cube();
207
208         glXSwapBuffers(dpy, win);
209 }
210
211 void draw_cube(void)
212 {
213         glBegin(GL_QUADS);
214         /* face +Z */
215         glNormal3f(0, 0, 1);
216         glColor3f(1, 0, 0);
217         glVertex3f(-1, -1, 1);
218         glVertex3f(1, -1, 1);
219         glVertex3f(1, 1, 1);
220         glVertex3f(-1, 1, 1);
221         /* face +X */
222         glNormal3f(1, 0, 0);
223         glColor3f(0, 1, 0);
224         glVertex3f(1, -1, 1);
225         glVertex3f(1, -1, -1);
226         glVertex3f(1, 1, -1);
227         glVertex3f(1, 1, 1);
228         /* face -Z */
229         glNormal3f(0, 0, -1);
230         glColor3f(0, 0, 1);
231         glVertex3f(1, -1, -1);
232         glVertex3f(-1, -1, -1);
233         glVertex3f(-1, 1, -1);
234         glVertex3f(1, 1, -1);
235         /* face -X */
236         glNormal3f(-1, 0, 0);
237         glColor3f(1, 1, 0);
238         glVertex3f(-1, -1, -1);
239         glVertex3f(-1, -1, 1);
240         glVertex3f(-1, 1, 1);
241         glVertex3f(-1, 1, -1);
242         /* face +Y */
243         glNormal3f(0, 1, 0);
244         glColor3f(0, 1, 1);
245         glVertex3f(-1, 1, 1);
246         glVertex3f(1, 1, 1);
247         glVertex3f(1, 1, -1);
248         glVertex3f(-1, 1, -1);
249         /* face -Y */
250         glNormal3f(0, -1, 0);
251         glColor3f(1, 0, 1);
252         glVertex3f(-1, -1, -1);
253         glVertex3f(1, -1, -1);
254         glVertex3f(1, -1, 1);
255         glVertex3f(-1, -1, 1);
256         glEnd();
257 }
258
259 int handle_event(XEvent *xev)
260 {
261         static int win_mapped;
262         KeySym sym;
263
264         switch(xev->type) {
265         case MapNotify:
266                 win_mapped = 1;
267                 break;
268
269         case UnmapNotify:
270                 win_mapped = 0;
271                 break;
272
273         case Expose:
274                 if(win_mapped && xev->xexpose.count == 0) {
275                         redraw();
276                 }
277                 break;
278
279         case KeyPress:
280                 sym = XLookupKeysym((XKeyEvent*)&xev->xkey, 0);
281                 if((sym & 0xff) == 27) {
282                         return 1;
283                 }
284                 break;
285
286         case ConfigureNotify:
287                 {
288                         int x = xev->xconfigure.width;
289                         int y = xev->xconfigure.height;
290
291                         glMatrixMode(GL_PROJECTION);
292                         glLoadIdentity();
293                         gluPerspective(45.0, (float)x / (float)y, 1.0, 1000.0);
294
295                         glViewport(0, 0, x, y);
296                 }
297                 break;
298
299         default:
300                 break;
301         }
302         return 0;
303 }
304
305 #define TX(ev)  ((ev)->motion.motion[0])
306 #define TY(ev)  ((ev)->motion.motion[1])
307 #define TZ(ev)  ((ev)->motion.motion[2])
308 #define RX(ev)  ((ev)->motion.motion[3])
309 #define RY(ev)  ((ev)->motion.motion[4])
310 #define RZ(ev)  ((ev)->motion.motion[5])
311
312 int handle_dev_event(device_event *ev)
313 {
314         switch(ev->type) {
315         case DEV_EV_MOTION:
316                 if(RX(ev) | RY(ev) | RZ(ev)) {
317                         float axis_len = sqrt(RX(ev) * RX(ev) + RY(ev) * RY(ev) + RZ(ev) * RZ(ev));
318                         if(axis_len != 0.0) {
319                                 rot = quat_rotate(rot, axis_len * 0.001, -RX(ev) / axis_len,
320                                                 -RY(ev) / axis_len, -RZ(ev) / axis_len);
321                         }
322                 }
323
324                 pos.x += TX(ev) * 0.001;
325                 pos.y += TY(ev) * 0.001;
326                 pos.z += TZ(ev) * 0.001;
327                 redisplay = 1;
328                 break;
329
330         case DEV_EV_BUTTON:
331                 if(ev->button.pressed) {
332                         pos = v3_cons(0, 0, -6);
333                         rot = quat_cons(1, 0, 0, 0);
334                         redisplay = 1;
335                 }
336                 break;
337         }
338
339         return 0;
340 }