2 * starwars, Copyright (c) 1998-2001 Jamie Zawinski <jwz@jwz.org> and
3 * Claudio Matauoka <claudio@helllabs.org>
5 * Permission to use, copy, modify, distribute, and sell this software and its
6 * documentation for any purpose is hereby granted without fee, provided that
7 * the above copyright notice appear in all copies and that both that
8 * copyright notice and this permission notice appear in supporting
9 * documentation. No representations are made about the suitability of this
10 * software for any purpose. It is provided "as is" without express or
13 * Star Wars -- Phosphor meets a well-known scroller from a galaxy far,
14 * far away. Hacked by Claudio Matsuoka. Includes portions of
15 * mjk's GLUT library, Copyright (c) 1994, 1995, 1996 by Mark J.
16 * Kilgard. Roman simplex stroke font Copyright (c) 1989, 1990,
17 * 1991 by Sun Microsystems, Inc. and the X Consortium.
20 * - I tried texturized fonts but the roman simplex stroke font
21 * was the most readable for the 80-column text from fortune.
22 * - The proportional font is bad for text from ps(1) or w(1).
23 * - Apparently the RIVA TNT cards for PCs don't like the stars to
24 * be drawn in orthogonal perspective, causing unnecessary system
28 * 20000221 claudio First version
29 * 20010124 jwz Rewrote large sections to add the ability to
30 * run a subprocess, customization of the font
31 * size and other parameters, etc.
34 #include <X11/Intrinsic.h>
36 extern XtAppContext app;
38 #define PROGCLASS "StarWars"
39 #define HACK_INIT init_sws
40 #define HACK_DRAW draw_sws
41 #define sws_opts xlockmore_opts
43 #define DEF_PROGRAM ZIPPY_PROGRAM
44 #define DEF_LINES "500"
45 #define DEF_STEPS "35"
46 #define DEF_SPIN "0.03"
47 #define DEF_FONT_SIZE "-1"
48 #define DEF_COLUMNS "-1"
49 #define DEF_WRAP "True"
50 #define DEF_ALIGN "Center"
54 #define BASE_FONT_SIZE 18 /* magic */
55 #define BASE_FONT_COLUMNS 80 /* magic */
58 #define DEFAULTS "*delay: 40000 \n" \
59 "*program: " DEF_PROGRAM "\n" \
60 "*lines: " DEF_LINES "\n" \
61 "*spin: " DEF_SPIN "\n" \
62 "*steps: " DEF_STEPS "\n" \
63 "*starwars.fontSize: " DEF_FONT_SIZE "\n" \
64 "*starwars.columns: " DEF_COLUMNS "\n" \
65 "*starwars.lineWrap: " DEF_WRAP "\n" \
66 "*starwars.alignment:" DEF_ALIGN "\n"
69 #define countof(x) (sizeof((x))/sizeof((*x)))
71 #include "xlockmore.h"
73 #ifdef USE_GL /* whole file */
76 #include "glutstroke.h"
77 #include "glut_roman.h"
78 #define GLUT_FONT (&glutStrokeRoman)
82 GLXContext *glx_context;
84 GLuint text_list, star_list;
88 Time subproc_relaunch_delay;
99 double intra_line_scroll;
104 static sws_configuration *scs = NULL;
106 static char *program;
107 static int max_lines;
108 static int scroll_steps;
109 static float star_spin;
110 static float font_size;
111 static int target_columns;
113 static char *alignment_str;
114 static int alignment;
116 static XrmOptionDescRec opts[] = {
117 {"-program", ".starwars.program", XrmoptionSepArg, (caddr_t) 0 },
118 {"-lines", ".starwars.lines", XrmoptionSepArg, (caddr_t) 0 },
119 {"-steps", ".starwars.steps", XrmoptionSepArg, (caddr_t) 0 },
120 {"-spin", ".starwars.spin", XrmoptionSepArg, (caddr_t) 0 },
121 {"-size", ".starwars.fontSize", XrmoptionSepArg, (caddr_t) 0 },
122 {"-columns", ".starwars.columns", XrmoptionSepArg, (caddr_t) 0 },
123 {"-wrap", ".starwars.lineWrap", XrmoptionNoArg, (caddr_t) "True" },
124 {"-no-wrap", ".starwars.lineWrap", XrmoptionNoArg, (caddr_t) "False" },
125 {"-nowrap", ".starwars.lineWrap", XrmoptionNoArg, (caddr_t) "False" },
126 {"-left", ".starwars.alignment",XrmoptionNoArg, (caddr_t) "Left" },
127 {"-right", ".starwars.alignment",XrmoptionNoArg, (caddr_t) "Right" },
128 {"-center", ".starwars.alignment",XrmoptionNoArg, (caddr_t) "Center" },
131 static argtype vars[] = {
132 {(caddr_t *) &program, "program", "Program", DEF_PROGRAM, t_String},
133 {(caddr_t *) &max_lines, "lines", "Integer", DEF_LINES, t_Int},
134 {(caddr_t *) &scroll_steps, "steps", "Integer", DEF_STEPS, t_Int},
135 {(caddr_t *) &star_spin, "spin", "Float", DEF_SPIN, t_Float},
136 {(caddr_t *) &font_size, "fontSize","Float", DEF_STEPS, t_Float},
137 {(caddr_t *) &target_columns, "columns", "Integer", DEF_COLUMNS, t_Int},
138 {(caddr_t *) &wrap_p, "lineWrap","Boolean", DEF_COLUMNS, t_Bool},
139 {(caddr_t *) &alignment_str, "alignment","Alignment",DEF_ALIGN, t_String},
142 ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
146 /* Tabs are bad, mmmkay? */
149 untabify (const char *string)
151 char *result = (char *) malloc ((strlen(string) * 8) + 1);
161 } while (col % TAB_WIDTH);
164 else if (*string == '\r' || *string == '\n')
182 (This bit mostly cribbed from phosphor.c)
185 static void drain_input (sws_configuration *sc);
188 subproc_cb (XtPointer closure, int *source, XtInputId *id)
190 sws_configuration *sc = (sws_configuration *) closure;
196 launch_text_generator (sws_configuration *sc)
198 char *oprogram = get_string_resource ("program", "Program");
199 char *program = (char *) malloc (strlen (oprogram) + 10);
201 strcpy (program, "( ");
202 strcat (program, oprogram);
203 strcat (program, " ) 2>&1");
205 if ((sc->pipe = popen (program, "r")))
208 XtAppAddInput (app, fileno (sc->pipe),
209 (XtPointer) (XtInputReadMask | XtInputExceptMask),
210 subproc_cb, (XtPointer) sc);
220 relaunch_generator_timer (XtPointer closure, XtIntervalId *id)
222 sws_configuration *sc = (sws_configuration *) closure;
223 launch_text_generator (sc);
227 /* When the subprocess has generated some output, this reads as much as it
228 can into sc->buf at sc->buf_tail.
231 drain_input (sws_configuration *sc)
233 if (sc->buf_tail < sizeof(sc->buf) - 2)
235 int target = sizeof(sc->buf) - sc->buf_tail - 2;
236 int n = read (fileno (sc->pipe),
237 (void *) (sc->buf + sc->buf_tail),
242 sc->buf[sc->buf_tail] = 0;
246 XtRemoveInput (sc->pipe_id);
251 /* If the process didn't print a terminating newline, add one. */
252 if (sc->buf_tail > 1 &&
253 sc->buf[sc->buf_tail-1] != '\n')
255 sc->buf[sc->buf_tail++] = '\n';
256 sc->buf[sc->buf_tail] = 0;
259 /* Then add one more, just for giggles. */
260 sc->buf[sc->buf_tail++] = '\n';
261 sc->buf[sc->buf_tail] = 0;
263 /* Set up a timer to re-launch the subproc in a bit. */
264 XtAppAddTimeOut (app, sc->subproc_relaunch_delay,
265 relaunch_generator_timer,
272 /* Populates the sc->lines list with as many lines as are currently in
273 sc->buf (which was filled by drain_input().
276 get_more_lines (sws_configuration *sc)
280 while (sc->total_lines < max_lines)
282 if (s >= sc->buf + sc->buf_tail)
284 /* Reached end of buffer before end of line. Bail. */
288 if (*s == '\n' || col > sc->columns)
296 /* We wrapped -- try to back up to the previous word boundary. */
299 while (s2 > sc->buf && *s2 != ' ' && *s2 != '\t')
309 sc->lines[sc->total_lines] = (char *) malloc (L+1);
310 memcpy (sc->lines[sc->total_lines], sc->buf, L);
311 sc->lines[sc->total_lines][L] = 0;
314 char *t = sc->lines[sc->total_lines];
315 char *ut = untabify (t);
316 sc->lines[sc->total_lines] = ut;
322 if (sc->buf_tail > (s - sc->buf))
324 int i = sc->buf_tail - (s - sc->buf);
325 memcpy (sc->buf, s, i);
327 sc->buf[sc->buf_tail] = 0;
334 sc->buf[sc->buf_tail] = 0;
342 col = TAB_WIDTH * ((col / TAB_WIDTH) + 1);
350 draw_string (int x, int y, const char *s)
352 if (!s || !*s) return;
354 glTranslatef (x, y, 0);
356 glutStrokeCharacter (GLUT_FONT, *s++);
363 grid (double width, double height, double spacing, double z)
366 for (y = 0; y <= height/2; y += spacing)
368 glBegin(GL_LINE_LOOP);
369 glVertex3f(-width/2, y, z);
370 glVertex3f( width/2, y, z);
372 glBegin(GL_LINE_LOOP);
373 glVertex3f(-width/2, -y, z);
374 glVertex3f( width/2, -y, z);
377 for (x = 0; x <= width/2; x += spacing)
379 glBegin(GL_LINE_LOOP);
380 glVertex3f( x, -height/2, z);
381 glVertex3f( x, height/2, z);
383 glBegin(GL_LINE_LOOP);
384 glVertex3f(-x, -height/2, z);
385 glVertex3f(-x, height/2, z);
389 glBegin(GL_LINE_LOOP);
390 glVertex3f(-width, 0, z);
391 glVertex3f( width, 0, z);
393 glBegin(GL_LINE_LOOP);
394 glVertex3f(0, -height, z);
395 glVertex3f(0, height, z);
400 box (double width, double height, double depth)
402 glBegin(GL_LINE_LOOP);
403 glVertex3f(-width/2, -height/2, -depth/2);
404 glVertex3f(-width/2, height/2, -depth/2);
405 glVertex3f( width/2, height/2, -depth/2);
406 glVertex3f( width/2, -height/2, -depth/2);
408 glBegin(GL_LINE_LOOP);
409 glVertex3f(-width/2, -height/2, depth/2);
410 glVertex3f(-width/2, height/2, depth/2);
411 glVertex3f( width/2, height/2, depth/2);
412 glVertex3f( width/2, -height/2, depth/2);
414 glBegin(GL_LINE_LOOP);
415 glVertex3f(-width/2, -height/2, -depth/2);
416 glVertex3f(-width/2, -height/2, depth/2);
417 glVertex3f(-width/2, height/2, depth/2);
418 glVertex3f(-width/2, height/2, -depth/2);
420 glBegin(GL_LINE_LOOP);
421 glVertex3f( width/2, -height/2, -depth/2);
422 glVertex3f( width/2, -height/2, depth/2);
423 glVertex3f( width/2, height/2, depth/2);
424 glVertex3f( width/2, height/2, -depth/2);
428 glBegin(GL_LINE_LOOP);
429 glVertex3f(-width/2, height/2, depth/2);
430 glVertex3f(-width/2, -height/2, -depth/2);
432 glBegin(GL_LINE_LOOP);
433 glVertex3f( width/2, height/2, depth/2);
434 glVertex3f( width/2, -height/2, -depth/2);
436 glBegin(GL_LINE_LOOP);
437 glVertex3f(-width/2, -height/2, depth/2);
438 glVertex3f(-width/2, height/2, -depth/2);
440 glBegin(GL_LINE_LOOP);
441 glVertex3f( width/2, -height/2, depth/2);
442 glVertex3f( width/2, height/2, -depth/2);
448 /* Window management, etc
451 reshape (sws_configuration *sc, int width, int height)
453 static Bool stars_done = False;
455 glViewport (0, 0, (GLint) width, (GLint) height);
459 int nstars = width * height / 320;
460 glDeleteLists (sc->star_list, 1);
461 sc->star_list = glGenLists (1);
462 glNewList (sc->star_list, GL_COMPILE);
464 for (i = 0; i < nstars; i++)
466 GLfloat c = 0.6 + 0.3 * random() / RAND_MAX;
468 glVertex3f (2 * width * (0.5 - 1.0 * random() / RAND_MAX),
469 2 * height * (0.5 - 1.0 * random() / RAND_MAX),
480 gl_init (ModeInfo *mi)
482 sws_configuration *sc = &scs[MI_SCREEN(mi)];
484 program = get_string_resource ("program", "Program");
486 glMatrixMode (GL_MODELVIEW);
488 glDisable (GL_LIGHTING);
489 glDisable (GL_DEPTH_TEST);
491 sc->text_list = glGenLists (1);
492 glNewList (sc->text_list, GL_COMPILE);
495 sc->star_list = glGenLists (1);
496 glNewList (sc->star_list, GL_COMPILE);
502 init_sws (ModeInfo *mi)
506 sws_configuration *sc;
509 scs = (sws_configuration *)
510 calloc (MI_NUM_SCREENS(mi), sizeof (sws_configuration));
512 fprintf(stderr, "%s: out of memory\n", progname);
516 sc = &scs[MI_SCREEN(mi)];
517 sc->lines = (char **) calloc (max_lines+1, sizeof(char *));
520 sc = &scs[MI_SCREEN(mi)];
522 if ((sc->glx_context = init_GL(mi)) != NULL) {
524 reshape(sc, MI_WIDTH(mi), MI_HEIGHT(mi));
529 font_height = GLUT_FONT->top - GLUT_FONT->bottom;
530 sc->font_scale = 1.0 / glutStrokeWidth (GLUT_FONT, 'z'); /* 'n' seems
532 if (target_columns > 0)
534 sc->columns = target_columns;
539 font_size = BASE_FONT_SIZE;
540 sc->columns = BASE_FONT_COLUMNS * ((double) BASE_FONT_SIZE / font_size);
543 sc->font_scale /= sc->columns;
544 sc->line_height = font_height * sc->font_scale;
547 if (!wrap_p) sc->columns = 1000; /* wrap anyway, if it's absurdly long. */
549 sc->subproc_relaunch_delay = 2 * 1000;
550 sc->total_lines = max_lines-1;
551 launch_text_generator (sc);
554 star_spin = -star_spin;
556 if (!alignment_str || !*alignment_str ||
557 !strcasecmp(alignment_str, "left"))
559 else if (!strcasecmp(alignment_str, "center") ||
560 !strcasecmp(alignment_str, "middle"))
562 else if (!strcasecmp(alignment_str, "right"))
567 "%s: alignment must be left, center, or right, not \"%s\"\n",
568 progname, alignment_str);
575 draw_sws (ModeInfo *mi)
577 sws_configuration *sc = &scs[MI_SCREEN(mi)];
578 Display *dpy = MI_DISPLAY(mi);
579 Window window = MI_WINDOW(mi);
582 if (!sc->glx_context)
585 if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
586 XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
588 glDrawBuffer (GL_BACK);
589 glXMakeCurrent (dpy, window, *(sc->glx_context));
591 glClear (GL_COLOR_BUFFER_BIT);
595 glMatrixMode (GL_PROJECTION);
597 glOrtho (-0.5 * MI_WIDTH(mi), 0.5 * MI_WIDTH(mi),
598 -0.5 * MI_HEIGHT(mi), 0.5 * MI_HEIGHT(mi),
600 glRotatef (sc->star_theta, 0.0, 0.0, 1.0);
601 glCallList (sc->star_list);
604 gluPerspective (80.0, 4.0/3.0, 10, 500000);
605 glMatrixMode (GL_MODELVIEW);
606 gluLookAt (0.0, 0.0, 4600.0,
610 glRotatef (-60.0, 1.0, 0.0, 0.0);
612 /* The above gives us an arena where the bottom edge of the screen is
613 represented by the line (-2100,-3140,0) - ( 2100,-3140,0). */
615 /* Now let's move the origin to the front of the screen. */
616 glTranslatef (0.0, -3140, 0.0);
618 /* And then let's scale so that the bottom of the screen is 1.0 wide. */
619 glScalef (4200, 4200, 4200);
621 /* Scroll to current position */
622 glTranslatef (0.0, sc->intra_line_scroll, 0.0);
624 glColor3f (1.0, 1.0, 0.4);
625 glCallList (sc->text_list);
627 sc->intra_line_scroll += sc->line_height / scroll_steps;
629 if (sc->intra_line_scroll >= sc->line_height)
631 static time_t reshape_time = 0;
632 time_t now = time((time_t) 0);
633 if (reshape_time != now) /* only poll for reshape once a second */
636 XGetWindowAttributes (dpy, window, &mi->xgwa);
637 reshape(sc, MI_WIDTH(mi), MI_HEIGHT(mi));
640 sc->intra_line_scroll = 0;
642 /* Drop the oldest line off the end. */
646 /* Scroll the contents of the lines array toward 0. */
647 if (sc->total_lines > 0)
649 for (i = 1; i < sc->total_lines; i++)
650 sc->lines[i-1] = sc->lines[i];
651 sc->lines[--sc->total_lines] = 0;
654 /* Bring in new lines at the end. */
657 if (sc->total_lines < max_lines)
658 /* Oops, we ran out of text... well, insert some blank lines
659 here so that new text still pulls in from the bottom of
660 the screen, isntead of just appearing. */
661 sc->total_lines = max_lines;
663 glDeleteLists (sc->text_list, 1);
664 sc->text_list = glGenLists (1);
665 glNewList (sc->text_list, GL_COMPILE);
667 glScalef (sc->font_scale, sc->font_scale, sc->font_scale);
668 for (i = 0; i < sc->total_lines; i++)
670 int offscreen_lines = 3;
673 double y = ((sc->total_lines - (i + offscreen_lines) - 1)
676 char *line = sc->lines[i];
679 sprintf(n, "%d:", i);
680 draw_string (x / sc->font_scale, y / sc->font_scale, n);
686 xoff = 1.0 - (glutStrokeLength(GLUT_FONT, line) * sc->font_scale);
690 draw_string ((x + xoff) / sc->font_scale, y / sc->font_scale, line);
699 glXSwapBuffers(dpy, window);
701 sc->star_theta += star_spin;