1 /* tronbit, Copyright (c) 2011-2014 Jamie Zawinski <jwz@jwz.org>
2 *
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
9 * implied warranty.
10 */
11
12 #define DEFAULTS "*delay: 30000 \n" \
13 "*count: 30 \n" \
14 "*showFPS: False \n" \
15 "*wireframe: False \n"
16
17 # define release_bit 0
18 #undef countof
19 #define countof(x) (sizeof((x))/sizeof((*x)))
20
21 #include "xlockmore.h"
22 #include "colors.h"
23 #include "sphere.h"
24 #include "rotator.h"
25 #include "gltrackball.h"
26 #include <ctype.h>
27
28 #ifdef USE_GL /* whole file */
29
30 #include "gllist.h"
31
32 extern const struct gllist *tronbit_idle1, *tronbit_idle2,
33 *tronbit_no, *tronbit_yes;
34 static const struct gllist * const *all_objs[] = {
35 &tronbit_idle1, &tronbit_idle2, &tronbit_no, &tronbit_yes };
36
37
38 #define DEF_SPIN "True"
39 #define DEF_WANDER "True"
40 #define DEF_SPEED "1.0"
41
42 #define HISTORY_LENGTH 512
43 typedef enum { BIT_IDLE1, BIT_IDLE2, BIT_NO, BIT_YES } bit_state;
44 #define MODELS 4
45
46
47 typedef struct {
48 GLXContext *glx_context;
49 rotator *rot;
50 trackball_state *trackball;
51 Bool button_down_p;
52
53 double frequency;
54 double confidence;
55
56 double last_time;
57 unsigned char history [HISTORY_LENGTH];
58 unsigned char histogram [HISTORY_LENGTH];
59 int history_fp, histogram_fp;
60
61 GLuint dlists[MODELS], polys[MODELS];
62 char kbd;
63
64 } bit_configuration;
65
66 static bit_configuration *bps = NULL;
67
68 static const GLfloat colors[][4] = {
69 { 0.66, 0.85, 1.00, 1.00 },
70 { 0.66, 0.85, 1.00, 1.00 },
71 { 1.00, 0.12, 0.12, 1.00 },
72 { 0.98, 0.85, 0.30, 1.00 }
73 };
74
75
76 static Bool do_spin;
77 static GLfloat speed;
78 static Bool do_wander;
79
80 static XrmOptionDescRec opts[] = {
81 { "-spin", ".spin", XrmoptionNoArg, "True" },
82 { "+spin", ".spin", XrmoptionNoArg, "False" },
83 { "-speed", ".speed", XrmoptionSepArg, 0 },
84 { "-wander", ".wander", XrmoptionNoArg, "True" },
85 { "+wander", ".wander", XrmoptionNoArg, "False" }
86 };
87
88 static argtype vars[] = {
89 {&do_spin, "spin", "Spin", DEF_SPIN, t_Bool},
90 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
91 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
92 };
93
94 ENTRYPOINT ModeSpecOpt bit_opts = {countof(opts), opts, countof(vars), vars, NULL};
95
96
97 /* Returns the current time in seconds as a double.
98 */
99 static double
double_time(void)100 double_time (void)
101 {
102 struct timeval now;
103 # ifdef GETTIMEOFDAY_TWO_ARGS
104 struct timezone tzp;
105 gettimeofday(&now, &tzp);
106 # else
107 gettimeofday(&now);
108 # endif
109
110 return (now.tv_sec + ((double) now.tv_usec * 0.000001));
111 }
112
113
114 static int
make_bit(ModeInfo * mi,bit_state which)115 make_bit (ModeInfo *mi, bit_state which)
116 {
117 static const GLfloat spec[4] = {1.0, 1.0, 1.0, 1.0};
118 static const GLfloat shiny = 128.0;
119 const GLfloat *color = colors[which];
120 int wire = MI_IS_WIREFRAME(mi);
121 int polys = 0;
122 GLfloat s;
123 const struct gllist *gll;
124
125 glMaterialfv (GL_FRONT, GL_SPECULAR, spec);
126 glMateriali (GL_FRONT, GL_SHININESS, shiny);
127 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
128 glColor4f (color[0], color[1], color[2], color[3]);
129
130 glPushMatrix();
131 switch (which)
132 {
133 case BIT_IDLE1:
134 glRotatef (-44, 0, 1, 0); /* line up the models with each other */
135 glRotatef (-11, 1, 0, 0);
136 glRotatef ( 8, 0, 0, 1);
137 s = 1.0;
138 break;
139 case BIT_IDLE2:
140 glRotatef ( 16.0, 0, 0, 1);
141 glRotatef (-28.0, 1, 0, 0);
142 s = 1.0;
143 break;
144 case BIT_NO:
145 glRotatef ( 16.0, 0, 0, 1);
146 glRotatef (-28.0, 1, 0, 0);
147 s = 1.6;
148 break;
149 case BIT_YES:
150 glRotatef (-44.0, 0, 1, 0);
151 glRotatef (-32.0, 1, 0, 0);
152 s = 1.53;
153 break;
154 default:
155 abort();
156 break;
157 }
158 glScalef (s, s, s);
159 gll = *all_objs[which];
160 renderList (gll, wire);
161 polys += gll->points / 3;
162 glPopMatrix();
163
164 return polys;
165 }
166
167
168 static void
tick_bit(ModeInfo * mi,double now)169 tick_bit (ModeInfo *mi, double now)
170 {
171 bit_configuration *bp = &bps[MI_SCREEN(mi)];
172 double freq = bp->frequency;
173 int n = bp->history[bp->history_fp];
174 int histogram_speed = 3 * speed;
175 int i;
176
177 if (histogram_speed < 1) histogram_speed = 1;
178
179 if (n == BIT_YES || n == BIT_NO)
180 freq *= 2;
181
182 if (bp->button_down_p) return;
183
184 for (i = 0; i < histogram_speed; i++)
185 {
186 int nn = (n == BIT_YES ? 240 : n == BIT_NO ? 17 : 128);
187 int on = bp->histogram[(bp->histogram_fp-1) % countof(bp->histogram)];
188
189 /* smooth out the square wave a little bit */
190
191 if (!(nn > 100 && nn < 200) !=
192 !(on > 100 && on < 200))
193 nn += (((random() % 48) - 32) *
194 ((on > 100 && on < 200) ? 1 : -1));
195
196 nn += (random() % 16) - 8;
197
198 bp->histogram_fp++;
199 if (bp->histogram_fp >= countof(bp->history))
200 bp->histogram_fp = 0;
201 bp->histogram [bp->histogram_fp] = nn;
202 }
203
204
205 if (bp->last_time + freq > now && !bp->kbd) return;
206
207 bp->last_time = now;
208
209 bp->history_fp++;
210 if (bp->history_fp >= countof(bp->history))
211 bp->history_fp = 0;
212
213 if (bp->kbd)
214 {
215 n = (bp->kbd == '1' ? BIT_YES :
216 bp->kbd == '0' ? BIT_NO :
217 (random() & 1) ? BIT_YES : BIT_NO);
218 bp->kbd = 0;
219 }
220 else if (n == BIT_YES ||
221 n == BIT_NO ||
222 frand(1.0) >= bp->confidence)
223 n = (n == BIT_IDLE1 ? BIT_IDLE2 : BIT_IDLE1);
224 else
225 n = (random() & 1) ? BIT_YES : BIT_NO;
226
227 bp->history [bp->history_fp] = n;
228 }
229
230
231 static int
animate_bits(ModeInfo * mi,bit_state omodel,bit_state nmodel,GLfloat ratio)232 animate_bits (ModeInfo *mi, bit_state omodel, bit_state nmodel, GLfloat ratio)
233 {
234 bit_configuration *bp = &bps[MI_SCREEN(mi)];
235 int polys = 0;
236 GLfloat scale = sin (ratio * M_PI / 2);
237 GLfloat osize, nsize, small;
238 int wire = MI_IS_WIREFRAME(mi);
239
240 glShadeModel(GL_SMOOTH);
241
242 glEnable(GL_DEPTH_TEST);
243 glEnable(GL_NORMALIZE);
244 glEnable(GL_CULL_FACE);
245
246 if (!wire)
247 {
248 glEnable(GL_LIGHTING);
249 glEnable(GL_DEPTH_TEST);
250 glEnable(GL_CULL_FACE);
251 }
252
253 if ((omodel == BIT_IDLE1 || omodel == BIT_IDLE2) &&
254 (nmodel == BIT_IDLE1 || nmodel == BIT_IDLE2))
255 small = 0.9;
256 else
257 small = 0.5;
258
259 nsize = small + (1 - small) * scale;
260 osize = small + (1 - small) * (1 - scale);
261
262 glPushMatrix();
263 glScalef (osize, osize, osize);
264 glCallList (bp->dlists [omodel]);
265 polys += bp->polys [omodel];
266 glPopMatrix();
267
268 glPushMatrix();
269 glScalef (nsize, nsize, nsize);
270 glCallList (bp->dlists [nmodel]);
271 polys += bp->polys [nmodel];
272 glPopMatrix();
273
274 return polys;
275 }
276
277
278 static int
draw_histogram(ModeInfo * mi,GLfloat ratio)279 draw_histogram (ModeInfo *mi, GLfloat ratio)
280 {
281 bit_configuration *bp = &bps[MI_SCREEN(mi)];
282 int samples = countof (bp->histogram);
283 GLfloat scalex = (GLfloat) mi->xgwa.width / samples;
284 GLfloat scaley = mi->xgwa.height / 255.0 / 4; /* about 1/4th of screen */
285 int polys = 0;
286 int overlays = 5;
287 int k;
288
289 glDisable (GL_TEXTURE_2D);
290 glDisable (GL_LIGHTING);
291 glDisable (GL_BLEND);
292 glDisable (GL_DEPTH_TEST);
293 glDisable (GL_CULL_FACE);
294
295 glMatrixMode(GL_PROJECTION);
296 glPushMatrix();
297 {
298 glLoadIdentity();
299 glMatrixMode(GL_MODELVIEW);
300 glPushMatrix();
301
302 glLoadIdentity();
303 /* glRotatef(current_device_rotation(), 0, 0, 1); */
304 glOrtho (0, mi->xgwa.width, 0, mi->xgwa.height, -1, 1);
305
306 for (k = 0; k < overlays; k++)
307 {
308 int i, j;
309 GLfloat a = (GLfloat) k / overlays;
310
311 glColor3f (0.3 * a, 0.7 * a, 1.0 * a);
312
313 glBegin (GL_LINE_STRIP);
314
315 j = bp->histogram_fp + 1;
316 for (i = 0; i < samples; i++)
317 {
318 GLfloat x, y, z;
319 if (j >= samples) j = 0;
320 x = i;
321 y = bp->histogram[j];
322 z = 0;
323
324 y += (int) ((random() % 16) - 8);
325 y += 16; /* margin at bottom of screen */
326
327 x *= scalex;
328 y *= scaley;
329
330 glVertex3f (x, y, z);
331 ++j;
332 polys++;
333 }
334 glEnd();
335 }
336
337 glPopMatrix();
338 }
339 glMatrixMode(GL_PROJECTION);
340 glPopMatrix();
341
342 glMatrixMode(GL_MODELVIEW);
343
344 return polys;
345 }
346
347
348 /* Window management, etc
349 */
350 ENTRYPOINT void
reshape_bit(ModeInfo * mi,int width,int height)351 reshape_bit (ModeInfo *mi, int width, int height)
352 {
353 GLfloat h = (GLfloat) height / (GLfloat) width;
354
355 glViewport (0, 0, (GLint) width, (GLint) height);
356
357 glMatrixMode(GL_PROJECTION);
358 glLoadIdentity();
359 gluPerspective (30.0, 1/h, 1.0, 100.0);
360
361 glMatrixMode(GL_MODELVIEW);
362 glLoadIdentity();
363 gluLookAt( 0.0, 0.0, 30.0,
364 0.0, 0.0, 0.0,
365 0.0, 1.0, 0.0);
366
367 glClear(GL_COLOR_BUFFER_BIT);
368 }
369
370
371
372 ENTRYPOINT Bool
bit_handle_event(ModeInfo * mi,XEvent * event)373 bit_handle_event (ModeInfo *mi, XEvent *event)
374 {
375 bit_configuration *bp = &bps[MI_SCREEN(mi)];
376
377 if (gltrackball_event_handler (event, bp->trackball,
378 MI_WIDTH (mi), MI_HEIGHT (mi),
379 &bp->button_down_p))
380 return True;
381 else if (event->xany.type == KeyPress)
382 {
383 KeySym keysym;
384 char c = 0;
385 XLookupString (&event->xkey, &c, 1, &keysym, 0);
386
387 if (keysym == XK_Up || keysym == XK_Left || keysym == XK_Prior)
388 c = '1';
389 else if (keysym == XK_Down || keysym == XK_Right || keysym == XK_Next)
390 c = '0';
391
392 if (c == ' ' || c == '\t' || c == '\n' || c == '1' || c == '0')
393 {
394 bp->kbd = c;
395 return True;
396 }
397 }
398
399 return False;
400 }
401
402
403 ENTRYPOINT void
init_bit(ModeInfo * mi)404 init_bit (ModeInfo *mi)
405 {
406 bit_configuration *bp;
407 int i;
408
409 MI_INIT (mi, bps);
410
411 bp = &bps[MI_SCREEN(mi)];
412
413 bp->glx_context = init_GL(mi);
414
415 reshape_bit (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
416
417 {
418 double spin_speed = 3.0;
419 double wander_speed = 0.03 * speed;
420 double spin_accel = 4.0;
421
422 bp->rot = make_rotator (do_spin ? spin_speed : 0,
423 do_spin ? spin_speed : 0,
424 do_spin ? spin_speed : 0,
425 spin_accel,
426 do_wander ? wander_speed : 0,
427 False);
428 bp->trackball = gltrackball_init (False);
429 }
430
431 for (i = 0; i < countof(bp->dlists); i++)
432 {
433 bp->dlists[i] = glGenLists (1);
434 glNewList (bp->dlists[i], GL_COMPILE);
435 bp->polys [i] = make_bit (mi, i);
436 glEndList ();
437 }
438
439 bp->frequency = 0.30 / speed; /* parity around 3x/second */
440 bp->confidence = 0.06; /* provide answer 1/15 or so */
441
442 for (i = 0; i < countof(bp->histogram); i++)
443 bp->histogram[i] = 128 + (random() % 16) - 8;
444 }
445
446
447 ENTRYPOINT void
draw_bit(ModeInfo * mi)448 draw_bit (ModeInfo *mi)
449 {
450 bit_configuration *bp = &bps[MI_SCREEN(mi)];
451 Display *dpy = MI_DISPLAY(mi);
452 Window window = MI_WINDOW(mi);
453 int wire = MI_IS_WIREFRAME(mi);
454
455 if (!bp->glx_context)
456 return;
457
458 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *bp->glx_context);
459
460 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
461
462 if (!wire)
463 {
464 GLfloat pos[4] = {1.0, 1.0, 1.0, 0.0};
465 GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
466 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
467 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
468
469 glEnable(GL_LIGHTING);
470 glEnable(GL_LIGHT0);
471 glLightfv(GL_LIGHT0, GL_POSITION, pos);
472 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
473 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
474 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
475 }
476
477 glPushMatrix ();
478 glScalef(1.1, 1.1, 1.1);
479
480 # ifdef HAVE_MOBILE /* Keep it the same relative size when rotated. */
481 {
482 GLfloat h = MI_HEIGHT(mi) / (GLfloat) MI_WIDTH(mi);
483 int o = (int) current_device_rotation();
484 if (o != 0 && o != 180 && o != -180)
485 glScalef (1/h, 1/h, 1/h);
486 glRotatef(o, 0, 0, 1);
487 }
488 # endif
489
490 {
491 double x, y, z;
492 get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
493 glTranslatef((x - 0.5) * 11,
494 (y - 0.5) * 5,
495 (z - 0.5) * 3);
496 gltrackball_rotate (bp->trackball);
497
498 get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p);
499 glRotatef (x * 360, 1.0, 0.0, 0.0);
500 glRotatef (y * 360, 0.0, 1.0, 0.0);
501 glRotatef (z * 360, 0.0, 0.0, 1.0);
502 }
503
504 mi->polygon_count = 0;
505
506 glScalef (6, 6, 6);
507
508 {
509 int nmodel = bp->history [bp->history_fp];
510 int omodel = bp->history [bp->history_fp > 0
511 ? bp->history_fp-1
512 : countof(bp->history)-1];
513 double now = double_time();
514 double ratio = 1 - ((bp->last_time + bp->frequency) - now) / bp->frequency;
515 if (ratio > 1) ratio = 1;
516 mi->polygon_count += draw_histogram (mi, ratio);
517
518 if (MI_WIDTH(mi) > MI_HEIGHT(mi) * 5) { /* wide window: scale up */
519 glScalef (8, 8, 8);
520 }
521
522 mi->polygon_count += animate_bits (mi, omodel, nmodel, ratio);
523 tick_bit (mi, now);
524 }
525 glPopMatrix ();
526
527 if (mi->fps_p) do_fps (mi);
528 glFinish();
529
530 glXSwapBuffers(dpy, window);
531 }
532
533
534 ENTRYPOINT void
free_bit(ModeInfo * mi)535 free_bit (ModeInfo *mi)
536 {
537 bit_configuration *bp = &bps[MI_SCREEN(mi)];
538 int i;
539
540 if (!bp->glx_context) return;
541 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *bp->glx_context);
542
543 if (bp->trackball) gltrackball_free (bp->trackball);
544 if (bp->rot) free_rotator (bp->rot);
545 for (i = 0; i < countof(bp->dlists); i++)
546 if (glIsList(bp->dlists[i])) glDeleteLists(bp->dlists[i], 1);
547
548 }
549
550 XSCREENSAVER_MODULE_2 ("TronBit", tronbit, bit)
551
552 #endif /* USE_GL */
553