eqemu

annotate src/main.cc @ 12:2656099aff12

added copyright notices and license
author John Tsiombikas <nuclear@member.fsf.org>
date Fri, 18 Jul 2014 07:04:21 +0300
parents 2b559dc24c7b
children
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 }