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 HACK_RESHAPE reshape_sws
42 #define sws_opts xlockmore_opts
44 #define DEF_PROGRAM ZIPPY_PROGRAM
45 #define DEF_LINES "500"
46 #define DEF_STEPS "35"
47 #define DEF_SPIN "0.03"
48 #define DEF_FONT_SIZE "-1"
49 #define DEF_COLUMNS "-1"
50 #define DEF_WRAP "True"
51 #define DEF_ALIGN "Center"
55 #define BASE_FONT_SIZE 18 /* magic */
56 #define BASE_FONT_COLUMNS 80 /* magic */
59 #define DEFAULTS "*delay: 40000 \n" \
60 "*showFPS: False \n" \
62 "*program: " DEF_PROGRAM "\n" \
63 "*lines: " DEF_LINES "\n" \
64 "*spin: " DEF_SPIN "\n" \
65 "*steps: " DEF_STEPS "\n" \
66 "*starwars.fontSize: " DEF_FONT_SIZE "\n" \
67 "*starwars.columns: " DEF_COLUMNS "\n" \
68 "*starwars.lineWrap: " DEF_WRAP "\n" \
69 "*starwars.alignment:" DEF_ALIGN "\n"
72 #define countof(x) (sizeof((x))/sizeof((*x)))
74 #include "xlockmore.h"
76 #ifdef USE_GL /* whole file */
79 #include "glutstroke.h"
80 #include "glut_roman.h"
81 #define GLUT_FONT (&glutStrokeRoman)
85 GLXContext *glx_context;
87 GLuint text_list, star_list;
91 Time subproc_relaunch_delay;
102 double intra_line_scroll;
107 static sws_configuration *scs = NULL;
109 static char *program;
110 static int max_lines;
111 static int scroll_steps;
112 static float star_spin;
113 static float font_size;
114 static int target_columns;
116 static char *alignment_str;
117 static int alignment;
119 static XrmOptionDescRec opts[] = {
120 {"-program", ".starwars.program", XrmoptionSepArg, (caddr_t) 0 },
121 {"-lines", ".starwars.lines", XrmoptionSepArg, (caddr_t) 0 },
122 {"-steps", ".starwars.steps", XrmoptionSepArg, (caddr_t) 0 },
123 {"-spin", ".starwars.spin", XrmoptionSepArg, (caddr_t) 0 },
124 {"-size", ".starwars.fontSize", XrmoptionSepArg, (caddr_t) 0 },
125 {"-columns", ".starwars.columns", XrmoptionSepArg, (caddr_t) 0 },
126 {"-wrap", ".starwars.lineWrap", XrmoptionNoArg, (caddr_t) "True" },
127 {"-no-wrap", ".starwars.lineWrap", XrmoptionNoArg, (caddr_t) "False" },
128 {"-nowrap", ".starwars.lineWrap", XrmoptionNoArg, (caddr_t) "False" },
129 {"-left", ".starwars.alignment",XrmoptionNoArg, (caddr_t) "Left" },
130 {"-right", ".starwars.alignment",XrmoptionNoArg, (caddr_t) "Right" },
131 {"-center", ".starwars.alignment",XrmoptionNoArg, (caddr_t) "Center" },
134 static argtype vars[] = {
135 {(caddr_t *) &program, "program", "Program", DEF_PROGRAM, t_String},
136 {(caddr_t *) &max_lines, "lines", "Integer", DEF_LINES, t_Int},
137 {(caddr_t *) &scroll_steps, "steps", "Integer", DEF_STEPS, t_Int},
138 {(caddr_t *) &star_spin, "spin", "Float", DEF_SPIN, t_Float},
139 {(caddr_t *) &font_size, "fontSize","Float", DEF_STEPS, t_Float},
140 {(caddr_t *) &target_columns, "columns", "Integer", DEF_COLUMNS, t_Int},
141 {(caddr_t *) &wrap_p, "lineWrap","Boolean", DEF_COLUMNS, t_Bool},
142 {(caddr_t *) &alignment_str, "alignment","Alignment",DEF_ALIGN, t_String},
145 ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
149 /* Tabs are bad, mmmkay? */
152 untabify (const char *string)
154 char *result = (char *) malloc ((strlen(string) * 8) + 1);
164 } while (col % TAB_WIDTH);
167 else if (*string == '\r' || *string == '\n')
185 (This bit mostly cribbed from phosphor.c)
188 static void drain_input (sws_configuration *sc);
191 subproc_cb (XtPointer closure, int *source, XtInputId *id)
193 sws_configuration *sc = (sws_configuration *) closure;
199 launch_text_generator (sws_configuration *sc)
201 char *oprogram = get_string_resource ("program", "Program");
202 char *program = (char *) malloc (strlen (oprogram) + 10);
204 strcpy (program, "( ");
205 strcat (program, oprogram);
206 strcat (program, " ) 2>&1");
208 if ((sc->pipe = popen (program, "r")))
211 XtAppAddInput (app, fileno (sc->pipe),
212 (XtPointer) (XtInputReadMask | XtInputExceptMask),
213 subproc_cb, (XtPointer) sc);
223 relaunch_generator_timer (XtPointer closure, XtIntervalId *id)
225 sws_configuration *sc = (sws_configuration *) closure;
226 launch_text_generator (sc);
230 /* When the subprocess has generated some output, this reads as much as it
231 can into sc->buf at sc->buf_tail.
234 drain_input (sws_configuration *sc)
236 if (sc->buf_tail < sizeof(sc->buf) - 2)
238 int target = sizeof(sc->buf) - sc->buf_tail - 2;
239 int n = read (fileno (sc->pipe),
240 (void *) (sc->buf + sc->buf_tail),
245 sc->buf[sc->buf_tail] = 0;
249 XtRemoveInput (sc->pipe_id);
254 /* If the process didn't print a terminating newline, add one. */
255 if (sc->buf_tail > 1 &&
256 sc->buf[sc->buf_tail-1] != '\n')
258 sc->buf[sc->buf_tail++] = '\n';
259 sc->buf[sc->buf_tail] = 0;
262 /* Then add one more, just for giggles. */
263 sc->buf[sc->buf_tail++] = '\n';
264 sc->buf[sc->buf_tail] = 0;
266 /* Set up a timer to re-launch the subproc in a bit. */
267 XtAppAddTimeOut (app, sc->subproc_relaunch_delay,
268 relaunch_generator_timer,
275 /* Populates the sc->lines list with as many lines as are currently in
276 sc->buf (which was filled by drain_input().
279 get_more_lines (sws_configuration *sc)
283 while (sc->total_lines < max_lines)
285 if (s >= sc->buf + sc->buf_tail)
287 /* Reached end of buffer before end of line. Bail. */
291 if (*s == '\n' || col > sc->columns)
299 /* We wrapped -- try to back up to the previous word boundary. */
302 while (s2 > sc->buf && *s2 != ' ' && *s2 != '\t')
312 sc->lines[sc->total_lines] = (char *) malloc (L+1);
313 memcpy (sc->lines[sc->total_lines], sc->buf, L);
314 sc->lines[sc->total_lines][L] = 0;
317 char *t = sc->lines[sc->total_lines];
318 char *ut = untabify (t);
319 sc->lines[sc->total_lines] = ut;
325 if (sc->buf_tail > (s - sc->buf))
327 int i = sc->buf_tail - (s - sc->buf);
328 memcpy (sc->buf, s, i);
330 sc->buf[sc->buf_tail] = 0;
337 sc->buf[sc->buf_tail] = 0;
345 col = TAB_WIDTH * ((col / TAB_WIDTH) + 1);
353 draw_string (int x, int y, const char *s)
355 if (!s || !*s) return;
357 glTranslatef (x, y, 0);
359 glutStrokeCharacter (GLUT_FONT, *s++);
366 grid (double width, double height, double spacing, double z)
369 for (y = 0; y <= height/2; y += spacing)
371 glBegin(GL_LINE_LOOP);
372 glVertex3f(-width/2, y, z);
373 glVertex3f( width/2, y, z);
375 glBegin(GL_LINE_LOOP);
376 glVertex3f(-width/2, -y, z);
377 glVertex3f( width/2, -y, z);
380 for (x = 0; x <= width/2; x += spacing)
382 glBegin(GL_LINE_LOOP);
383 glVertex3f( x, -height/2, z);
384 glVertex3f( x, height/2, z);
386 glBegin(GL_LINE_LOOP);
387 glVertex3f(-x, -height/2, z);
388 glVertex3f(-x, height/2, z);
392 glBegin(GL_LINE_LOOP);
393 glVertex3f(-width, 0, z);
394 glVertex3f( width, 0, z);
396 glBegin(GL_LINE_LOOP);
397 glVertex3f(0, -height, z);
398 glVertex3f(0, height, z);
403 box (double width, double height, double depth)
405 glBegin(GL_LINE_LOOP);
406 glVertex3f(-width/2, -height/2, -depth/2);
407 glVertex3f(-width/2, height/2, -depth/2);
408 glVertex3f( width/2, height/2, -depth/2);
409 glVertex3f( width/2, -height/2, -depth/2);
411 glBegin(GL_LINE_LOOP);
412 glVertex3f(-width/2, -height/2, depth/2);
413 glVertex3f(-width/2, height/2, depth/2);
414 glVertex3f( width/2, height/2, depth/2);
415 glVertex3f( width/2, -height/2, depth/2);
417 glBegin(GL_LINE_LOOP);
418 glVertex3f(-width/2, -height/2, -depth/2);
419 glVertex3f(-width/2, -height/2, depth/2);
420 glVertex3f(-width/2, height/2, depth/2);
421 glVertex3f(-width/2, height/2, -depth/2);
423 glBegin(GL_LINE_LOOP);
424 glVertex3f( width/2, -height/2, -depth/2);
425 glVertex3f( width/2, -height/2, depth/2);
426 glVertex3f( width/2, height/2, depth/2);
427 glVertex3f( width/2, height/2, -depth/2);
431 glBegin(GL_LINE_LOOP);
432 glVertex3f(-width/2, height/2, depth/2);
433 glVertex3f(-width/2, -height/2, -depth/2);
435 glBegin(GL_LINE_LOOP);
436 glVertex3f( width/2, height/2, depth/2);
437 glVertex3f( width/2, -height/2, -depth/2);
439 glBegin(GL_LINE_LOOP);
440 glVertex3f(-width/2, -height/2, depth/2);
441 glVertex3f(-width/2, height/2, -depth/2);
443 glBegin(GL_LINE_LOOP);
444 glVertex3f( width/2, -height/2, depth/2);
445 glVertex3f( width/2, height/2, -depth/2);
451 /* Window management, etc
454 reshape_sws (ModeInfo *mi, int width, int height)
456 sws_configuration *sc = &scs[MI_SCREEN(mi)];
457 static Bool stars_done = False;
459 glViewport (0, 0, (GLint) width, (GLint) height);
463 int nstars = width * height / 320;
464 glDeleteLists (sc->star_list, 1);
465 sc->star_list = glGenLists (1);
466 glNewList (sc->star_list, GL_COMPILE);
468 for (i = 0; i < nstars; i++)
470 GLfloat c = 0.6 + 0.3 * random() / RAND_MAX;
472 glVertex3f (2 * width * (0.5 - 1.0 * random() / RAND_MAX),
473 2 * height * (0.5 - 1.0 * random() / RAND_MAX),
484 gl_init (ModeInfo *mi)
486 sws_configuration *sc = &scs[MI_SCREEN(mi)];
488 program = get_string_resource ("program", "Program");
490 glMatrixMode (GL_MODELVIEW);
492 glDisable (GL_LIGHTING);
493 glDisable (GL_DEPTH_TEST);
495 sc->text_list = glGenLists (1);
496 glNewList (sc->text_list, GL_COMPILE);
499 sc->star_list = glGenLists (1);
500 glNewList (sc->star_list, GL_COMPILE);
506 init_sws (ModeInfo *mi)
510 sws_configuration *sc;
513 scs = (sws_configuration *)
514 calloc (MI_NUM_SCREENS(mi), sizeof (sws_configuration));
516 fprintf(stderr, "%s: out of memory\n", progname);
520 sc = &scs[MI_SCREEN(mi)];
521 sc->lines = (char **) calloc (max_lines+1, sizeof(char *));
524 sc = &scs[MI_SCREEN(mi)];
526 if ((sc->glx_context = init_GL(mi)) != NULL) {
528 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
533 font_height = GLUT_FONT->top - GLUT_FONT->bottom;
534 sc->font_scale = 1.0 / glutStrokeWidth (GLUT_FONT, 'z'); /* 'n' seems
536 if (target_columns > 0)
538 sc->columns = target_columns;
543 font_size = BASE_FONT_SIZE;
544 sc->columns = BASE_FONT_COLUMNS * ((double) BASE_FONT_SIZE / font_size);
547 sc->font_scale /= sc->columns;
548 sc->line_height = font_height * sc->font_scale;
551 if (!wrap_p) sc->columns = 1000; /* wrap anyway, if it's absurdly long. */
553 sc->subproc_relaunch_delay = 2 * 1000;
554 sc->total_lines = max_lines-1;
555 launch_text_generator (sc);
558 star_spin = -star_spin;
560 if (!alignment_str || !*alignment_str ||
561 !strcasecmp(alignment_str, "left"))
563 else if (!strcasecmp(alignment_str, "center") ||
564 !strcasecmp(alignment_str, "middle"))
566 else if (!strcasecmp(alignment_str, "right"))
571 "%s: alignment must be left, center, or right, not \"%s\"\n",
572 progname, alignment_str);
579 draw_sws (ModeInfo *mi)
581 sws_configuration *sc = &scs[MI_SCREEN(mi)];
582 Display *dpy = MI_DISPLAY(mi);
583 Window window = MI_WINDOW(mi);
586 if (!sc->glx_context)
589 if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
590 XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
592 glDrawBuffer (GL_BACK);
593 glXMakeCurrent (dpy, window, *(sc->glx_context));
595 glClear (GL_COLOR_BUFFER_BIT);
599 glMatrixMode (GL_PROJECTION);
601 glOrtho (-0.5 * MI_WIDTH(mi), 0.5 * MI_WIDTH(mi),
602 -0.5 * MI_HEIGHT(mi), 0.5 * MI_HEIGHT(mi),
604 glRotatef (sc->star_theta, 0.0, 0.0, 1.0);
605 glCallList (sc->star_list);
608 gluPerspective (80.0, 4.0/3.0, 10, 500000);
609 glMatrixMode (GL_MODELVIEW);
610 gluLookAt (0.0, 0.0, 4600.0,
614 glRotatef (-60.0, 1.0, 0.0, 0.0);
616 /* The above gives us an arena where the bottom edge of the screen is
617 represented by the line (-2100,-3140,0) - ( 2100,-3140,0). */
619 /* Now let's move the origin to the front of the screen. */
620 glTranslatef (0.0, -3140, 0.0);
622 /* And then let's scale so that the bottom of the screen is 1.0 wide. */
623 glScalef (4200, 4200, 4200);
625 /* Scroll to current position */
626 glTranslatef (0.0, sc->intra_line_scroll, 0.0);
628 glColor3f (1.0, 1.0, 0.4);
629 glCallList (sc->text_list);
631 sc->intra_line_scroll += sc->line_height / scroll_steps;
633 if (sc->intra_line_scroll >= sc->line_height)
635 sc->intra_line_scroll = 0;
637 /* Drop the oldest line off the end. */
641 /* Scroll the contents of the lines array toward 0. */
642 if (sc->total_lines > 0)
644 for (i = 1; i < sc->total_lines; i++)
645 sc->lines[i-1] = sc->lines[i];
646 sc->lines[--sc->total_lines] = 0;
649 /* Bring in new lines at the end. */
652 if (sc->total_lines < max_lines)
653 /* Oops, we ran out of text... well, insert some blank lines
654 here so that new text still pulls in from the bottom of
655 the screen, isntead of just appearing. */
656 sc->total_lines = max_lines;
658 glDeleteLists (sc->text_list, 1);
659 sc->text_list = glGenLists (1);
660 glNewList (sc->text_list, GL_COMPILE);
662 glScalef (sc->font_scale, sc->font_scale, sc->font_scale);
663 for (i = 0; i < sc->total_lines; i++)
665 int offscreen_lines = 3;
668 double y = ((sc->total_lines - (i + offscreen_lines) - 1)
671 char *line = sc->lines[i];
674 sprintf(n, "%d:", i);
675 draw_string (x / sc->font_scale, y / sc->font_scale, n);
681 xoff = 1.0 - (glutStrokeLength(GLUT_FONT, line) * sc->font_scale);
685 draw_string ((x + xoff) / sc->font_scale, y / sc->font_scale, line);
693 if (mi->fps_p) do_fps (mi);
695 glXSwapBuffers(dpy, window);
697 sc->star_theta += star_spin;