rev |
line source |
nuclear@12
|
1 /*
|
nuclear@12
|
2 eqemu - electronic queue system emulator
|
nuclear@12
|
3 Copyright (C) 2014 John Tsiombikas <nuclear@member.fsf.org>,
|
nuclear@12
|
4 Eleni-Maria Stea <eleni@mutantstargoat.com>
|
nuclear@12
|
5
|
nuclear@12
|
6 This program is free software: you can redistribute it and/or modify
|
nuclear@12
|
7 it under the terms of the GNU General Public License as published by
|
nuclear@12
|
8 the Free Software Foundation, either version 3 of the License, or
|
nuclear@12
|
9 (at your option) any later version.
|
nuclear@12
|
10
|
nuclear@12
|
11 This program is distributed in the hope that it will be useful,
|
nuclear@12
|
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
nuclear@12
|
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
nuclear@12
|
14 GNU General Public License for more details.
|
nuclear@12
|
15
|
nuclear@12
|
16 You should have received a copy of the GNU General Public License
|
nuclear@12
|
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
|
nuclear@12
|
18 */
|
nuclear@0
|
19 #include <stdio.h>
|
nuclear@0
|
20 #include <stdlib.h>
|
nuclear@4
|
21 #include <float.h>
|
nuclear@0
|
22 #include <assert.h>
|
nuclear@0
|
23 #include <errno.h>
|
nuclear@0
|
24 #include <unistd.h>
|
nuclear@0
|
25 #include <sys/select.h>
|
nuclear@0
|
26 #include <GL/glew.h>
|
nuclear@0
|
27 #include <X11/Xlib.h>
|
nuclear@0
|
28 #include <GL/glx.h>
|
nuclear@1
|
29 #include "dev.h"
|
nuclear@4
|
30 #include "scene.h"
|
nuclear@7
|
31 #include "timer.h"
|
nuclear@7
|
32 #include "fblur.h"
|
nuclear@7
|
33
|
nuclear@7
|
34
|
nuclear@7
|
35 enum {
|
nuclear@7
|
36 REGULAR_PASS,
|
nuclear@7
|
37 GLOW_PASS
|
nuclear@7
|
38 };
|
nuclear@0
|
39
|
nuclear@11
|
40 void post_redisplay();
|
nuclear@0
|
41 static bool init();
|
nuclear@0
|
42 static void cleanup();
|
nuclear@0
|
43 static void display();
|
nuclear@7
|
44 static void draw_scene(int pass = REGULAR_PASS);
|
nuclear@7
|
45 static void post_glow(void);
|
nuclear@0
|
46 static void keyb(int key, bool pressed);
|
nuclear@0
|
47 static void mouse(int bn, bool pressed, int x, int y);
|
nuclear@0
|
48 static void motion(int x, int y);
|
nuclear@4
|
49 static Ray calc_pick_ray(int x, int y);
|
nuclear@7
|
50 static int next_pow2(int x);
|
nuclear@0
|
51
|
nuclear@0
|
52 static Window create_window(const char *title, int xsz, int ysz);
|
nuclear@0
|
53 static void process_events();
|
nuclear@0
|
54 static int translate_keysym(KeySym sym);
|
nuclear@0
|
55
|
nuclear@2
|
56 static int proc_args(int argc, char **argv);
|
nuclear@2
|
57
|
nuclear@0
|
58 static Display *dpy;
|
nuclear@0
|
59 static Window win;
|
nuclear@0
|
60 static GLXContext ctx;
|
nuclear@0
|
61 static Atom xa_wm_prot, xa_wm_del_win;
|
nuclear@0
|
62
|
nuclear@0
|
63 static int win_width, win_height;
|
nuclear@0
|
64
|
nuclear@4
|
65 static bool draw_pending;
|
nuclear@0
|
66 static bool win_mapped;
|
nuclear@0
|
67
|
nuclear@2
|
68 static int fakefd = -1;
|
nuclear@2
|
69 static char *fake_devpath;
|
nuclear@2
|
70
|
nuclear@4
|
71 static float cam_theta, cam_phi, cam_dist = 140;
|
nuclear@4
|
72 static Scene *scn;
|
nuclear@4
|
73
|
nuclear@4
|
74 enum { BN_TICKET, BN_NEXT, NUM_BUTTONS };
|
nuclear@6
|
75 static const char *button_names[] = { "button1", "button2" };
|
nuclear@4
|
76 static Object *button_obj[NUM_BUTTONS];
|
nuclear@6
|
77 static Object *disp_obj[2];
|
nuclear@6
|
78 static Object *led_obj[2];
|
nuclear@6
|
79 static Vector3 led_on_emissive;
|
nuclear@4
|
80
|
nuclear@7
|
81 static bool opt_use_glow = true;
|
nuclear@7
|
82 #define GLOW_SZ_DIV 3
|
nuclear@7
|
83 static unsigned int glow_tex;
|
nuclear@7
|
84 static int glow_tex_xsz, glow_tex_ysz, glow_xsz, glow_ysz;
|
nuclear@7
|
85 static int glow_iter = 1;
|
nuclear@7
|
86 static int blur_size = 5;
|
nuclear@7
|
87 unsigned char *glow_framebuf;
|
nuclear@7
|
88
|
nuclear@7
|
89
|
nuclear@2
|
90 int main(int argc, char **argv)
|
nuclear@0
|
91 {
|
nuclear@2
|
92 if(proc_args(argc, argv) == -1) {
|
nuclear@2
|
93 return 1;
|
nuclear@2
|
94 }
|
nuclear@0
|
95 if(!init()) {
|
nuclear@0
|
96 return 1;
|
nuclear@0
|
97 }
|
nuclear@0
|
98 atexit(cleanup);
|
nuclear@0
|
99
|
nuclear@0
|
100 int xfd = ConnectionNumber(dpy);
|
nuclear@0
|
101
|
nuclear@4
|
102 // run once through pending events before going into the select loop
|
nuclear@4
|
103 process_events();
|
nuclear@4
|
104
|
nuclear@0
|
105 for(;;) {
|
nuclear@0
|
106 fd_set rd;
|
nuclear@0
|
107 FD_ZERO(&rd);
|
nuclear@0
|
108
|
nuclear@0
|
109 FD_SET(xfd, &rd);
|
nuclear@2
|
110 FD_SET(fakefd, &rd);
|
nuclear@0
|
111
|
nuclear@4
|
112 struct timeval noblock = {0, 0};
|
nuclear@2
|
113 int maxfd = xfd > fakefd ? xfd : fakefd;
|
nuclear@9
|
114 while(!XPending(dpy) && select(maxfd + 1, &rd, 0, 0, draw_pending ? &noblock : 0) == -1 && errno == EINTR);
|
nuclear@0
|
115
|
nuclear@9
|
116 if(XPending(dpy) || FD_ISSET(xfd, &rd)) {
|
nuclear@0
|
117 process_events();
|
nuclear@0
|
118 }
|
nuclear@2
|
119 if(FD_ISSET(fakefd, &rd)) {
|
nuclear@2
|
120 proc_dev_input();
|
nuclear@2
|
121 }
|
nuclear@0
|
122
|
nuclear@4
|
123 if(draw_pending) {
|
nuclear@7
|
124 draw_pending = false;
|
nuclear@0
|
125 display();
|
nuclear@0
|
126 }
|
nuclear@0
|
127 }
|
nuclear@0
|
128 return 0;
|
nuclear@0
|
129 }
|
nuclear@0
|
130
|
nuclear@11
|
131 void post_redisplay()
|
nuclear@11
|
132 {
|
nuclear@11
|
133 draw_pending = true;
|
nuclear@11
|
134 }
|
nuclear@11
|
135
|
nuclear@0
|
136 static bool init()
|
nuclear@0
|
137 {
|
nuclear@2
|
138 if(fake_devpath) {
|
nuclear@2
|
139 if((fakefd = start_dev(fake_devpath)) == -1) {
|
nuclear@2
|
140 return false;
|
nuclear@2
|
141 }
|
nuclear@2
|
142 }
|
nuclear@1
|
143
|
nuclear@0
|
144 if(!(dpy = XOpenDisplay(0))) {
|
nuclear@0
|
145 fprintf(stderr, "failed to connect to the X server!\n");
|
nuclear@0
|
146 return false;
|
nuclear@0
|
147 }
|
nuclear@0
|
148
|
nuclear@7
|
149 if(!(win = create_window("equeue device emulator", 512, 512))) {
|
nuclear@0
|
150 return false;
|
nuclear@0
|
151 }
|
nuclear@0
|
152
|
nuclear@4
|
153 glewInit();
|
nuclear@4
|
154
|
nuclear@4
|
155 scn = new Scene;
|
nuclear@4
|
156 if(!scn->load("data/device.obj")) {
|
nuclear@4
|
157 fprintf(stderr, "failed to load device 3D model\n");
|
nuclear@4
|
158 return false;
|
nuclear@4
|
159 }
|
nuclear@4
|
160
|
nuclear@4
|
161 for(int i=0; i<NUM_BUTTONS; i++) {
|
nuclear@4
|
162 button_obj[i] = scn->get_object(button_names[i]);
|
nuclear@4
|
163 if(!button_obj[i]) {
|
nuclear@4
|
164 fprintf(stderr, "invalid 3D model\n");
|
nuclear@4
|
165 return false;
|
nuclear@4
|
166 }
|
nuclear@4
|
167 BSphere &bs = button_obj[i]->get_mesh()->get_bounds();
|
nuclear@4
|
168 bs.set_radius(bs.get_radius() * 1.5);
|
nuclear@4
|
169 }
|
nuclear@4
|
170
|
nuclear@6
|
171 disp_obj[0] = scn->get_object("7seg0");
|
nuclear@6
|
172 disp_obj[1] = scn->get_object("7seg1");
|
nuclear@6
|
173 if(!disp_obj[0] || !disp_obj[1]) {
|
nuclear@6
|
174 fprintf(stderr, "invalid 3D model\n");
|
nuclear@6
|
175 return false;
|
nuclear@6
|
176 }
|
nuclear@6
|
177 scn->remove_object(disp_obj[0]);
|
nuclear@6
|
178 scn->remove_object(disp_obj[1]);
|
nuclear@6
|
179
|
nuclear@6
|
180 led_obj[0] = scn->get_object("led1");
|
nuclear@6
|
181 led_obj[1] = scn->get_object("led2");
|
nuclear@6
|
182 if(!led_obj[0] || !led_obj[1]) {
|
nuclear@6
|
183 fprintf(stderr, "invalid 3D model\n");
|
nuclear@6
|
184 return false;
|
nuclear@6
|
185 }
|
nuclear@6
|
186 scn->remove_object(led_obj[0]);
|
nuclear@6
|
187 scn->remove_object(led_obj[1]);
|
nuclear@6
|
188 led_on_emissive = led_obj[0]->mtl.emissive;
|
nuclear@6
|
189
|
nuclear@7
|
190 // create the glow texture
|
nuclear@7
|
191 glGenTextures(1, &glow_tex);
|
nuclear@7
|
192 glBindTexture(GL_TEXTURE_2D, glow_tex);
|
nuclear@7
|
193 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
nuclear@7
|
194 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
nuclear@7
|
195 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
nuclear@7
|
196
|
nuclear@4
|
197 glEnable(GL_DEPTH_TEST);
|
nuclear@4
|
198 glEnable(GL_CULL_FACE);
|
nuclear@4
|
199 glEnable(GL_LIGHTING);
|
nuclear@4
|
200 glEnable(GL_LIGHT0);
|
nuclear@4
|
201
|
nuclear@4
|
202 glClearColor(0.1, 0.1, 0.1, 1);
|
nuclear@4
|
203
|
nuclear@0
|
204 return true;
|
nuclear@0
|
205 }
|
nuclear@0
|
206
|
nuclear@0
|
207 static void cleanup()
|
nuclear@0
|
208 {
|
nuclear@4
|
209 delete scn;
|
nuclear@4
|
210
|
nuclear@1
|
211 stop_dev();
|
nuclear@1
|
212
|
nuclear@0
|
213 if(!dpy) return;
|
nuclear@0
|
214
|
nuclear@0
|
215 if(win) {
|
nuclear@0
|
216 XDestroyWindow(dpy, win);
|
nuclear@0
|
217 }
|
nuclear@0
|
218 XCloseDisplay(dpy);
|
nuclear@0
|
219 }
|
nuclear@0
|
220
|
nuclear@6
|
221 #define DIGIT_USZ (1.0 / 11.0)
|
nuclear@7
|
222 #define MIN_REDRAW_INTERVAL (1000 / 40) /* 40fps */
|
nuclear@6
|
223
|
nuclear@0
|
224 static void display()
|
nuclear@0
|
225 {
|
nuclear@0
|
226 glMatrixMode(GL_MODELVIEW);
|
nuclear@0
|
227 glLoadIdentity();
|
nuclear@4
|
228 glTranslatef(0, 0, -cam_dist);
|
nuclear@4
|
229 glRotatef(cam_phi, 1, 0, 0);
|
nuclear@4
|
230 glRotatef(cam_theta, 0, 1, 0);
|
nuclear@4
|
231
|
nuclear@4
|
232 float lpos[] = {-7, 5, 10, 0};
|
nuclear@4
|
233 glLightfv(GL_LIGHT0, GL_POSITION, lpos);
|
nuclear@4
|
234
|
nuclear@7
|
235 if(opt_use_glow) {
|
nuclear@7
|
236 glViewport(0, 0, glow_xsz, glow_ysz);
|
nuclear@7
|
237
|
nuclear@7
|
238 glClearColor(0, 0, 0, 1);
|
nuclear@7
|
239 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
nuclear@7
|
240
|
nuclear@7
|
241 draw_scene(GLOW_PASS);
|
nuclear@7
|
242
|
nuclear@7
|
243 glReadPixels(0, 0, glow_xsz, glow_ysz, GL_RGBA, GL_UNSIGNED_BYTE, glow_framebuf);
|
nuclear@7
|
244 glViewport(0, 0, win_width, win_height);
|
nuclear@7
|
245 }
|
nuclear@7
|
246
|
nuclear@7
|
247 glClearColor(0.05, 0.05, 0.05, 1);
|
nuclear@7
|
248 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
nuclear@7
|
249
|
nuclear@7
|
250 draw_scene();
|
nuclear@7
|
251
|
nuclear@7
|
252 if(opt_use_glow) {
|
nuclear@7
|
253 for(int i=0; i<glow_iter; i++) {
|
nuclear@7
|
254 fast_blur(BLUR_BOTH, blur_size, (uint32_t*)glow_framebuf, glow_xsz, glow_ysz);
|
nuclear@7
|
255 glBindTexture(GL_TEXTURE_2D, glow_tex);
|
nuclear@7
|
256 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, glow_xsz, glow_ysz, GL_RGBA, GL_UNSIGNED_BYTE, glow_framebuf);
|
nuclear@7
|
257
|
nuclear@7
|
258 post_glow();
|
nuclear@7
|
259 }
|
nuclear@7
|
260 }
|
nuclear@7
|
261
|
nuclear@9
|
262 if(get_led_state(0)) {
|
nuclear@7
|
263 // continuously redraw until the left LED times out
|
nuclear@7
|
264 draw_pending = true;
|
nuclear@9
|
265 }
|
nuclear@7
|
266
|
nuclear@7
|
267 glXSwapBuffers(dpy, win);
|
nuclear@7
|
268 assert(glGetError() == GL_NO_ERROR);
|
nuclear@7
|
269
|
nuclear@9
|
270 static long prev_msec;
|
nuclear@7
|
271 long msec = get_msec();
|
nuclear@7
|
272 long dt = msec - prev_msec;
|
nuclear@7
|
273
|
nuclear@7
|
274 if(dt < MIN_REDRAW_INTERVAL) {
|
nuclear@7
|
275 wait_for(MIN_REDRAW_INTERVAL - dt);
|
nuclear@7
|
276 }
|
nuclear@9
|
277 prev_msec = get_msec();
|
nuclear@7
|
278 }
|
nuclear@7
|
279
|
nuclear@7
|
280 static void draw_scene(int pass)
|
nuclear@7
|
281 {
|
nuclear@7
|
282 if(pass != GLOW_PASS) {
|
nuclear@7
|
283 scn->render();
|
nuclear@7
|
284 }
|
nuclear@0
|
285
|
nuclear@6
|
286 // shift the textures and modify the materials to make the display match our state
|
nuclear@6
|
287 for(int i=0; i<2; i++) {
|
nuclear@7
|
288 // 7seg
|
nuclear@6
|
289 int digit = get_display_number();
|
nuclear@6
|
290 for(int j=0; j<i; j++) {
|
nuclear@6
|
291 digit /= 10;
|
nuclear@6
|
292 }
|
nuclear@6
|
293 digit %= 10;
|
nuclear@6
|
294
|
nuclear@6
|
295 float uoffs = DIGIT_USZ + DIGIT_USZ * digit;
|
nuclear@6
|
296
|
nuclear@6
|
297 disp_obj[i]->mtl.tex_offset[TEX_DIFFUSE] = Vector2(uoffs, 0);
|
nuclear@6
|
298 disp_obj[i]->render();
|
nuclear@6
|
299
|
nuclear@6
|
300 // LEDs
|
nuclear@6
|
301 if(get_led_state(i)) {
|
nuclear@6
|
302 led_obj[i]->mtl.emissive = led_on_emissive;
|
nuclear@6
|
303 } else {
|
nuclear@6
|
304 led_obj[i]->mtl.emissive = Vector3(0, 0, 0);
|
nuclear@6
|
305 }
|
nuclear@6
|
306 led_obj[i]->render();
|
nuclear@6
|
307 }
|
nuclear@7
|
308 }
|
nuclear@6
|
309
|
nuclear@7
|
310 static void post_glow(void)
|
nuclear@7
|
311 {
|
nuclear@7
|
312 float max_s = (float)glow_xsz / (float)glow_tex_xsz;
|
nuclear@7
|
313 float max_t = (float)glow_ysz / (float)glow_tex_ysz;
|
nuclear@7
|
314
|
nuclear@7
|
315 glPushAttrib(GL_ENABLE_BIT);
|
nuclear@7
|
316
|
nuclear@7
|
317 glBlendFunc(GL_ONE, GL_ONE);
|
nuclear@7
|
318 glEnable(GL_BLEND);
|
nuclear@7
|
319 glDisable(GL_CULL_FACE);
|
nuclear@7
|
320 glDisable(GL_LIGHTING);
|
nuclear@7
|
321 glDisable(GL_DEPTH_TEST);
|
nuclear@7
|
322
|
nuclear@7
|
323 glMatrixMode(GL_MODELVIEW);
|
nuclear@7
|
324 glPushMatrix();
|
nuclear@7
|
325 glLoadIdentity();
|
nuclear@7
|
326 glMatrixMode(GL_PROJECTION);
|
nuclear@7
|
327 glPushMatrix();
|
nuclear@7
|
328 glLoadIdentity();
|
nuclear@7
|
329
|
nuclear@7
|
330 glEnable(GL_TEXTURE_2D);
|
nuclear@7
|
331 glBindTexture(GL_TEXTURE_2D, glow_tex);
|
nuclear@7
|
332
|
nuclear@7
|
333 glBegin(GL_QUADS);
|
nuclear@7
|
334 glColor4f(1, 1, 1, 1);
|
nuclear@7
|
335 glTexCoord2f(0, 0);
|
nuclear@7
|
336 glVertex2f(-1, -1);
|
nuclear@7
|
337 glTexCoord2f(max_s, 0);
|
nuclear@7
|
338 glVertex2f(1, -1);
|
nuclear@7
|
339 glTexCoord2f(max_s, max_t);
|
nuclear@7
|
340 glVertex2f(1, 1);
|
nuclear@7
|
341 glTexCoord2f(0, max_t);
|
nuclear@7
|
342 glVertex2f(-1, 1);
|
nuclear@7
|
343 glEnd();
|
nuclear@7
|
344
|
nuclear@7
|
345 glPopMatrix();
|
nuclear@7
|
346 glMatrixMode(GL_MODELVIEW);
|
nuclear@7
|
347 glPopMatrix();
|
nuclear@7
|
348
|
nuclear@7
|
349 glPopAttrib();
|
nuclear@0
|
350 }
|
nuclear@0
|
351
|
nuclear@7
|
352
|
nuclear@0
|
353 static void reshape(int x, int y)
|
nuclear@0
|
354 {
|
nuclear@0
|
355 glViewport(0, 0, x, y);
|
nuclear@0
|
356
|
nuclear@0
|
357 glMatrixMode(GL_PROJECTION);
|
nuclear@0
|
358 glLoadIdentity();
|
nuclear@4
|
359 gluPerspective(50.0, (float)x / (float)y, 1.0, 1000.0);
|
nuclear@4
|
360
|
nuclear@4
|
361 win_width = x;
|
nuclear@4
|
362 win_height = y;
|
nuclear@7
|
363
|
nuclear@7
|
364 if(opt_use_glow) {
|
nuclear@7
|
365 glow_xsz = x / GLOW_SZ_DIV;
|
nuclear@7
|
366 glow_ysz = y / GLOW_SZ_DIV;
|
nuclear@7
|
367 printf("glow image size: %dx%d\n", glow_xsz, glow_ysz);
|
nuclear@7
|
368
|
nuclear@7
|
369 delete [] glow_framebuf;
|
nuclear@7
|
370 glow_framebuf = new unsigned char[glow_xsz * glow_ysz * 4];
|
nuclear@7
|
371
|
nuclear@7
|
372 glow_tex_xsz = next_pow2(glow_xsz);
|
nuclear@7
|
373 glow_tex_ysz = next_pow2(glow_ysz);
|
nuclear@7
|
374 glBindTexture(GL_TEXTURE_2D, glow_tex);
|
nuclear@7
|
375 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glow_tex_xsz, glow_tex_ysz, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
nuclear@7
|
376 }
|
nuclear@0
|
377 }
|
nuclear@0
|
378
|
nuclear@0
|
379 static void keyb(int key, bool pressed)
|
nuclear@0
|
380 {
|
nuclear@0
|
381 if(pressed) {
|
nuclear@0
|
382 switch(key) {
|
nuclear@0
|
383 case 27:
|
nuclear@0
|
384 exit(0);
|
nuclear@0
|
385 }
|
nuclear@0
|
386 }
|
nuclear@0
|
387 }
|
nuclear@0
|
388
|
nuclear@4
|
389 static bool bnstate[32];
|
nuclear@4
|
390 static int prev_x, prev_y;
|
nuclear@4
|
391
|
nuclear@0
|
392 static void mouse(int bn, bool pressed, int x, int y)
|
nuclear@0
|
393 {
|
nuclear@4
|
394 bnstate[bn] = pressed;
|
nuclear@4
|
395 prev_x = x;
|
nuclear@4
|
396 prev_y = y;
|
nuclear@4
|
397
|
nuclear@4
|
398 if(bn == 0 && pressed) {
|
nuclear@4
|
399 // do picking
|
nuclear@4
|
400 Ray ray = calc_pick_ray(x, win_height - y);
|
nuclear@4
|
401
|
nuclear@4
|
402 HitPoint minhit;
|
nuclear@4
|
403 minhit.t = FLT_MAX;
|
nuclear@4
|
404 int hit_found = -1;
|
nuclear@4
|
405
|
nuclear@4
|
406 for(int i=0; i<NUM_BUTTONS; i++) {
|
nuclear@4
|
407 HitPoint hit;
|
nuclear@4
|
408 if(button_obj[i]->get_mesh()->get_bounds().intersect(ray, &hit) && hit.t < minhit.t) {
|
nuclear@4
|
409 minhit = hit;
|
nuclear@4
|
410 hit_found = i;
|
nuclear@4
|
411 }
|
nuclear@4
|
412 }
|
nuclear@4
|
413
|
nuclear@4
|
414 if(hit_found != -1) {
|
nuclear@4
|
415 switch(hit_found) {
|
nuclear@4
|
416 case BN_TICKET:
|
nuclear@4
|
417 issue_ticket();
|
nuclear@4
|
418 break;
|
nuclear@4
|
419
|
nuclear@4
|
420 case BN_NEXT:
|
nuclear@4
|
421 next_customer();
|
nuclear@4
|
422 break;
|
nuclear@4
|
423 }
|
nuclear@4
|
424 draw_pending = true;
|
nuclear@4
|
425 }
|
nuclear@4
|
426 }
|
nuclear@0
|
427 }
|
nuclear@0
|
428
|
nuclear@0
|
429 static void motion(int x, int y)
|
nuclear@0
|
430 {
|
nuclear@4
|
431 int dx = x - prev_x;
|
nuclear@4
|
432 int dy = y - prev_y;
|
nuclear@4
|
433 prev_x = x;
|
nuclear@4
|
434 prev_y = y;
|
nuclear@4
|
435
|
nuclear@4
|
436 if(bnstate[0]) {
|
nuclear@4
|
437 cam_theta += dx * 0.5;
|
nuclear@4
|
438 cam_phi += dy * 0.5;
|
nuclear@4
|
439 if(cam_phi < -90) cam_phi = -90;
|
nuclear@4
|
440 if(cam_phi > 90) cam_phi = 90;
|
nuclear@6
|
441
|
nuclear@6
|
442 } else if(bnstate[2]) {
|
nuclear@4
|
443 cam_dist += dy * 0.5;
|
nuclear@4
|
444 if(cam_dist < 0.0) cam_dist = 0.0;
|
nuclear@6
|
445
|
nuclear@6
|
446 } else {
|
nuclear@6
|
447 float xoffs = 2.0 * x / win_width - 1.0;
|
nuclear@6
|
448 float yoffs = 2.0 * y / win_height - 1.0;
|
nuclear@6
|
449 cam_theta = -xoffs * 15.0 * (win_width / win_height);
|
nuclear@6
|
450 cam_phi = -yoffs * 15.0;
|
nuclear@4
|
451 }
|
nuclear@6
|
452 draw_pending = true;
|
nuclear@4
|
453 }
|
nuclear@4
|
454
|
nuclear@4
|
455 static Ray calc_pick_ray(int x, int y)
|
nuclear@4
|
456 {
|
nuclear@4
|
457 double mv[16], proj[16];
|
nuclear@4
|
458 int vp[4];
|
nuclear@4
|
459 double resx, resy, resz;
|
nuclear@4
|
460 Ray ray;
|
nuclear@4
|
461
|
nuclear@4
|
462 glGetDoublev(GL_MODELVIEW_MATRIX, mv);
|
nuclear@4
|
463 glGetDoublev(GL_PROJECTION_MATRIX, proj);
|
nuclear@4
|
464 glGetIntegerv(GL_VIEWPORT, vp);
|
nuclear@4
|
465
|
nuclear@4
|
466 gluUnProject(x, y, 0, mv, proj, vp, &resx, &resy, &resz);
|
nuclear@4
|
467 ray.origin = Vector3(resx, resy, resz);
|
nuclear@4
|
468
|
nuclear@4
|
469 gluUnProject(x, y, 1, mv, proj, vp, &resx, &resy, &resz);
|
nuclear@4
|
470 ray.dir = normalize(Vector3(resx, resy, resz) - ray.origin);
|
nuclear@4
|
471
|
nuclear@4
|
472 return ray;
|
nuclear@0
|
473 }
|
nuclear@0
|
474
|
nuclear@7
|
475 static int next_pow2(int x)
|
nuclear@7
|
476 {
|
nuclear@7
|
477 x--;
|
nuclear@7
|
478 x = (x >> 1) | x;
|
nuclear@7
|
479 x = (x >> 2) | x;
|
nuclear@7
|
480 x = (x >> 4) | x;
|
nuclear@7
|
481 x = (x >> 8) | x;
|
nuclear@7
|
482 x = (x >> 16) | x;
|
nuclear@7
|
483 return x + 1;
|
nuclear@7
|
484 }
|
nuclear@7
|
485
|
nuclear@0
|
486 static Window create_window(const char *title, int xsz, int ysz)
|
nuclear@0
|
487 {
|
nuclear@0
|
488 int scr = DefaultScreen(dpy);
|
nuclear@0
|
489 Window root = RootWindow(dpy, scr);
|
nuclear@0
|
490
|
nuclear@0
|
491 int glxattr[] = {
|
nuclear@0
|
492 GLX_RGBA,
|
nuclear@0
|
493 GLX_RED_SIZE, 8,
|
nuclear@0
|
494 GLX_GREEN_SIZE, 8,
|
nuclear@0
|
495 GLX_BLUE_SIZE, 8,
|
nuclear@0
|
496 GLX_DEPTH_SIZE, 24,
|
nuclear@0
|
497 GLX_DOUBLEBUFFER,
|
nuclear@0
|
498 #if defined(GLX_VERSION_1_4) || defined(GLX_ARB_multisample)
|
nuclear@0
|
499 GLX_SAMPLE_BUFFERS_ARB, 1,
|
nuclear@0
|
500 GLX_SAMPLES_ARB, 1,
|
nuclear@0
|
501 #endif
|
nuclear@0
|
502 None
|
nuclear@0
|
503 };
|
nuclear@0
|
504
|
nuclear@0
|
505 XVisualInfo *vis = glXChooseVisual(dpy, scr, glxattr);
|
nuclear@0
|
506 if(!vis) {
|
nuclear@0
|
507 fprintf(stderr, "failed to find a suitable visual\n");
|
nuclear@0
|
508 return 0;
|
nuclear@0
|
509 }
|
nuclear@0
|
510
|
nuclear@0
|
511 if(!(ctx = glXCreateContext(dpy, vis, 0, True))) {
|
nuclear@0
|
512 fprintf(stderr, "failed to create OpenGL context\n");
|
nuclear@0
|
513 XFree(vis);
|
nuclear@0
|
514 return -1;
|
nuclear@0
|
515 }
|
nuclear@0
|
516
|
nuclear@0
|
517 XSetWindowAttributes xattr;
|
nuclear@0
|
518 xattr.background_pixel = xattr.border_pixel = BlackPixel(dpy, scr);
|
nuclear@0
|
519 xattr.colormap = XCreateColormap(dpy, root, vis->visual, AllocNone);
|
nuclear@0
|
520 unsigned int xattr_mask = CWColormap | CWBackPixel | CWBorderPixel;
|
nuclear@0
|
521
|
nuclear@0
|
522 Window win = XCreateWindow(dpy, root, 0, 0, xsz, ysz, 0, vis->depth, InputOutput,
|
nuclear@0
|
523 vis->visual, xattr_mask, &xattr);
|
nuclear@0
|
524 if(!win) {
|
nuclear@0
|
525 fprintf(stderr, "failed to create window\n");
|
nuclear@0
|
526 glXDestroyContext(dpy, ctx);
|
nuclear@0
|
527 XFree(vis);
|
nuclear@0
|
528 return -1;
|
nuclear@0
|
529 }
|
nuclear@0
|
530 XFree(vis);
|
nuclear@0
|
531
|
nuclear@0
|
532 unsigned int evmask = StructureNotifyMask | VisibilityChangeMask | ExposureMask |
|
nuclear@0
|
533 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask |
|
nuclear@11
|
534 PointerMotionMask | LeaveWindowMask;
|
nuclear@0
|
535 XSelectInput(dpy, win, evmask);
|
nuclear@0
|
536
|
nuclear@0
|
537 xa_wm_prot = XInternAtom(dpy, "WM_PROTOCOLS", False);
|
nuclear@0
|
538 xa_wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
|
nuclear@0
|
539 XSetWMProtocols(dpy, win, &xa_wm_del_win, 1);
|
nuclear@0
|
540
|
nuclear@0
|
541 XClassHint hint;
|
nuclear@0
|
542 hint.res_name = hint.res_class = (char*)"equeue_win";
|
nuclear@0
|
543 XSetClassHint(dpy, win, &hint);
|
nuclear@0
|
544
|
nuclear@0
|
545 XTextProperty wm_name;
|
nuclear@0
|
546 XStringListToTextProperty((char**)&title, 1, &wm_name);
|
nuclear@0
|
547 XSetWMName(dpy, win, &wm_name);
|
nuclear@0
|
548 XSetWMIconName(dpy, win, &wm_name);
|
nuclear@0
|
549 XFree(wm_name.value);
|
nuclear@0
|
550
|
nuclear@0
|
551 XMapWindow(dpy, win);
|
nuclear@0
|
552 glXMakeCurrent(dpy, win, ctx);
|
nuclear@0
|
553
|
nuclear@0
|
554 return win;
|
nuclear@0
|
555 }
|
nuclear@0
|
556
|
nuclear@0
|
557 static void process_events()
|
nuclear@0
|
558 {
|
nuclear@0
|
559 XEvent ev;
|
nuclear@0
|
560
|
nuclear@0
|
561 while(XPending(dpy)) {
|
nuclear@0
|
562 XNextEvent(dpy, &ev);
|
nuclear@0
|
563 switch(ev.type) {
|
nuclear@0
|
564 case MapNotify:
|
nuclear@0
|
565 win_mapped = true;
|
nuclear@0
|
566 break;
|
nuclear@0
|
567
|
nuclear@0
|
568 case UnmapNotify:
|
nuclear@0
|
569 win_mapped = false;
|
nuclear@0
|
570 break;
|
nuclear@0
|
571
|
nuclear@0
|
572 case Expose:
|
nuclear@0
|
573 if(win_mapped && ev.xexpose.count == 0) {
|
nuclear@6
|
574 draw_pending = true;
|
nuclear@0
|
575 }
|
nuclear@0
|
576 break;
|
nuclear@0
|
577
|
nuclear@0
|
578 case MotionNotify:
|
nuclear@0
|
579 motion(ev.xmotion.x, ev.xmotion.y);
|
nuclear@0
|
580 break;
|
nuclear@0
|
581
|
nuclear@0
|
582 case ButtonPress:
|
nuclear@0
|
583 mouse(ev.xbutton.button - 1, true, ev.xbutton.x, ev.xbutton.y);
|
nuclear@0
|
584 break;
|
nuclear@0
|
585
|
nuclear@0
|
586 case ButtonRelease:
|
nuclear@0
|
587 mouse(ev.xbutton.button - 1, false, ev.xbutton.x, ev.xbutton.y);
|
nuclear@0
|
588 break;
|
nuclear@0
|
589
|
nuclear@0
|
590 case KeyPress:
|
nuclear@0
|
591 {
|
nuclear@0
|
592 KeySym sym = XLookupKeysym(&ev.xkey, 0);
|
nuclear@0
|
593 keyb(translate_keysym(sym), true);
|
nuclear@0
|
594 }
|
nuclear@0
|
595 break;
|
nuclear@0
|
596
|
nuclear@0
|
597 case KeyRelease:
|
nuclear@0
|
598 {
|
nuclear@0
|
599 KeySym sym = XLookupKeysym(&ev.xkey, 0);
|
nuclear@0
|
600 keyb(translate_keysym(sym), false);
|
nuclear@0
|
601 }
|
nuclear@0
|
602 break;
|
nuclear@0
|
603
|
nuclear@0
|
604 case ConfigureNotify:
|
nuclear@0
|
605 {
|
nuclear@0
|
606 int xsz = ev.xconfigure.width;
|
nuclear@0
|
607 int ysz = ev.xconfigure.height;
|
nuclear@0
|
608
|
nuclear@0
|
609 if(xsz != win_width || ysz != win_height) {
|
nuclear@0
|
610 win_width = xsz;
|
nuclear@0
|
611 win_height = ysz;
|
nuclear@0
|
612 reshape(xsz, ysz);
|
nuclear@0
|
613 }
|
nuclear@0
|
614 }
|
nuclear@0
|
615 break;
|
nuclear@0
|
616
|
nuclear@0
|
617 case ClientMessage:
|
nuclear@0
|
618 if(ev.xclient.message_type == xa_wm_prot) {
|
nuclear@0
|
619 if((Atom)ev.xclient.data.l[0] == xa_wm_del_win) {
|
nuclear@0
|
620 exit(0);
|
nuclear@0
|
621 }
|
nuclear@0
|
622 }
|
nuclear@0
|
623 break;
|
nuclear@0
|
624
|
nuclear@11
|
625 case LeaveNotify:
|
nuclear@11
|
626 if(ev.xcrossing.mode == NotifyNormal) {
|
nuclear@11
|
627 cam_theta = cam_phi = 0;
|
nuclear@11
|
628 draw_pending = true;
|
nuclear@11
|
629 }
|
nuclear@11
|
630 break;
|
nuclear@11
|
631
|
nuclear@0
|
632 default:
|
nuclear@0
|
633 break;
|
nuclear@0
|
634 }
|
nuclear@0
|
635
|
nuclear@0
|
636 }
|
nuclear@0
|
637 }
|
nuclear@0
|
638
|
nuclear@0
|
639 static int translate_keysym(KeySym sym)
|
nuclear@0
|
640 {
|
nuclear@0
|
641 switch(sym) {
|
nuclear@0
|
642 case XK_BackSpace:
|
nuclear@0
|
643 return '\b';
|
nuclear@0
|
644 case XK_Tab:
|
nuclear@0
|
645 return '\t';
|
nuclear@0
|
646 case XK_Linefeed:
|
nuclear@0
|
647 return '\r';
|
nuclear@0
|
648 case XK_Return:
|
nuclear@0
|
649 return '\n';
|
nuclear@0
|
650 case XK_Escape:
|
nuclear@0
|
651 return 27;
|
nuclear@0
|
652 default:
|
nuclear@0
|
653 break;
|
nuclear@0
|
654 }
|
nuclear@0
|
655 return (int)sym;
|
nuclear@0
|
656 }
|
nuclear@2
|
657
|
nuclear@2
|
658 static int proc_args(int argc, char **argv)
|
nuclear@2
|
659 {
|
nuclear@2
|
660 for(int i=1; i<argc; i++) {
|
nuclear@2
|
661 if(argv[i][0] == '-') {
|
nuclear@2
|
662 fprintf(stderr, "unexpected option: %s\n", argv[i]);
|
nuclear@2
|
663 return -1;
|
nuclear@2
|
664
|
nuclear@2
|
665 } else {
|
nuclear@2
|
666 if(fake_devpath) {
|
nuclear@2
|
667 fprintf(stderr, "unexpected argument: %s\n", argv[i]);
|
nuclear@2
|
668 return -1;
|
nuclear@2
|
669 }
|
nuclear@2
|
670 fake_devpath = argv[i];
|
nuclear@2
|
671 }
|
nuclear@2
|
672 }
|
nuclear@2
|
673 if(!fake_devpath) {
|
nuclear@2
|
674 fprintf(stderr, "no device path specified, running standalone\n");
|
nuclear@2
|
675 }
|
nuclear@2
|
676 return 0;
|
nuclear@2
|
677 }
|