eqemu

annotate src/main.cc @ 6:977bc1cb055b

almost done
author John Tsiombikas <nuclear@member.fsf.org>
date Fri, 18 Jul 2014 02:35:06 +0300
parents 3d3656360a82
children e9ab4861536d
rev   line source
nuclear@0 1 #include <stdio.h>
nuclear@0 2 #include <stdlib.h>
nuclear@4 3 #include <float.h>
nuclear@0 4 #include <assert.h>
nuclear@0 5 #include <errno.h>
nuclear@0 6 #include <unistd.h>
nuclear@0 7 #include <sys/select.h>
nuclear@0 8 #include <GL/glew.h>
nuclear@0 9 #include <X11/Xlib.h>
nuclear@0 10 #include <GL/glx.h>
nuclear@1 11 #include "dev.h"
nuclear@4 12 #include "scene.h"
nuclear@0 13
nuclear@0 14 static bool init();
nuclear@0 15 static void cleanup();
nuclear@0 16 static void display();
nuclear@0 17 static void keyb(int key, bool pressed);
nuclear@0 18 static void mouse(int bn, bool pressed, int x, int y);
nuclear@0 19 static void motion(int x, int y);
nuclear@4 20 static Ray calc_pick_ray(int x, int y);
nuclear@0 21
nuclear@0 22 static Window create_window(const char *title, int xsz, int ysz);
nuclear@0 23 static void process_events();
nuclear@0 24 static int translate_keysym(KeySym sym);
nuclear@0 25
nuclear@2 26 static int proc_args(int argc, char **argv);
nuclear@2 27
nuclear@0 28 static Display *dpy;
nuclear@0 29 static Window win;
nuclear@0 30 static GLXContext ctx;
nuclear@0 31 static Atom xa_wm_prot, xa_wm_del_win;
nuclear@0 32
nuclear@0 33 static int win_width, win_height;
nuclear@0 34
nuclear@4 35 static bool draw_pending;
nuclear@0 36 static bool win_mapped;
nuclear@0 37
nuclear@2 38 static int fakefd = -1;
nuclear@2 39 static char *fake_devpath;
nuclear@2 40
nuclear@4 41 static float cam_theta, cam_phi, cam_dist = 140;
nuclear@4 42 static Scene *scn;
nuclear@4 43
nuclear@4 44 enum { BN_TICKET, BN_NEXT, NUM_BUTTONS };
nuclear@6 45 static const char *button_names[] = { "button1", "button2" };
nuclear@4 46 static Object *button_obj[NUM_BUTTONS];
nuclear@6 47 static Object *disp_obj[2];
nuclear@6 48 static Object *led_obj[2];
nuclear@6 49 static Vector3 led_on_emissive;
nuclear@4 50
nuclear@2 51 int main(int argc, char **argv)
nuclear@0 52 {
nuclear@2 53 if(proc_args(argc, argv) == -1) {
nuclear@2 54 return 1;
nuclear@2 55 }
nuclear@0 56 if(!init()) {
nuclear@0 57 return 1;
nuclear@0 58 }
nuclear@0 59 atexit(cleanup);
nuclear@0 60
nuclear@0 61 int xfd = ConnectionNumber(dpy);
nuclear@0 62
nuclear@4 63 // run once through pending events before going into the select loop
nuclear@4 64 process_events();
nuclear@4 65
nuclear@0 66 for(;;) {
nuclear@0 67 fd_set rd;
nuclear@0 68 FD_ZERO(&rd);
nuclear@0 69
nuclear@0 70 FD_SET(xfd, &rd);
nuclear@2 71 FD_SET(fakefd, &rd);
nuclear@0 72
nuclear@4 73 struct timeval noblock = {0, 0};
nuclear@2 74 int maxfd = xfd > fakefd ? xfd : fakefd;
nuclear@4 75 while(select(maxfd + 1, &rd, 0, 0, draw_pending ? &noblock : 0) == -1 && errno == EINTR);
nuclear@0 76
nuclear@0 77 if(FD_ISSET(xfd, &rd)) {
nuclear@0 78 process_events();
nuclear@0 79 }
nuclear@2 80 if(FD_ISSET(fakefd, &rd)) {
nuclear@2 81 proc_dev_input();
nuclear@2 82 }
nuclear@0 83
nuclear@4 84 if(draw_pending) {
nuclear@0 85 display();
nuclear@4 86 draw_pending = false;
nuclear@0 87 }
nuclear@0 88 }
nuclear@0 89 return 0;
nuclear@0 90 }
nuclear@0 91
nuclear@0 92 static bool init()
nuclear@0 93 {
nuclear@2 94 if(fake_devpath) {
nuclear@2 95 if((fakefd = start_dev(fake_devpath)) == -1) {
nuclear@2 96 return false;
nuclear@2 97 }
nuclear@2 98 }
nuclear@1 99
nuclear@0 100 if(!(dpy = XOpenDisplay(0))) {
nuclear@0 101 fprintf(stderr, "failed to connect to the X server!\n");
nuclear@0 102 return false;
nuclear@0 103 }
nuclear@0 104
nuclear@4 105 if(!(win = create_window("equeue device emulator", 800, 600))) {
nuclear@0 106 return false;
nuclear@0 107 }
nuclear@0 108
nuclear@4 109 glewInit();
nuclear@4 110
nuclear@4 111 scn = new Scene;
nuclear@4 112 if(!scn->load("data/device.obj")) {
nuclear@4 113 fprintf(stderr, "failed to load device 3D model\n");
nuclear@4 114 return false;
nuclear@4 115 }
nuclear@4 116
nuclear@4 117 for(int i=0; i<NUM_BUTTONS; i++) {
nuclear@4 118 button_obj[i] = scn->get_object(button_names[i]);
nuclear@4 119 if(!button_obj[i]) {
nuclear@4 120 fprintf(stderr, "invalid 3D model\n");
nuclear@4 121 return false;
nuclear@4 122 }
nuclear@4 123 BSphere &bs = button_obj[i]->get_mesh()->get_bounds();
nuclear@4 124 bs.set_radius(bs.get_radius() * 1.5);
nuclear@4 125 }
nuclear@4 126
nuclear@6 127 disp_obj[0] = scn->get_object("7seg0");
nuclear@6 128 disp_obj[1] = scn->get_object("7seg1");
nuclear@6 129 if(!disp_obj[0] || !disp_obj[1]) {
nuclear@6 130 fprintf(stderr, "invalid 3D model\n");
nuclear@6 131 return false;
nuclear@6 132 }
nuclear@6 133 scn->remove_object(disp_obj[0]);
nuclear@6 134 scn->remove_object(disp_obj[1]);
nuclear@6 135
nuclear@6 136 led_obj[0] = scn->get_object("led1");
nuclear@6 137 led_obj[1] = scn->get_object("led2");
nuclear@6 138 if(!led_obj[0] || !led_obj[1]) {
nuclear@6 139 fprintf(stderr, "invalid 3D model\n");
nuclear@6 140 return false;
nuclear@6 141 }
nuclear@6 142 scn->remove_object(led_obj[0]);
nuclear@6 143 scn->remove_object(led_obj[1]);
nuclear@6 144 led_on_emissive = led_obj[0]->mtl.emissive;
nuclear@6 145
nuclear@4 146 glEnable(GL_DEPTH_TEST);
nuclear@4 147 glEnable(GL_CULL_FACE);
nuclear@4 148 glEnable(GL_LIGHTING);
nuclear@4 149 glEnable(GL_LIGHT0);
nuclear@4 150
nuclear@4 151 glClearColor(0.1, 0.1, 0.1, 1);
nuclear@4 152
nuclear@0 153 return true;
nuclear@0 154 }
nuclear@0 155
nuclear@0 156 static void cleanup()
nuclear@0 157 {
nuclear@4 158 delete scn;
nuclear@4 159
nuclear@1 160 stop_dev();
nuclear@1 161
nuclear@0 162 if(!dpy) return;
nuclear@0 163
nuclear@0 164 if(win) {
nuclear@0 165 XDestroyWindow(dpy, win);
nuclear@0 166 }
nuclear@0 167 XCloseDisplay(dpy);
nuclear@0 168 }
nuclear@0 169
nuclear@6 170 #define DIGIT_USZ (1.0 / 11.0)
nuclear@6 171
nuclear@0 172 static void display()
nuclear@0 173 {
nuclear@0 174 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
nuclear@0 175
nuclear@0 176 glMatrixMode(GL_MODELVIEW);
nuclear@0 177 glLoadIdentity();
nuclear@4 178 glTranslatef(0, 0, -cam_dist);
nuclear@4 179 glRotatef(cam_phi, 1, 0, 0);
nuclear@4 180 glRotatef(cam_theta, 0, 1, 0);
nuclear@4 181
nuclear@4 182 float lpos[] = {-7, 5, 10, 0};
nuclear@4 183 glLightfv(GL_LIGHT0, GL_POSITION, lpos);
nuclear@4 184
nuclear@4 185 scn->render();
nuclear@0 186
nuclear@6 187 // shift the textures and modify the materials to make the display match our state
nuclear@6 188 for(int i=0; i<2; i++) {
nuclear@6 189 int digit = get_display_number();
nuclear@6 190 for(int j=0; j<i; j++) {
nuclear@6 191 digit /= 10;
nuclear@6 192 }
nuclear@6 193 digit %= 10;
nuclear@6 194
nuclear@6 195 float uoffs = DIGIT_USZ + DIGIT_USZ * digit;
nuclear@6 196
nuclear@6 197 disp_obj[i]->mtl.tex_offset[TEX_DIFFUSE] = Vector2(uoffs, 0);
nuclear@6 198 disp_obj[i]->render();
nuclear@6 199
nuclear@6 200 // LEDs
nuclear@6 201 if(get_led_state(i)) {
nuclear@6 202 led_obj[i]->mtl.emissive = led_on_emissive;
nuclear@6 203 } else {
nuclear@6 204 led_obj[i]->mtl.emissive = Vector3(0, 0, 0);
nuclear@6 205 }
nuclear@6 206 led_obj[i]->render();
nuclear@6 207 }
nuclear@6 208
nuclear@0 209 glXSwapBuffers(dpy, win);
nuclear@0 210 assert(glGetError() == GL_NO_ERROR);
nuclear@0 211 }
nuclear@0 212
nuclear@0 213 static void reshape(int x, int y)
nuclear@0 214 {
nuclear@0 215 glViewport(0, 0, x, y);
nuclear@0 216
nuclear@0 217 glMatrixMode(GL_PROJECTION);
nuclear@0 218 glLoadIdentity();
nuclear@4 219 gluPerspective(50.0, (float)x / (float)y, 1.0, 1000.0);
nuclear@4 220
nuclear@4 221 win_width = x;
nuclear@4 222 win_height = y;
nuclear@0 223 }
nuclear@0 224
nuclear@0 225 static void keyb(int key, bool pressed)
nuclear@0 226 {
nuclear@0 227 if(pressed) {
nuclear@0 228 switch(key) {
nuclear@0 229 case 27:
nuclear@0 230 exit(0);
nuclear@0 231 }
nuclear@0 232 }
nuclear@0 233 }
nuclear@0 234
nuclear@4 235 static bool bnstate[32];
nuclear@4 236 static int prev_x, prev_y;
nuclear@4 237
nuclear@0 238 static void mouse(int bn, bool pressed, int x, int y)
nuclear@0 239 {
nuclear@4 240 bnstate[bn] = pressed;
nuclear@4 241 prev_x = x;
nuclear@4 242 prev_y = y;
nuclear@4 243
nuclear@4 244 if(bn == 0 && pressed) {
nuclear@4 245 // do picking
nuclear@4 246 Ray ray = calc_pick_ray(x, win_height - y);
nuclear@4 247
nuclear@4 248 HitPoint minhit;
nuclear@4 249 minhit.t = FLT_MAX;
nuclear@4 250 int hit_found = -1;
nuclear@4 251
nuclear@4 252 for(int i=0; i<NUM_BUTTONS; i++) {
nuclear@4 253 HitPoint hit;
nuclear@4 254 if(button_obj[i]->get_mesh()->get_bounds().intersect(ray, &hit) && hit.t < minhit.t) {
nuclear@4 255 minhit = hit;
nuclear@4 256 hit_found = i;
nuclear@4 257 }
nuclear@4 258 }
nuclear@4 259
nuclear@4 260 if(hit_found != -1) {
nuclear@4 261 switch(hit_found) {
nuclear@4 262 case BN_TICKET:
nuclear@4 263 issue_ticket();
nuclear@4 264 printf("issue ticket\n");
nuclear@4 265 break;
nuclear@4 266
nuclear@4 267 case BN_NEXT:
nuclear@4 268 next_customer();
nuclear@4 269 printf("next customer\n");
nuclear@4 270 break;
nuclear@4 271 }
nuclear@4 272 draw_pending = true;
nuclear@4 273 }
nuclear@4 274 }
nuclear@0 275 }
nuclear@0 276
nuclear@0 277 static void motion(int x, int y)
nuclear@0 278 {
nuclear@4 279 int dx = x - prev_x;
nuclear@4 280 int dy = y - prev_y;
nuclear@4 281 prev_x = x;
nuclear@4 282 prev_y = y;
nuclear@4 283
nuclear@4 284 if(bnstate[0]) {
nuclear@4 285 cam_theta += dx * 0.5;
nuclear@4 286 cam_phi += dy * 0.5;
nuclear@4 287 if(cam_phi < -90) cam_phi = -90;
nuclear@4 288 if(cam_phi > 90) cam_phi = 90;
nuclear@6 289
nuclear@6 290 } else if(bnstate[2]) {
nuclear@4 291 cam_dist += dy * 0.5;
nuclear@4 292 if(cam_dist < 0.0) cam_dist = 0.0;
nuclear@6 293
nuclear@6 294 } else {
nuclear@6 295 float xoffs = 2.0 * x / win_width - 1.0;
nuclear@6 296 float yoffs = 2.0 * y / win_height - 1.0;
nuclear@6 297 cam_theta = -xoffs * 15.0 * (win_width / win_height);
nuclear@6 298 cam_phi = -yoffs * 15.0;
nuclear@4 299 }
nuclear@6 300 draw_pending = true;
nuclear@4 301 }
nuclear@4 302
nuclear@4 303 static Ray calc_pick_ray(int x, int y)
nuclear@4 304 {
nuclear@4 305 double mv[16], proj[16];
nuclear@4 306 int vp[4];
nuclear@4 307 double resx, resy, resz;
nuclear@4 308 Ray ray;
nuclear@4 309
nuclear@4 310 glGetDoublev(GL_MODELVIEW_MATRIX, mv);
nuclear@4 311 glGetDoublev(GL_PROJECTION_MATRIX, proj);
nuclear@4 312 glGetIntegerv(GL_VIEWPORT, vp);
nuclear@4 313
nuclear@4 314 gluUnProject(x, y, 0, mv, proj, vp, &resx, &resy, &resz);
nuclear@4 315 ray.origin = Vector3(resx, resy, resz);
nuclear@4 316
nuclear@4 317 gluUnProject(x, y, 1, mv, proj, vp, &resx, &resy, &resz);
nuclear@4 318 ray.dir = normalize(Vector3(resx, resy, resz) - ray.origin);
nuclear@4 319
nuclear@4 320 return ray;
nuclear@0 321 }
nuclear@0 322
nuclear@0 323 static Window create_window(const char *title, int xsz, int ysz)
nuclear@0 324 {
nuclear@0 325 int scr = DefaultScreen(dpy);
nuclear@0 326 Window root = RootWindow(dpy, scr);
nuclear@0 327
nuclear@0 328 int glxattr[] = {
nuclear@0 329 GLX_RGBA,
nuclear@0 330 GLX_RED_SIZE, 8,
nuclear@0 331 GLX_GREEN_SIZE, 8,
nuclear@0 332 GLX_BLUE_SIZE, 8,
nuclear@0 333 GLX_DEPTH_SIZE, 24,
nuclear@0 334 GLX_DOUBLEBUFFER,
nuclear@0 335 #if defined(GLX_VERSION_1_4) || defined(GLX_ARB_multisample)
nuclear@0 336 GLX_SAMPLE_BUFFERS_ARB, 1,
nuclear@0 337 GLX_SAMPLES_ARB, 1,
nuclear@0 338 #endif
nuclear@0 339 None
nuclear@0 340 };
nuclear@0 341
nuclear@0 342 XVisualInfo *vis = glXChooseVisual(dpy, scr, glxattr);
nuclear@0 343 if(!vis) {
nuclear@0 344 fprintf(stderr, "failed to find a suitable visual\n");
nuclear@0 345 return 0;
nuclear@0 346 }
nuclear@0 347
nuclear@0 348 if(!(ctx = glXCreateContext(dpy, vis, 0, True))) {
nuclear@0 349 fprintf(stderr, "failed to create OpenGL context\n");
nuclear@0 350 XFree(vis);
nuclear@0 351 return -1;
nuclear@0 352 }
nuclear@0 353
nuclear@0 354 XSetWindowAttributes xattr;
nuclear@0 355 xattr.background_pixel = xattr.border_pixel = BlackPixel(dpy, scr);
nuclear@0 356 xattr.colormap = XCreateColormap(dpy, root, vis->visual, AllocNone);
nuclear@0 357 unsigned int xattr_mask = CWColormap | CWBackPixel | CWBorderPixel;
nuclear@0 358
nuclear@0 359 Window win = XCreateWindow(dpy, root, 0, 0, xsz, ysz, 0, vis->depth, InputOutput,
nuclear@0 360 vis->visual, xattr_mask, &xattr);
nuclear@0 361 if(!win) {
nuclear@0 362 fprintf(stderr, "failed to create window\n");
nuclear@0 363 glXDestroyContext(dpy, ctx);
nuclear@0 364 XFree(vis);
nuclear@0 365 return -1;
nuclear@0 366 }
nuclear@0 367 XFree(vis);
nuclear@0 368
nuclear@0 369 unsigned int evmask = StructureNotifyMask | VisibilityChangeMask | ExposureMask |
nuclear@0 370 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask |
nuclear@6 371 PointerMotionMask;
nuclear@0 372 XSelectInput(dpy, win, evmask);
nuclear@0 373
nuclear@0 374 xa_wm_prot = XInternAtom(dpy, "WM_PROTOCOLS", False);
nuclear@0 375 xa_wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
nuclear@0 376 XSetWMProtocols(dpy, win, &xa_wm_del_win, 1);
nuclear@0 377
nuclear@0 378 XClassHint hint;
nuclear@0 379 hint.res_name = hint.res_class = (char*)"equeue_win";
nuclear@0 380 XSetClassHint(dpy, win, &hint);
nuclear@0 381
nuclear@0 382 XTextProperty wm_name;
nuclear@0 383 XStringListToTextProperty((char**)&title, 1, &wm_name);
nuclear@0 384 XSetWMName(dpy, win, &wm_name);
nuclear@0 385 XSetWMIconName(dpy, win, &wm_name);
nuclear@0 386 XFree(wm_name.value);
nuclear@0 387
nuclear@0 388 XMapWindow(dpy, win);
nuclear@0 389 glXMakeCurrent(dpy, win, ctx);
nuclear@0 390
nuclear@0 391 return win;
nuclear@0 392 }
nuclear@0 393
nuclear@0 394 static void process_events()
nuclear@0 395 {
nuclear@0 396 XEvent ev;
nuclear@0 397
nuclear@0 398 while(XPending(dpy)) {
nuclear@0 399 XNextEvent(dpy, &ev);
nuclear@0 400 switch(ev.type) {
nuclear@0 401 case MapNotify:
nuclear@0 402 win_mapped = true;
nuclear@0 403 break;
nuclear@0 404
nuclear@0 405 case UnmapNotify:
nuclear@0 406 win_mapped = false;
nuclear@0 407 break;
nuclear@0 408
nuclear@0 409 case Expose:
nuclear@0 410 if(win_mapped && ev.xexpose.count == 0) {
nuclear@6 411 draw_pending = true;
nuclear@0 412 }
nuclear@0 413 break;
nuclear@0 414
nuclear@0 415 case MotionNotify:
nuclear@0 416 motion(ev.xmotion.x, ev.xmotion.y);
nuclear@0 417 break;
nuclear@0 418
nuclear@0 419 case ButtonPress:
nuclear@0 420 mouse(ev.xbutton.button - 1, true, ev.xbutton.x, ev.xbutton.y);
nuclear@0 421 break;
nuclear@0 422
nuclear@0 423 case ButtonRelease:
nuclear@0 424 mouse(ev.xbutton.button - 1, false, ev.xbutton.x, ev.xbutton.y);
nuclear@0 425 break;
nuclear@0 426
nuclear@0 427 case KeyPress:
nuclear@0 428 {
nuclear@0 429 KeySym sym = XLookupKeysym(&ev.xkey, 0);
nuclear@0 430 keyb(translate_keysym(sym), true);
nuclear@0 431 }
nuclear@0 432 break;
nuclear@0 433
nuclear@0 434 case KeyRelease:
nuclear@0 435 {
nuclear@0 436 KeySym sym = XLookupKeysym(&ev.xkey, 0);
nuclear@0 437 keyb(translate_keysym(sym), false);
nuclear@0 438 }
nuclear@0 439 break;
nuclear@0 440
nuclear@0 441 case ConfigureNotify:
nuclear@0 442 {
nuclear@0 443 int xsz = ev.xconfigure.width;
nuclear@0 444 int ysz = ev.xconfigure.height;
nuclear@0 445
nuclear@0 446 if(xsz != win_width || ysz != win_height) {
nuclear@0 447 win_width = xsz;
nuclear@0 448 win_height = ysz;
nuclear@0 449 reshape(xsz, ysz);
nuclear@0 450 }
nuclear@0 451 }
nuclear@0 452 break;
nuclear@0 453
nuclear@0 454 case ClientMessage:
nuclear@0 455 if(ev.xclient.message_type == xa_wm_prot) {
nuclear@0 456 if((Atom)ev.xclient.data.l[0] == xa_wm_del_win) {
nuclear@0 457 exit(0);
nuclear@0 458 }
nuclear@0 459 }
nuclear@0 460 break;
nuclear@0 461
nuclear@0 462 default:
nuclear@0 463 break;
nuclear@0 464 }
nuclear@0 465
nuclear@0 466 }
nuclear@0 467 }
nuclear@0 468
nuclear@0 469 static int translate_keysym(KeySym sym)
nuclear@0 470 {
nuclear@0 471 switch(sym) {
nuclear@0 472 case XK_BackSpace:
nuclear@0 473 return '\b';
nuclear@0 474 case XK_Tab:
nuclear@0 475 return '\t';
nuclear@0 476 case XK_Linefeed:
nuclear@0 477 return '\r';
nuclear@0 478 case XK_Return:
nuclear@0 479 return '\n';
nuclear@0 480 case XK_Escape:
nuclear@0 481 return 27;
nuclear@0 482 default:
nuclear@0 483 break;
nuclear@0 484 }
nuclear@0 485 return (int)sym;
nuclear@0 486 }
nuclear@2 487
nuclear@2 488 static int proc_args(int argc, char **argv)
nuclear@2 489 {
nuclear@2 490 for(int i=1; i<argc; i++) {
nuclear@2 491 if(argv[i][0] == '-') {
nuclear@2 492 fprintf(stderr, "unexpected option: %s\n", argv[i]);
nuclear@2 493 return -1;
nuclear@2 494
nuclear@2 495 } else {
nuclear@2 496 if(fake_devpath) {
nuclear@2 497 fprintf(stderr, "unexpected argument: %s\n", argv[i]);
nuclear@2 498 return -1;
nuclear@2 499 }
nuclear@2 500 fake_devpath = argv[i];
nuclear@2 501 }
nuclear@2 502 }
nuclear@2 503 if(!fake_devpath) {
nuclear@2 504 fprintf(stderr, "no device path specified, running standalone\n");
nuclear@2 505 }
nuclear@2 506 return 0;
nuclear@2 507 }