2 * starwars, Copyright (c) 1998-2005 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,
16 * Feb 2000 Claudio Matsuoka First version.
17 * Jan 2001 Jamie Zawinski Rewrote large sections to add the ability to
18 * run a subprocess, customization of the font
19 * size and other parameters, etc.
20 * Feb 2001 jepler@inetnebr.com Added anti-aliased lines, and fade-to-black.
21 * Feb 2005 Jamie Zawinski Added texture fonts.
26 * starwars -program 'cat starwars.txt' -columns 25 -no-wrap -texture
29 #include <X11/Intrinsic.h>
31 extern XtAppContext app;
33 #define PROGCLASS "StarWars"
34 #define HACK_INIT init_sws
35 #define HACK_DRAW draw_sws
36 #define HACK_RESHAPE reshape_sws
37 #define sws_opts xlockmore_opts
39 #define DEF_PROGRAM "xscreensaver-text --cols 0" /* don't wrap */
40 #define DEF_LINES "125"
41 #define DEF_STEPS "35"
42 #define DEF_SPIN "0.03"
43 #define DEF_FONT_SIZE "-1"
44 #define DEF_COLUMNS "-1"
45 #define DEF_WRAP "True"
46 #define DEF_ALIGN "Center"
47 #define DEF_SMOOTH "True"
48 #define DEF_THICK "True"
49 #define DEF_FADE "True"
50 #define DEF_TEXTURES "True"
51 #define DEF_DEBUG "False"
53 /* Utopia 800 needs 64 512x512 textures (4096x4096 bitmap).
54 Utopia 720 needs 16 512x512 textures (2048x2048 bitmap).
55 Utopia 480 needs 16 512x512 textures (2048x2048 bitmap).
56 Utopia 400 needs 4 512x512 textures (1024x1024 bitmap).
57 Utopia 180 needs 1 512x512 texture.
58 Times 240 needs 1 512x512 texture.
60 #define DEF_FONT "-*-utopia-bold-r-normal-*-*-720-*-*-*-*-iso8859-1"
64 #define MAX_THICK_LINES 25
65 #define FONT_WEIGHT 14
68 #define DEFAULTS "*delay: 40000 \n" \
69 "*showFPS: False \n" \
71 "*font: " DEF_FONT "\n" \
74 #define countof(x) (sizeof((x))/sizeof((*x)))
76 #include "xlockmore.h"
78 #ifdef USE_GL /* whole file */
84 #include "glutstroke.h"
85 #include "glut_roman.h"
86 #define GLUT_FONT (&glutStrokeRoman)
89 # include <sys/utsname.h>
90 #endif /* HAVE_UNAME */
94 GLXContext *glx_context;
96 GLuint text_list, star_list;
97 texture_font_data *texfont;
102 Time subproc_relaunch_delay;
115 double intra_line_scroll;
117 int line_pixel_width; /* in font units (for wrapping text) */
118 int line_pixel_height; /* in screen units (for computing line thickness) */
119 GLfloat line_thickness;
124 static sws_configuration *scs = NULL;
126 static char *program;
127 static int max_lines;
128 static int scroll_steps;
129 static float star_spin;
130 static float font_size;
131 static int target_columns;
136 static int textures_p;
138 static char *alignment_str;
139 static int alignment;
141 static XrmOptionDescRec opts[] = {
142 {"-program", ".program", XrmoptionSepArg, 0 },
143 {"-lines", ".lines", XrmoptionSepArg, 0 },
144 {"-steps", ".steps", XrmoptionSepArg, 0 },
145 {"-spin", ".spin", XrmoptionSepArg, 0 },
146 {"-size", ".fontSize", XrmoptionSepArg, 0 },
147 {"-columns", ".columns", XrmoptionSepArg, 0 },
148 /*{"-font", ".font", XrmoptionSepArg, 0 },*/
149 {"-fade", ".fade", XrmoptionNoArg, "True" },
150 {"-no-fade", ".fade", XrmoptionNoArg, "False" },
151 {"-textures", ".textures", XrmoptionNoArg, "True" },
152 {"-smooth", ".smooth", XrmoptionNoArg, "True" },
153 {"-no-smooth", ".smooth", XrmoptionNoArg, "False" },
154 {"-thick", ".thick", XrmoptionNoArg, "True" },
155 {"-no-thick", ".thick", XrmoptionNoArg, "False" },
156 {"-no-textures", ".textures", XrmoptionNoArg, "False" },
157 {"-wrap", ".lineWrap", XrmoptionNoArg, "True" },
158 {"-no-wrap", ".lineWrap", XrmoptionNoArg, "False" },
159 {"-nowrap", ".lineWrap", XrmoptionNoArg, "False" },
160 {"-left", ".alignment", XrmoptionNoArg, "Left" },
161 {"-right", ".alignment", XrmoptionNoArg, "Right" },
162 {"-center", ".alignment", XrmoptionNoArg, "Center" },
163 {"-debug", ".debug", XrmoptionNoArg, "True" },
166 static argtype vars[] = {
167 {&program, "program", "Program", DEF_PROGRAM, t_String},
168 {&max_lines, "lines", "Integer", DEF_LINES, t_Int},
169 {&scroll_steps, "steps", "Integer", DEF_STEPS, t_Int},
170 {&star_spin, "spin", "Float", DEF_SPIN, t_Float},
171 {&font_size, "fontSize", "Float", DEF_FONT_SIZE, t_Float},
172 {&target_columns, "columns", "Integer", DEF_COLUMNS, t_Int},
173 {&wrap_p, "lineWrap", "Boolean", DEF_WRAP, t_Bool},
174 {&alignment_str, "alignment", "Alignment", DEF_ALIGN, t_String},
175 {&smooth_p, "smooth", "Boolean", DEF_SMOOTH, t_Bool},
176 {&thick_p, "thick", "Boolean", DEF_THICK, t_Bool},
177 {&fade_p, "fade", "Boolean", DEF_FADE, t_Bool},
178 {&textures_p, "textures", "Boolean", DEF_TEXTURES, t_Bool},
179 {&debug_p, "debug", "Boolean", DEF_DEBUG, t_Bool},
182 ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
186 /* Tabs are bad, mmmkay? */
189 untabify (const char *string)
191 const char *ostring = string;
192 char *result = (char *) malloc ((strlen(string) * 8) + 1);
202 } while (col % TAB_WIDTH);
205 else if (*string == '\r' || *string == '\n')
210 else if (*string == '\010') /* backspace */
212 if (string > ostring)
227 strip (char *s, Bool leading, Bool trailing)
231 while (L > 0 && (s[L-1] == ' ' || s[L-1] == '\t'))
236 while (*s2 == ' ' || *s2 == '\t')
247 /* The GLUT font only has ASCII characters in them, so do what we can to
248 convert Latin1 characters to the nearest ASCII equivalent...
251 latin1_to_ascii (char *s)
253 unsigned char *us = (unsigned char *) s;
254 const unsigned char ascii[95] = {
255 '!', 'C', '#', '#', 'Y', '|', 'S', '_', 'C', '?', '<', '=', '-', 'R', '_',
256 '?', '?', '2', '3', '\'','u', 'P', '.', ',', '1', 'o', '>', '?', '?', '?',
257 '?', 'A', 'A', 'A', 'A', 'A', 'A', 'E', 'C', 'E', 'E', 'E', 'E', 'I', 'I',
258 'I', 'I', 'D', 'N', 'O', 'O', 'O', 'O', 'O', 'x', '0', 'U', 'U', 'U', 'U',
259 'Y', 'p', 'S', 'a', 'a', 'a', 'a', 'a', 'a', 'e', 'c', 'e', 'e', 'e', 'e',
260 'i', 'i', 'i', 'i', 'o', 'n', 'o', 'o', 'o', 'o', 'o', '/', 'o', 'u', 'u',
261 'u', 'u', 'y', 'p', 'y' };
265 *us = ascii[*us - 161];
274 (This bit mostly cribbed from phosphor.c)
277 static void drain_input (sws_configuration *sc);
280 subproc_cb (XtPointer closure, int *source, XtInputId *id)
282 sws_configuration *sc = (sws_configuration *) closure;
288 launch_text_generator (sws_configuration *sc)
290 char *oprogram = get_string_resource ("program", "Program");
291 char *program = (char *) malloc (strlen (oprogram) + 10);
292 strcpy (program, "( ");
293 strcat (program, oprogram);
294 strcat (program, " ) 2>&1");
296 if ((sc->pipe = popen (program, "r")))
299 XtAppAddInput (app, fileno (sc->pipe),
300 (XtPointer) (XtInputReadMask | XtInputExceptMask),
301 subproc_cb, (XtPointer) sc);
311 relaunch_generator_timer (XtPointer closure, XtIntervalId *id)
313 sws_configuration *sc = (sws_configuration *) closure;
314 launch_text_generator (sc);
318 /* When the subprocess has generated some output, this reads as much as it
319 can into sc->buf at sc->buf_tail.
322 drain_input (sws_configuration *sc)
324 if (sc->buf_tail < sc->buf_size - 2)
326 int target = sc->buf_size - sc->buf_tail - 2;
328 ? read (fileno (sc->pipe),
329 (void *) (sc->buf + sc->buf_tail),
335 sc->buf[sc->buf_tail] = 0;
341 XtRemoveInput (sc->pipe_id);
347 /* If the process didn't print a terminating newline, add one. */
348 if (sc->buf_tail > 1 &&
349 sc->buf[sc->buf_tail-1] != '\n')
351 sc->buf[sc->buf_tail++] = '\n';
352 sc->buf[sc->buf_tail] = 0;
355 /* Then add one more, just for giggles. */
356 sc->buf[sc->buf_tail++] = '\n';
357 sc->buf[sc->buf_tail] = 0;
359 /* Set up a timer to re-launch the subproc in a bit. */
360 XtAppAddTimeOut (app, sc->subproc_relaunch_delay,
361 relaunch_generator_timer,
369 string_width (sws_configuration *sc, const char *s)
372 return texture_string_width (sc->texfont, s, 0);
374 return glutStrokeLength (GLUT_FONT, (unsigned char *) s);
378 char_width (sws_configuration *sc, char c)
383 return string_width (sc, s);
387 /* Populates the sc->lines list with as many lines as are currently in
388 sc->buf (which was filled by drain_input().
391 get_more_lines (sws_configuration *sc)
393 /* wrap anyway, if it's absurdly long. */
394 int wrap_pix = (wrap_p ? sc->line_pixel_width : 10000);
400 while (sc->total_lines < max_lines)
404 if (s >= sc->buf + sc->buf_tail)
405 /* Reached end of buffer before end of line. Bail. */
408 cw = char_width (sc, *s);
410 if (*s == '\r' || *s == '\n' ||
411 col_pix + cw >= wrap_pix)
415 if (*s == '\r' || *s == '\n')
417 if (*s == '\r' && s[1] == '\n') /* swallow CRLF too */
424 /* We wrapped -- try to back up to the previous word boundary. */
427 while (s2 > sc->buf && *s2 != ' ' && *s2 != '\t')
437 sc->lines[sc->total_lines] = (char *) malloc (L+1);
438 memcpy (sc->lines[sc->total_lines], sc->buf, L);
439 sc->lines[sc->total_lines][L] = 0;
442 latin1_to_ascii (sc->lines[sc->total_lines]);
445 char *t = sc->lines[sc->total_lines];
446 char *ut = untabify (t);
447 strip (ut, (alignment == 0), 1); /* if centering, strip
448 leading whitespace too */
449 sc->lines[sc->total_lines] = ut;
455 if (sc->buf_tail > (s - sc->buf))
457 int i = sc->buf_tail - (s - sc->buf);
458 memmove (sc->buf, s, i);
460 sc->buf[sc->buf_tail] = 0;
467 sc->buf[sc->buf_tail] = 0;
478 int tab_pix = TAB_WIDTH * sc->char_width;
479 col = TAB_WIDTH * ((col / TAB_WIDTH) + 1);
480 col_pix = tab_pix * ((col / tab_pix) + 1);
489 draw_string (sws_configuration *sc, GLfloat x, GLfloat y, const char *s)
492 if (!s || !*s) return;
494 glTranslatef (x, y, 0);
497 print_texture_string (sc->texfont, s);
500 glutStrokeCharacter (GLUT_FONT, *s++);
506 GLfloat h = sc->line_height / sc->font_scale;
510 if (textures_p) glDisable (GL_TEXTURE_2D);
512 glColor3f (0.4, 0.4, 0.4);
514 glTranslatef (x, y, 0);
518 w = string_width (sc, c);
519 glBegin (GL_LINE_LOOP);
520 glVertex3f (0, 0, 0);
521 glVertex3f (w, 0, 0);
522 glVertex3f (w, h, 0);
523 glVertex3f (0, h, 0);
525 glTranslatef (w, 0, 0);
528 if (textures_p) glEnable (GL_TEXTURE_2D);
534 grid (double width, double height, double xspacing, double yspacing, double z)
537 for (y = 0; y <= height/2; y += yspacing)
540 glVertex3f(-width/2, y, z);
541 glVertex3f( width/2, y, z);
542 glVertex3f(-width/2, -y, z);
543 glVertex3f( width/2, -y, z);
546 for (x = 0; x <= width/2; x += xspacing)
549 glVertex3f( x, -height/2, z);
550 glVertex3f( x, height/2, z);
551 glVertex3f(-x, -height/2, z);
552 glVertex3f(-x, height/2, z);
557 glVertex3f(-width, 0, z);
558 glVertex3f( width, 0, z);
559 glVertex3f(0, -height, z);
560 glVertex3f(0, height, z);
565 box (double width, double height, double depth)
567 glBegin(GL_LINE_LOOP);
568 glVertex3f(-width/2, -height/2, -depth/2);
569 glVertex3f(-width/2, height/2, -depth/2);
570 glVertex3f( width/2, height/2, -depth/2);
571 glVertex3f( width/2, -height/2, -depth/2);
573 glBegin(GL_LINE_LOOP);
574 glVertex3f(-width/2, -height/2, depth/2);
575 glVertex3f(-width/2, height/2, depth/2);
576 glVertex3f( width/2, height/2, depth/2);
577 glVertex3f( width/2, -height/2, depth/2);
579 glBegin(GL_LINE_LOOP);
580 glVertex3f(-width/2, -height/2, -depth/2);
581 glVertex3f(-width/2, -height/2, depth/2);
582 glVertex3f(-width/2, height/2, depth/2);
583 glVertex3f(-width/2, height/2, -depth/2);
585 glBegin(GL_LINE_LOOP);
586 glVertex3f( width/2, -height/2, -depth/2);
587 glVertex3f( width/2, -height/2, depth/2);
588 glVertex3f( width/2, height/2, depth/2);
589 glVertex3f( width/2, height/2, -depth/2);
593 glVertex3f(-width/2, height/2, depth/2);
594 glVertex3f(-width/2, -height/2, -depth/2);
596 glVertex3f( width/2, height/2, depth/2);
597 glVertex3f( width/2, -height/2, -depth/2);
599 glVertex3f(-width/2, -height/2, depth/2);
600 glVertex3f(-width/2, height/2, -depth/2);
602 glVertex3f( width/2, -height/2, depth/2);
603 glVertex3f( width/2, height/2, -depth/2);
608 /* Construct stars (number of stars is dependent on size of screen) */
610 init_stars (ModeInfo *mi, int width, int height)
612 sws_configuration *sc = &scs[MI_SCREEN(mi)];
614 int size = (width > height ? width : height);
615 int nstars = size * size / 320;
618 int steps = max_size / inc;
620 glDeleteLists (sc->star_list, 1);
621 sc->star_list = glGenLists (1);
622 glNewList (sc->star_list, GL_COMPILE);
624 glEnable(GL_POINT_SMOOTH);
626 for (j = 1; j <= steps; j++)
628 glPointSize(inc * j);
630 for (i = 0; i < nstars / steps; i++)
632 glColor3f (0.6 + frand(0.3),
635 glVertex2f (2 * size * (0.5 - frand(1.0)),
636 2 * size * (0.5 - frand(1.0)));
644 /* Window management, etc
647 reshape_sws (ModeInfo *mi, int width, int height)
649 sws_configuration *sc = &scs[MI_SCREEN(mi)];
651 /* Set up matrices for perspective text display
654 GLfloat desired_aspect = (GLfloat) 3/4;
655 int w = mi->xgwa.width;
656 int h = mi->xgwa.height;
661 int h2 = w * desired_aspect;
662 yoff = (h - h2) / 2; /* Wide window: letterbox at top and bottom. */
663 if (yoff < 0) yoff = 0; /* Tall window: clip off the top. */
668 glMatrixMode (GL_PROJECTION);
669 glViewport (0, yoff, w, h);
671 glMatrixMode (GL_MODELVIEW);
673 gluPerspective (80.0, 1/desired_aspect, 1000, 55000);
674 gluLookAt (0.0, 0.0, 4600.0,
677 glRotatef (-60.0, 1.0, 0.0, 0.0);
680 glRotatef (60.0, 1.0, 0.0, 0.0);
681 glTranslatef (260, 3200, 0);
682 glScalef (1.85, 1.85, 1);
685 /* The above gives us an arena where the bottom edge of the screen is
686 represented by the line (-2100,-3140,0) - ( 2100,-3140,0). */
688 /* Now let's move the origin to the front of the screen. */
689 glTranslatef (0.0, -3140, 0.0);
691 /* And then let's scale so that the bottom of the screen is 1.0 wide. */
692 glScalef (4200, 4200, 4200);
696 /* Compute the height in pixels of the line at the bottom of the screen. */
698 GLdouble mm[17], pm[17];
700 GLfloat x = 0.5, y1 = 0, z = 0;
701 GLfloat y2 = sc->line_height;
702 GLdouble wx=-1, wy1=-1, wy2=-1, wz=-1;
704 glGetDoublev (GL_MODELVIEW_MATRIX, mm);
705 glGetDoublev (GL_PROJECTION_MATRIX, pm);
706 glGetIntegerv (GL_VIEWPORT, vp);
707 gluProject (x, y1, z, mm, pm, vp, &wx, &wy1, &wz);
708 gluProject (x, y2, z, mm, pm, vp, &wx, &wy2, &wz);
709 sc->line_pixel_height = (wy2 - wy1);
713 /* Compute the best looking line thickness for the bottom line.
716 sc->line_thickness = 1.0;
718 sc->line_thickness = (GLfloat) sc->line_pixel_height / FONT_WEIGHT;
720 if (sc->line_thickness < 1.2)
721 sc->line_thickness = 1.0;
726 gl_init (ModeInfo *mi)
728 sws_configuration *sc = &scs[MI_SCREEN(mi)];
730 program = get_string_resource ("program", "Program");
732 glDisable (GL_LIGHTING);
733 glDisable (GL_DEPTH_TEST);
737 glEnable (GL_LINE_SMOOTH);
738 glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
739 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
743 sc->text_list = glGenLists (1);
744 glNewList (sc->text_list, GL_COMPILE);
747 sc->star_list = glGenLists (1);
748 glNewList (sc->star_list, GL_COMPILE);
751 sc->line_thickness = 1.0;
756 init_sws (ModeInfo *mi)
760 sws_configuration *sc;
763 scs = (sws_configuration *)
764 calloc (MI_NUM_SCREENS(mi), sizeof (sws_configuration));
766 fprintf(stderr, "%s: out of memory\n", progname);
770 sc = &scs[MI_SCREEN(mi)];
771 sc->lines = (char **) calloc (max_lines+1, sizeof(char *));
774 sc = &scs[MI_SCREEN(mi)];
776 if ((sc->glx_context = init_GL(mi)) != NULL) {
778 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
779 init_stars (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
785 sc->texfont = load_texture_font (MI_DISPLAY(mi), "font");
786 cw = texture_string_width (sc->texfont, "n", &lh);
789 glEnable(GL_ALPHA_TEST);
790 glEnable (GL_TEXTURE_2D);
792 check_gl_error ("loading font");
794 # ifdef GL_TEXTURE_MAX_ANISOTROPY_EXT
795 /* "Anistropic filtering helps for quadrilateral-angled textures.
796 A sharper image is accomplished by interpolating and filtering
797 multiple samples from one or more mipmaps to better approximate
798 very distorted textures. This is the next level of filtering
799 after trilinear filtering." */
801 glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16);
807 font_height = GLUT_FONT->top - GLUT_FONT->bottom;
808 sc->char_width = glutStrokeWidth (GLUT_FONT, 'z'); /* 'n' seems wide */
811 sc->font_scale = 1.0 / sc->char_width;
814 /* We consider a font that consumes 80 columns to be "18 points".
816 If neither -size nor -columns was specified, default to 60 columns
817 (which is 24 points.)
819 If both were specified, -columns has priority.
825 if (target_columns <= 0 && font_size <= 0)
828 if (target_columns > 0)
829 font_size = base_size * (base_col / (double) target_columns);
830 else if (font_size > 0)
831 target_columns = base_col * (base_size / (double) font_size);
834 sc->line_pixel_width = target_columns * sc->char_width;
836 sc->font_scale /= target_columns;
837 sc->line_height = font_height * sc->font_scale;
840 /* Buffer only two lines of text.
841 If the buffer is too big, there's a significant delay between
842 when the program launches and when the text appears, which can be
843 irritating for time-sensitive output (clock, current music, etc.)
845 sc->buf_size = target_columns * 2;
846 if (sc->buf_size < 80) sc->buf_size = 80;
847 sc->buf = (char *) calloc (1, sc->buf_size);
849 sc->subproc_relaunch_delay = 2 * 1000; /* 2 seconds */
850 sc->total_lines = max_lines-1;
853 star_spin = -star_spin;
855 if (!alignment_str || !*alignment_str ||
856 !strcasecmp(alignment_str, "left"))
858 else if (!strcasecmp(alignment_str, "center") ||
859 !strcasecmp(alignment_str, "middle"))
861 else if (!strcasecmp(alignment_str, "right"))
866 "%s: alignment must be left, center, or right, not \"%s\"\n",
867 progname, alignment_str);
871 launch_text_generator (sc);
873 /* one more reshape, after line_height has been computed */
874 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
879 draw_stars (ModeInfo *mi)
881 sws_configuration *sc = &scs[MI_SCREEN(mi)];
883 glMatrixMode (GL_PROJECTION);
888 glMatrixMode (GL_MODELVIEW);
892 glOrtho (-0.5 * MI_WIDTH(mi), 0.5 * MI_WIDTH(mi),
893 -0.5 * MI_HEIGHT(mi), 0.5 * MI_HEIGHT(mi),
895 glRotatef (sc->star_theta, 0.0, 0.0, 1.0);
896 if (textures_p) glDisable (GL_TEXTURE_2D);
897 glCallList (sc->star_list);
898 if (textures_p) glEnable (GL_TEXTURE_2D);
902 glMatrixMode (GL_PROJECTION);
907 draw_sws (ModeInfo *mi)
909 sws_configuration *sc = &scs[MI_SCREEN(mi)];
910 Display *dpy = MI_DISPLAY(mi);
911 Window window = MI_WINDOW(mi);
914 if (!sc->glx_context)
917 if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
918 XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
920 glDrawBuffer (GL_BACK);
921 glXMakeCurrent (dpy, window, *(sc->glx_context));
923 glClear (GL_COLOR_BUFFER_BIT);
927 glMatrixMode (GL_MODELVIEW);
934 if (textures_p) glDisable (GL_TEXTURE_2D);
936 glColor3f (0.4, 0.4, 0.4);
937 glTranslatef (0,-1, 0);
938 for (i = 0; i < 16; i++)
941 grid (1, 1, sc->char_width * sc->font_scale, sc->line_height, 0);
942 glTranslatef(0, 1, 0);
944 if (textures_p) glEnable (GL_TEXTURE_2D);
948 /* Scroll to current position */
949 glTranslatef (0.0, sc->intra_line_scroll, 0.0);
951 glColor3f (1.0, 1.0, 0.4);
952 glCallList (sc->text_list);
953 mi->polygon_count = sc->polygon_count;
955 sc->intra_line_scroll += sc->line_height / scroll_steps;
957 if (sc->intra_line_scroll >= sc->line_height)
959 sc->intra_line_scroll = 0;
961 /* Drop the oldest line off the end. */
965 /* Scroll the contents of the lines array toward 0. */
966 if (sc->total_lines > 0)
968 for (i = 1; i < sc->total_lines; i++)
969 sc->lines[i-1] = sc->lines[i];
970 sc->lines[--sc->total_lines] = 0;
973 /* Bring in new lines at the end. */
976 if (sc->total_lines < max_lines)
977 /* Oops, we ran out of text... well, insert some blank lines
978 here so that new text still pulls in from the bottom of
979 the screen, isntead of just appearing. */
980 sc->total_lines = max_lines;
982 glDeleteLists (sc->text_list, 1);
983 sc->text_list = glGenLists (1);
984 glNewList (sc->text_list, GL_COMPILE);
985 sc->polygon_count = 0;
987 glScalef (sc->font_scale, sc->font_scale, sc->font_scale);
988 for (i = 0; i < sc->total_lines; i++)
990 double fade = (fade_p ? 1.0 * i / sc->total_lines : 1.0);
991 int offscreen_lines = 2;
994 double y = ((sc->total_lines - (i + offscreen_lines) - 1)
997 char *line = sc->lines[i];
1001 double xx = x * 1.4; /* a little more to the left */
1003 sprintf(n, "%d:", i);
1004 draw_string (sc, xx / sc->font_scale, y / sc->font_scale, n);
1007 if (!line || !*line)
1010 if (sc->line_thickness != 1 && !textures_p)
1012 int max_thick_lines = MAX_THICK_LINES;
1013 GLfloat thinnest_line = 1.0;
1014 GLfloat thickest_line = sc->line_thickness;
1015 GLfloat range = thickest_line - thinnest_line;
1018 int j = sc->total_lines - i - 1;
1020 if (j > max_thick_lines)
1021 thickness = thinnest_line;
1023 thickness = (thinnest_line +
1024 (range * ((max_thick_lines - j) /
1025 (GLfloat) max_thick_lines)));
1027 glLineWidth (thickness);
1032 int n = string_width (sc, line);
1033 xoff = 1.0 - (n * sc->font_scale);
1039 glColor3f (fade, fade, 0.5 * fade);
1040 draw_string (sc, (x + xoff) / sc->font_scale, y / sc->font_scale,
1043 sc->polygon_count += strlen (line);
1051 if (mi->fps_p) do_fps (mi);
1053 glXSwapBuffers(dpy, window);
1055 sc->star_theta += star_spin;