2 * starwars, Copyright (c) 1998-2001, 2004 Jamie Zawinski <jwz@jwz.org> and
3 * Claudio Matsuoka <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.
32 * 20010224 jepler@mail.inetnebr.com made the lines be anti-aliased,
33 * made the text fade to black at the end.
36 #include <X11/Intrinsic.h>
38 extern XtAppContext app;
40 #define PROGCLASS "StarWars"
41 #define HACK_INIT init_sws
42 #define HACK_DRAW draw_sws
43 #define HACK_RESHAPE reshape_sws
44 #define sws_opts xlockmore_opts
46 #define DEF_PROGRAM "(default)"
47 #define DEF_LINES "125"
48 #define DEF_STEPS "35"
49 #define DEF_SPIN "0.03"
50 #define DEF_FONT_SIZE "-1"
51 #define DEF_COLUMNS "-1"
52 #define DEF_WRAP "True"
53 #define DEF_ALIGN "Center"
54 #define DEF_SMOOTH "True"
55 #define DEF_THICK "True"
56 #define DEF_FADE "True"
60 #define BASE_FONT_SIZE 18 /* magic */
61 #define BASE_FONT_COLUMNS 80 /* magic */
63 #define MAX_THICK_LINES 25
64 #define FONT_WEIGHT 14
68 #define DEFAULTS "*delay: 40000 \n" \
69 "*showFPS: False \n" \
71 "*program: " DEF_PROGRAM "\n" \
72 "*lines: " DEF_LINES "\n" \
73 "*spin: " DEF_SPIN "\n" \
74 "*steps: " DEF_STEPS "\n" \
75 "*smooth: " DEF_SMOOTH "\n" \
76 "*thick: " DEF_THICK "\n" \
77 "*fade: " DEF_FADE "\n" \
78 "*starwars.fontSize: " DEF_FONT_SIZE "\n" \
79 "*starwars.columns: " DEF_COLUMNS "\n" \
80 "*starwars.lineWrap: " DEF_WRAP "\n" \
81 "*starwars.alignment:" DEF_ALIGN "\n"
84 #define countof(x) (sizeof((x))/sizeof((*x)))
86 #include "xlockmore.h"
88 #ifdef USE_GL /* whole file */
93 #include "glutstroke.h"
94 #include "glut_roman.h"
95 #define GLUT_FONT (&glutStrokeRoman)
98 # include <sys/utsname.h>
99 #endif /* HAVE_UNAME */
103 GLXContext *glx_context;
105 GLuint text_list, star_list;
109 Time subproc_relaunch_delay;
120 double intra_line_scroll;
122 int line_pixel_height;
123 GLfloat line_thickness;
128 static sws_configuration *scs = NULL;
130 static char *program;
131 static int max_lines;
132 static int scroll_steps;
133 static float star_spin;
134 static float font_size;
135 static int target_columns;
140 static char *alignment_str;
141 static int alignment;
143 static XrmOptionDescRec opts[] = {
144 {"-program", ".starwars.program", XrmoptionSepArg, 0 },
145 {"-lines", ".starwars.lines", XrmoptionSepArg, 0 },
146 {"-steps", ".starwars.steps", XrmoptionSepArg, 0 },
147 {"-spin", ".starwars.spin", XrmoptionSepArg, 0 },
148 {"-size", ".starwars.fontSize", XrmoptionSepArg, 0 },
149 {"-columns", ".starwars.columns", XrmoptionSepArg, 0 },
150 {"-smooth", ".starwars.smooth", XrmoptionNoArg, "True" },
151 {"-no-smooth", ".starwars.smooth", XrmoptionNoArg, "False" },
152 {"-thick", ".starwars.thick", XrmoptionNoArg, "True" },
153 {"-no-thick", ".starwars.thick", XrmoptionNoArg, "False" },
154 {"-fade", ".starwars.fade", XrmoptionNoArg, "True" },
155 {"-no-fade", ".starwars.fade", XrmoptionNoArg, "False" },
156 {"-wrap", ".starwars.lineWrap", XrmoptionNoArg, "True" },
157 {"-no-wrap", ".starwars.lineWrap", XrmoptionNoArg, "False" },
158 {"-nowrap", ".starwars.lineWrap", XrmoptionNoArg, "False" },
159 {"-left", ".starwars.alignment",XrmoptionNoArg, "Left" },
160 {"-right", ".starwars.alignment",XrmoptionNoArg, "Right" },
161 {"-center", ".starwars.alignment",XrmoptionNoArg, "Center" },
164 static argtype vars[] = {
165 {&program, "program", "Program", DEF_PROGRAM, t_String},
166 {&max_lines, "lines", "Integer", DEF_LINES, t_Int},
167 {&scroll_steps, "steps", "Integer", DEF_STEPS, t_Int},
168 {&star_spin, "spin", "Float", DEF_SPIN, t_Float},
169 {&font_size, "fontSize", "Float", DEF_STEPS, t_Float},
170 {&target_columns, "columns", "Integer", DEF_COLUMNS, t_Int},
171 {&wrap_p, "lineWrap", "Boolean", DEF_COLUMNS, t_Bool},
172 {&alignment_str, "alignment", "Alignment", DEF_ALIGN, t_String},
173 {&smooth_p, "smooth", "Boolean", DEF_SMOOTH, t_Bool},
174 {&thick_p, "thick", "Boolean", DEF_THICK, t_Bool},
175 {&fade_p, "fade", "Boolean", DEF_FADE, t_Bool},
178 ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
182 /* Tabs are bad, mmmkay? */
185 untabify (const char *string)
187 const char *ostring = string;
188 char *result = (char *) malloc ((strlen(string) * 8) + 1);
198 } while (col % TAB_WIDTH);
201 else if (*string == '\r' || *string == '\n')
206 else if (*string == '\010') /* backspace */
208 if (string > ostring)
223 strip (char *s, Bool leading, Bool trailing)
227 while (L > 0 && (s[L-1] == ' ' || s[L-1] == '\t'))
232 while (*s2 == ' ' || *s2 == '\t')
243 /* The GLUT font only has ASCII characters in them, so do what we can to
244 convert Latin1 characters to the nearest ASCII equivalent...
247 latin1_to_ascii (char *s)
249 unsigned char *us = (unsigned char *) s;
250 const unsigned char ascii[95] = {
251 '!', 'C', '#', '#', 'Y', '|', 'S', '_', 'C', '?', '<', '=', '-', 'R', '_',
252 '?', '?', '2', '3', '\'','u', 'P', '.', ',', '1', 'o', '>', '?', '?', '?',
253 '?', 'A', 'A', 'A', 'A', 'A', 'A', 'E', 'C', 'E', 'E', 'E', 'E', 'I', 'I',
254 'I', 'I', 'D', 'N', 'O', 'O', 'O', 'O', 'O', 'x', '0', 'U', 'U', 'U', 'U',
255 'Y', 'p', 'S', 'a', 'a', 'a', 'a', 'a', 'a', 'e', 'c', 'e', 'e', 'e', 'e',
256 'i', 'i', 'i', 'i', 'o', 'n', 'o', 'o', 'o', 'o', 'o', '/', 'o', 'u', 'u',
257 'u', 'u', 'y', 'p', 'y' };
261 *us = ascii[*us - 161];
270 (This bit mostly cribbed from phosphor.c)
273 static void drain_input (sws_configuration *sc);
276 subproc_cb (XtPointer closure, int *source, XtInputId *id)
278 sws_configuration *sc = (sws_configuration *) closure;
284 launch_text_generator (sws_configuration *sc)
286 char *oprogram = get_string_resource ("program", "Program");
289 if (!strcasecmp(oprogram, "(default)"))
291 oprogram = FORTUNE_PROGRAM;
293 #if defined(__linux__) && defined(HAVE_UNAME)
295 static int done_once = 0;
301 if (uname (&uts) == 0)
303 static char cmd[200];
305 /* strip version at the first non-digit-dash-dot, to
306 lose any "SMP" crap at the end. */
307 for (s = uts.release; *s; s++)
308 if (!isdigit(*s) && *s != '.' && *s != '-')
310 sprintf (cmd, "cat /usr/src/linux-%s/README", uts.release);
311 if (!stat (cmd+4, &st))
315 /* kernel source not installed? try X... */
316 strcpy (cmd, "cat /usr/X11R6/lib/X11/doc/README");
317 if (!stat (cmd+4, &st))
323 #endif /* __linux__ && HAVE_UNAME */
325 #ifdef __APPLE__ /* MacOS X + XDarwin */
327 static int done_once = 0;
331 static char *cmd = "cat /usr/X11R6/README";
332 if (!stat (cmd+4, &st))
336 #endif /* __APPLE__ */
339 program = (char *) malloc (strlen (oprogram) + 10);
340 strcpy (program, "( ");
341 strcat (program, oprogram);
342 strcat (program, " ) 2>&1");
344 if ((sc->pipe = popen (program, "r")))
347 XtAppAddInput (app, fileno (sc->pipe),
348 (XtPointer) (XtInputReadMask | XtInputExceptMask),
349 subproc_cb, (XtPointer) sc);
359 relaunch_generator_timer (XtPointer closure, XtIntervalId *id)
361 sws_configuration *sc = (sws_configuration *) closure;
362 launch_text_generator (sc);
366 /* When the subprocess has generated some output, this reads as much as it
367 can into sc->buf at sc->buf_tail.
370 drain_input (sws_configuration *sc)
372 if (sc->buf_tail < sizeof(sc->buf) - 2)
374 int target = sizeof(sc->buf) - sc->buf_tail - 2;
375 int n = read (fileno (sc->pipe),
376 (void *) (sc->buf + sc->buf_tail),
381 sc->buf[sc->buf_tail] = 0;
385 XtRemoveInput (sc->pipe_id);
390 /* If the process didn't print a terminating newline, add one. */
391 if (sc->buf_tail > 1 &&
392 sc->buf[sc->buf_tail-1] != '\n')
394 sc->buf[sc->buf_tail++] = '\n';
395 sc->buf[sc->buf_tail] = 0;
398 /* Then add one more, just for giggles. */
399 sc->buf[sc->buf_tail++] = '\n';
400 sc->buf[sc->buf_tail] = 0;
402 /* Set up a timer to re-launch the subproc in a bit. */
403 XtAppAddTimeOut (app, sc->subproc_relaunch_delay,
404 relaunch_generator_timer,
411 /* Populates the sc->lines list with as many lines as are currently in
412 sc->buf (which was filled by drain_input().
415 get_more_lines (sws_configuration *sc)
419 while (sc->total_lines < max_lines)
421 if (s >= sc->buf + sc->buf_tail)
423 /* Reached end of buffer before end of line. Bail. */
427 if (*s == '\r' || *s == '\n' || col > sc->columns)
431 if (*s == '\r' || *s == '\n')
433 if (*s == '\r' && s[1] == '\n') /* swallow CRLF too */
440 /* We wrapped -- try to back up to the previous word boundary. */
443 while (s2 > sc->buf && *s2 != ' ' && *s2 != '\t')
453 sc->lines[sc->total_lines] = (char *) malloc (L+1);
454 memcpy (sc->lines[sc->total_lines], sc->buf, L);
455 sc->lines[sc->total_lines][L] = 0;
456 latin1_to_ascii (sc->lines[sc->total_lines]);
459 char *t = sc->lines[sc->total_lines];
460 char *ut = untabify (t);
461 strip (ut, (alignment == 0), 1); /* if centering, strip
462 leading whitespace too */
463 sc->lines[sc->total_lines] = ut;
469 if (sc->buf_tail > (s - sc->buf))
471 int i = sc->buf_tail - (s - sc->buf);
472 memmove (sc->buf, s, i);
474 sc->buf[sc->buf_tail] = 0;
481 sc->buf[sc->buf_tail] = 0;
489 col = TAB_WIDTH * ((col / TAB_WIDTH) + 1);
497 draw_string (int x, int y, const char *s)
499 if (!s || !*s) return;
501 glTranslatef (x, y, 0);
504 glutStrokeCharacter (GLUT_FONT, *s++);
511 grid (double width, double height, double spacing, double z)
514 for (y = 0; y <= height/2; y += spacing)
517 glVertex3f(-width/2, y, z);
518 glVertex3f( width/2, y, z);
519 glVertex3f(-width/2, -y, z);
520 glVertex3f( width/2, -y, z);
523 for (x = 0; x <= width/2; x += spacing)
526 glVertex3f( x, -height/2, z);
527 glVertex3f( x, height/2, z);
528 glVertex3f(-x, -height/2, z);
529 glVertex3f(-x, height/2, z);
534 glVertex3f(-width, 0, z);
535 glVertex3f( width, 0, z);
536 glVertex3f(0, -height, z);
537 glVertex3f(0, height, z);
542 box (double width, double height, double depth)
544 glBegin(GL_LINE_LOOP);
545 glVertex3f(-width/2, -height/2, -depth/2);
546 glVertex3f(-width/2, height/2, -depth/2);
547 glVertex3f( width/2, height/2, -depth/2);
548 glVertex3f( width/2, -height/2, -depth/2);
550 glBegin(GL_LINE_LOOP);
551 glVertex3f(-width/2, -height/2, depth/2);
552 glVertex3f(-width/2, height/2, depth/2);
553 glVertex3f( width/2, height/2, depth/2);
554 glVertex3f( width/2, -height/2, depth/2);
556 glBegin(GL_LINE_LOOP);
557 glVertex3f(-width/2, -height/2, -depth/2);
558 glVertex3f(-width/2, -height/2, depth/2);
559 glVertex3f(-width/2, height/2, depth/2);
560 glVertex3f(-width/2, height/2, -depth/2);
562 glBegin(GL_LINE_LOOP);
563 glVertex3f( width/2, -height/2, -depth/2);
564 glVertex3f( width/2, -height/2, depth/2);
565 glVertex3f( width/2, height/2, depth/2);
566 glVertex3f( width/2, height/2, -depth/2);
571 glVertex3f(-width/2, height/2, depth/2);
572 glVertex3f(-width/2, -height/2, -depth/2);
574 glVertex3f( width/2, height/2, depth/2);
575 glVertex3f( width/2, -height/2, -depth/2);
577 glVertex3f(-width/2, -height/2, depth/2);
578 glVertex3f(-width/2, height/2, -depth/2);
580 glVertex3f( width/2, -height/2, depth/2);
581 glVertex3f( width/2, height/2, -depth/2);
587 /* Construct stars (number of stars is dependent on size of screen) */
589 init_stars (ModeInfo *mi, int width, int height)
591 sws_configuration *sc = &scs[MI_SCREEN(mi)];
593 int nstars = width * height / 320;
596 int steps = max_size / inc;
598 glDeleteLists (sc->star_list, 1);
599 sc->star_list = glGenLists (1);
600 glNewList (sc->star_list, GL_COMPILE);
602 glEnable(GL_POINT_SMOOTH);
604 for (j = 1; j <= steps; j++)
606 glPointSize(inc * j);
608 for (i = 0; i < nstars / steps; i++)
610 glColor3f (0.6 + frand(0.3),
613 glVertex2f (2 * width * (0.5 - frand(1.0)),
614 2 * height * (0.5 - frand(1.0)));
622 /* Window management, etc
625 reshape_sws (ModeInfo *mi, int width, int height)
627 sws_configuration *sc = &scs[MI_SCREEN(mi)];
629 /* Set up matrices for perspective text display
632 GLfloat desired_aspect = (GLfloat) 3/4;
633 int w = mi->xgwa.width;
634 int h = mi->xgwa.height;
637 h = w * desired_aspect;
640 glMatrixMode (GL_PROJECTION);
641 glViewport (0, 0, w, h);
643 glMatrixMode (GL_MODELVIEW);
645 gluPerspective (80.0, 1/desired_aspect, 10, 500000);
646 gluLookAt (0.0, 0.0, 4600.0,
649 glRotatef (-60.0, 1.0, 0.0, 0.0);
651 /* The above gives us an arena where the bottom edge of the screen is
652 represented by the line (-2100,-3140,0) - ( 2100,-3140,0). */
654 /* Now let's move the origin to the front of the screen. */
655 glTranslatef (0.0, -3140, 0.0);
657 /* And then let's scale so that the bottom of the screen is 1.0 wide. */
658 glScalef (4200, 4200, 4200);
662 /* Compute the height in pixels of the line at the bottom of the screen. */
664 GLdouble mm[17], pm[17];
666 GLfloat x = 0.5, y1 = 0, z = 0;
667 GLfloat y2 = sc->line_height;
668 GLdouble wx=-1, wy1=-1, wy2=-1, wz=-1;
670 glGetDoublev (GL_MODELVIEW_MATRIX, mm);
671 glGetDoublev (GL_PROJECTION_MATRIX, pm);
672 glGetIntegerv (GL_VIEWPORT, vp);
673 gluProject (x, y1, z, mm, pm, vp, &wx, &wy1, &wz);
674 gluProject (x, y2, z, mm, pm, vp, &wx, &wy2, &wz);
675 sc->line_pixel_height = (wy2 - wy1);
679 /* Compute the best looking line thickness for the bottom line.
682 sc->line_thickness = 1.0;
684 sc->line_thickness = (GLfloat) sc->line_pixel_height / FONT_WEIGHT;
686 if (sc->line_thickness < 1.2)
687 sc->line_thickness = 1.0;
692 gl_init (ModeInfo *mi)
694 sws_configuration *sc = &scs[MI_SCREEN(mi)];
696 program = get_string_resource ("program", "Program");
698 glDisable (GL_LIGHTING);
699 glDisable (GL_DEPTH_TEST);
703 glEnable (GL_LINE_SMOOTH);
704 glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
705 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
709 sc->text_list = glGenLists (1);
710 glNewList (sc->text_list, GL_COMPILE);
713 sc->star_list = glGenLists (1);
714 glNewList (sc->star_list, GL_COMPILE);
717 sc->line_thickness = 1.0;
722 init_sws (ModeInfo *mi)
726 sws_configuration *sc;
729 scs = (sws_configuration *)
730 calloc (MI_NUM_SCREENS(mi), sizeof (sws_configuration));
732 fprintf(stderr, "%s: out of memory\n", progname);
736 sc = &scs[MI_SCREEN(mi)];
737 sc->lines = (char **) calloc (max_lines+1, sizeof(char *));
740 sc = &scs[MI_SCREEN(mi)];
742 if ((sc->glx_context = init_GL(mi)) != NULL) {
744 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
745 init_stars (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
749 font_height = GLUT_FONT->top - GLUT_FONT->bottom;
750 sc->font_scale = 1.0 / glutStrokeWidth (GLUT_FONT, 'z'); /* 'n' seems
752 if (target_columns > 0)
754 sc->columns = target_columns;
759 font_size = BASE_FONT_SIZE;
760 sc->columns = BASE_FONT_COLUMNS * ((double) BASE_FONT_SIZE / font_size);
763 sc->font_scale /= sc->columns;
764 sc->line_height = font_height * sc->font_scale;
767 if (!wrap_p) sc->columns = 1000; /* wrap anyway, if it's absurdly long. */
769 sc->subproc_relaunch_delay = 2 * 1000;
770 sc->total_lines = max_lines-1;
773 star_spin = -star_spin;
775 if (!alignment_str || !*alignment_str ||
776 !strcasecmp(alignment_str, "left"))
778 else if (!strcasecmp(alignment_str, "center") ||
779 !strcasecmp(alignment_str, "middle"))
781 else if (!strcasecmp(alignment_str, "right"))
786 "%s: alignment must be left, center, or right, not \"%s\"\n",
787 progname, alignment_str);
791 launch_text_generator (sc);
793 /* one more reshape, after line_height has been computed */
794 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
799 draw_stars (ModeInfo *mi)
801 sws_configuration *sc = &scs[MI_SCREEN(mi)];
803 glMatrixMode (GL_PROJECTION);
808 glMatrixMode (GL_MODELVIEW);
812 glOrtho (-0.5 * MI_WIDTH(mi), 0.5 * MI_WIDTH(mi),
813 -0.5 * MI_HEIGHT(mi), 0.5 * MI_HEIGHT(mi),
815 glRotatef (sc->star_theta, 0.0, 0.0, 1.0);
816 glCallList (sc->star_list);
820 glMatrixMode (GL_PROJECTION);
825 draw_sws (ModeInfo *mi)
827 sws_configuration *sc = &scs[MI_SCREEN(mi)];
828 Display *dpy = MI_DISPLAY(mi);
829 Window window = MI_WINDOW(mi);
832 if (!sc->glx_context)
835 if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
836 XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
838 glDrawBuffer (GL_BACK);
839 glXMakeCurrent (dpy, window, *(sc->glx_context));
841 glClear (GL_COLOR_BUFFER_BIT);
845 glMatrixMode (GL_MODELVIEW);
849 glColor3f (0.4, 0.4, 0.4);
851 glTranslatef(0, 1, 0);
853 glTranslatef(0, -1, 0);
855 grid (1, 1, sc->line_height, 0);
858 /* Scroll to current position */
859 glTranslatef (0.0, sc->intra_line_scroll, 0.0);
861 glColor3f (1.0, 1.0, 0.4);
862 glCallList (sc->text_list);
864 sc->intra_line_scroll += sc->line_height / scroll_steps;
866 if (sc->intra_line_scroll >= sc->line_height)
868 sc->intra_line_scroll = 0;
870 /* Drop the oldest line off the end. */
874 /* Scroll the contents of the lines array toward 0. */
875 if (sc->total_lines > 0)
877 for (i = 1; i < sc->total_lines; i++)
878 sc->lines[i-1] = sc->lines[i];
879 sc->lines[--sc->total_lines] = 0;
882 /* Bring in new lines at the end. */
885 if (sc->total_lines < max_lines)
886 /* Oops, we ran out of text... well, insert some blank lines
887 here so that new text still pulls in from the bottom of
888 the screen, isntead of just appearing. */
889 sc->total_lines = max_lines;
891 glDeleteLists (sc->text_list, 1);
892 sc->text_list = glGenLists (1);
893 glNewList (sc->text_list, GL_COMPILE);
895 glScalef (sc->font_scale, sc->font_scale, sc->font_scale);
896 for (i = 0; i < sc->total_lines; i++)
898 int offscreen_lines = 3;
901 double y = ((sc->total_lines - (i + offscreen_lines) - 1)
904 char *line = sc->lines[i];
907 sprintf(n, "%d:", i);
908 draw_string (x / sc->font_scale, y / sc->font_scale, n);
913 if (sc->line_thickness != 1)
915 int max_thick_lines = MAX_THICK_LINES;
916 GLfloat thinnest_line = 1.0;
917 GLfloat thickest_line = sc->line_thickness;
918 GLfloat range = thickest_line - thinnest_line;
921 int j = sc->total_lines - i - 1;
923 if (j > max_thick_lines)
924 thickness = thinnest_line;
926 thickness = (thinnest_line +
927 (range * ((max_thick_lines - j) /
928 (GLfloat) max_thick_lines)));
930 glLineWidth (thickness);
934 xoff = 1.0 - (glutStrokeLength(GLUT_FONT,
935 (unsigned char *) line)
942 double factor = 1.0 * i / sc->total_lines;
943 glColor3f (factor, factor, 0.5 * factor);
946 draw_string ((x + xoff) / sc->font_scale, y / sc->font_scale, line);
954 if (mi->fps_p) do_fps (mi);
956 glXSwapBuffers(dpy, window);
958 sc->star_theta += star_spin;