2 * starwars, Copyright (c) 1998-2006 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
31 #endif /* HAVE_CONFIG_H */
41 # include <sys/utsname.h>
42 #endif /* HAVE_UNAME */
45 # include <X11/Intrinsic.h>
49 #define DEFAULTS "*delay: 40000 \n" \
50 "*showFPS: False \n" \
52 "*font: " DEF_FONT "\n"
54 # define refresh_sws 0
55 # define sws_handle_event 0
57 #define countof(x) (sizeof((x))/sizeof((*x)))
59 #include "xlockmore.h"
61 #ifdef USE_GL /* whole file */
64 #define DEF_PROGRAM "xscreensaver-text --cols 0" /* don't wrap */
65 #define DEF_LINES "125"
66 #define DEF_STEPS "35"
67 #define DEF_SPIN "0.03"
68 #define DEF_FONT_SIZE "-1"
69 #define DEF_COLUMNS "-1"
70 #define DEF_WRAP "True"
71 #define DEF_ALIGN "Center"
72 #define DEF_SMOOTH "True"
73 #define DEF_THICK "True"
74 #define DEF_FADE "True"
75 #define DEF_TEXTURES "True"
76 #define DEF_DEBUG "False"
78 /* Utopia 800 needs 64 512x512 textures (4096x4096 bitmap).
79 Utopia 720 needs 16 512x512 textures (2048x2048 bitmap).
80 Utopia 480 needs 16 512x512 textures (2048x2048 bitmap).
81 Utopia 400 needs 4 512x512 textures (1024x1024 bitmap).
82 Utopia 180 needs 1 512x512 texture.
83 Times 240 needs 1 512x512 texture.
85 #define DEF_FONT "-*-utopia-bold-r-normal-*-*-720-*-*-*-*-iso8859-1"
89 #define MAX_THICK_LINES 25
90 #define FONT_WEIGHT 14
94 #include "glutstroke.h"
95 #include "glut_roman.h"
96 #define GLUT_FONT (&glutStrokeRoman)
100 GLXContext *glx_context;
102 GLuint text_list, star_list;
103 texture_font_data *texfont;
108 XtIntervalId pipe_timer;
109 Time subproc_relaunch_delay;
122 double intra_line_scroll;
124 int line_pixel_width; /* in font units (for wrapping text) */
125 int line_pixel_height; /* in screen units (for computing line thickness) */
126 GLfloat line_thickness;
131 static sws_configuration *scs = NULL;
133 static char *program;
134 static int max_lines;
135 static int scroll_steps;
136 static float star_spin;
137 static float font_size;
138 static int target_columns;
143 static int textures_p;
145 static char *alignment_str;
146 static int alignment;
148 static XrmOptionDescRec opts[] = {
149 {"-program", ".program", XrmoptionSepArg, 0 },
150 {"-lines", ".lines", XrmoptionSepArg, 0 },
151 {"-steps", ".steps", XrmoptionSepArg, 0 },
152 {"-spin", ".spin", XrmoptionSepArg, 0 },
153 {"-size", ".fontSize", XrmoptionSepArg, 0 },
154 {"-columns", ".columns", XrmoptionSepArg, 0 },
155 /*{"-font", ".font", XrmoptionSepArg, 0 },*/
156 {"-fade", ".fade", XrmoptionNoArg, "True" },
157 {"-no-fade", ".fade", XrmoptionNoArg, "False" },
158 {"-textures", ".textures", XrmoptionNoArg, "True" },
159 {"-smooth", ".smooth", XrmoptionNoArg, "True" },
160 {"-no-smooth", ".smooth", XrmoptionNoArg, "False" },
161 {"-thick", ".thick", XrmoptionNoArg, "True" },
162 {"-no-thick", ".thick", XrmoptionNoArg, "False" },
163 {"-no-textures", ".textures", XrmoptionNoArg, "False" },
164 {"-wrap", ".lineWrap", XrmoptionNoArg, "True" },
165 {"-no-wrap", ".lineWrap", XrmoptionNoArg, "False" },
166 {"-nowrap", ".lineWrap", XrmoptionNoArg, "False" },
167 {"-alignment", ".alignment", XrmoptionSepArg, 0 },
168 {"-left", ".alignment", XrmoptionNoArg, "Left" },
169 {"-right", ".alignment", XrmoptionNoArg, "Right" },
170 {"-center", ".alignment", XrmoptionNoArg, "Center" },
171 {"-debug", ".debug", XrmoptionNoArg, "True" },
174 static argtype vars[] = {
175 {&program, "program", "Program", DEF_PROGRAM, t_String},
176 {&max_lines, "lines", "Integer", DEF_LINES, t_Int},
177 {&scroll_steps, "steps", "Integer", DEF_STEPS, t_Int},
178 {&star_spin, "spin", "Float", DEF_SPIN, t_Float},
179 {&font_size, "fontSize", "Float", DEF_FONT_SIZE, t_Float},
180 {&target_columns, "columns", "Integer", DEF_COLUMNS, t_Int},
181 {&wrap_p, "lineWrap", "Boolean", DEF_WRAP, t_Bool},
182 {&alignment_str, "alignment", "Alignment", DEF_ALIGN, t_String},
183 {&smooth_p, "smooth", "Boolean", DEF_SMOOTH, t_Bool},
184 {&thick_p, "thick", "Boolean", DEF_THICK, t_Bool},
185 {&fade_p, "fade", "Boolean", DEF_FADE, t_Bool},
186 {&textures_p, "textures", "Boolean", DEF_TEXTURES, t_Bool},
187 {&debug_p, "debug", "Boolean", DEF_DEBUG, t_Bool},
190 ENTRYPOINT ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
194 /* Tabs are bad, mmmkay? */
197 untabify (const char *string)
199 const char *ostring = string;
200 char *result = (char *) malloc ((strlen(string) * 8) + 1);
210 } while (col % TAB_WIDTH);
213 else if (*string == '\r' || *string == '\n')
218 else if (*string == '\010') /* backspace */
220 if (string > ostring)
235 strip (char *s, Bool leading, Bool trailing)
239 while (L > 0 && (s[L-1] == ' ' || s[L-1] == '\t'))
244 while (*s2 == ' ' || *s2 == '\t')
255 /* The GLUT font only has ASCII characters in them, so do what we can to
256 convert Latin1 characters to the nearest ASCII equivalent...
259 latin1_to_ascii (char *s)
261 unsigned char *us = (unsigned char *) s;
262 const unsigned char ascii[95] = {
263 '!', 'C', '#', '#', 'Y', '|', 'S', '_', 'C', '?', '<', '=', '-', 'R', '_',
264 '?', '?', '2', '3', '\'','u', 'P', '.', ',', '1', 'o', '>', '?', '?', '?',
265 '?', 'A', 'A', 'A', 'A', 'A', 'A', 'E', 'C', 'E', 'E', 'E', 'E', 'I', 'I',
266 'I', 'I', 'D', 'N', 'O', 'O', 'O', 'O', 'O', 'x', '0', 'U', 'U', 'U', 'U',
267 'Y', 'p', 'S', 'a', 'a', 'a', 'a', 'a', 'a', 'e', 'c', 'e', 'e', 'e', 'e',
268 'i', 'i', 'i', 'i', 'o', 'n', 'o', 'o', 'o', 'o', 'o', '/', 'o', 'u', 'u',
269 'u', 'u', 'y', 'p', 'y' };
273 *us = ascii[*us - 161];
282 (This bit mostly cribbed from phosphor.c)
285 static void drain_input (sws_configuration *sc);
288 subproc_cb (XtPointer closure, int *source, XtInputId *id)
290 sws_configuration *sc = (sws_configuration *) closure;
296 launch_text_generator (sws_configuration *sc)
298 XtAppContext app = XtDisplayToApplicationContext (sc->dpy);
299 char *oprogram = get_string_resource (sc->dpy, "program", "Program");
300 char *program = (char *) malloc (strlen (oprogram) + 10);
301 strcpy (program, "( ");
302 strcat (program, oprogram);
303 strcat (program, " ) 2>&1");
305 if ((sc->pipe = popen (program, "r")))
308 XtAppAddInput (app, fileno (sc->pipe),
309 (XtPointer) (XtInputReadMask | XtInputExceptMask),
310 subproc_cb, (XtPointer) sc);
320 relaunch_generator_timer (XtPointer closure, XtIntervalId *id)
322 sws_configuration *sc = (sws_configuration *) closure;
323 if (!sc->pipe_timer) abort();
325 launch_text_generator (sc);
329 /* When the subprocess has generated some output, this reads as much as it
330 can into sc->buf at sc->buf_tail.
333 drain_input (sws_configuration *sc)
335 XtAppContext app = XtDisplayToApplicationContext (sc->dpy);
336 if (sc->buf_tail < sc->buf_size - 2)
338 int target = sc->buf_size - sc->buf_tail - 2;
340 ? read (fileno (sc->pipe),
341 (void *) (sc->buf + sc->buf_tail),
347 sc->buf[sc->buf_tail] = 0;
353 XtRemoveInput (sc->pipe_id);
359 /* If the process didn't print a terminating newline, add one. */
360 if (sc->buf_tail > 1 &&
361 sc->buf[sc->buf_tail-1] != '\n')
363 sc->buf[sc->buf_tail++] = '\n';
364 sc->buf[sc->buf_tail] = 0;
367 /* Then add one more, just for giggles. */
368 sc->buf[sc->buf_tail++] = '\n';
369 sc->buf[sc->buf_tail] = 0;
371 /* Set up a timer to re-launch the subproc in a bit. */
372 sc->pipe_timer = XtAppAddTimeOut (app, sc->subproc_relaunch_delay,
373 relaunch_generator_timer,
381 string_width (sws_configuration *sc, const char *s)
384 return texture_string_width (sc->texfont, s, 0);
386 return glutStrokeLength (GLUT_FONT, (unsigned char *) s);
390 char_width (sws_configuration *sc, char c)
395 return string_width (sc, s);
399 /* Populates the sc->lines list with as many lines as are currently in
400 sc->buf (which was filled by drain_input().
403 get_more_lines (sws_configuration *sc)
405 /* wrap anyway, if it's absurdly long. */
406 int wrap_pix = (wrap_p ? sc->line_pixel_width : 10000);
412 while (sc->total_lines < max_lines)
416 if (s >= sc->buf + sc->buf_tail)
417 /* Reached end of buffer before end of line. Bail. */
420 cw = char_width (sc, *s);
422 if (*s == '\r' || *s == '\n' ||
423 col_pix + cw >= wrap_pix)
427 if (*s == '\r' || *s == '\n')
429 if (*s == '\r' && s[1] == '\n') /* swallow CRLF too */
436 /* We wrapped -- try to back up to the previous word boundary. */
439 while (s2 > sc->buf && *s2 != ' ' && *s2 != '\t')
449 sc->lines[sc->total_lines] = (char *) malloc (L+1);
450 memcpy (sc->lines[sc->total_lines], sc->buf, L);
451 sc->lines[sc->total_lines][L] = 0;
454 latin1_to_ascii (sc->lines[sc->total_lines]);
457 char *t = sc->lines[sc->total_lines];
458 char *ut = untabify (t);
459 strip (ut, (alignment == 0), 1); /* if centering, strip
460 leading whitespace too */
461 sc->lines[sc->total_lines] = ut;
467 if (sc->buf_tail > (s - sc->buf))
469 int i = sc->buf_tail - (s - sc->buf);
470 memmove (sc->buf, s, i);
472 sc->buf[sc->buf_tail] = 0;
479 sc->buf[sc->buf_tail] = 0;
490 int tab_pix = TAB_WIDTH * sc->char_width;
491 col = TAB_WIDTH * ((col / TAB_WIDTH) + 1);
492 col_pix = tab_pix * ((col / tab_pix) + 1);
501 draw_string (sws_configuration *sc, GLfloat x, GLfloat y, const char *s)
504 if (!s || !*s) return;
506 glTranslatef (x, y, 0);
509 print_texture_string (sc->texfont, s);
512 glutStrokeCharacter (GLUT_FONT, *s++);
518 GLfloat h = sc->line_height / sc->font_scale;
522 if (textures_p) glDisable (GL_TEXTURE_2D);
524 glColor3f (0.4, 0.4, 0.4);
526 glTranslatef (x, y, 0);
530 w = string_width (sc, c);
531 glBegin (GL_LINE_LOOP);
532 glVertex3f (0, 0, 0);
533 glVertex3f (w, 0, 0);
534 glVertex3f (w, h, 0);
535 glVertex3f (0, h, 0);
537 glTranslatef (w, 0, 0);
540 if (textures_p) glEnable (GL_TEXTURE_2D);
546 grid (double width, double height, double xspacing, double yspacing, double z)
549 for (y = 0; y <= height/2; y += yspacing)
552 glVertex3f(-width/2, y, z);
553 glVertex3f( width/2, y, z);
554 glVertex3f(-width/2, -y, z);
555 glVertex3f( width/2, -y, z);
558 for (x = 0; x <= width/2; x += xspacing)
561 glVertex3f( x, -height/2, z);
562 glVertex3f( x, height/2, z);
563 glVertex3f(-x, -height/2, z);
564 glVertex3f(-x, height/2, z);
569 glVertex3f(-width, 0, z);
570 glVertex3f( width, 0, z);
571 glVertex3f(0, -height, z);
572 glVertex3f(0, height, z);
577 box (double width, double height, double depth)
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);
591 glBegin(GL_LINE_LOOP);
592 glVertex3f(-width/2, -height/2, -depth/2);
593 glVertex3f(-width/2, -height/2, depth/2);
594 glVertex3f(-width/2, height/2, depth/2);
595 glVertex3f(-width/2, height/2, -depth/2);
597 glBegin(GL_LINE_LOOP);
598 glVertex3f( width/2, -height/2, -depth/2);
599 glVertex3f( width/2, -height/2, depth/2);
600 glVertex3f( width/2, height/2, depth/2);
601 glVertex3f( width/2, height/2, -depth/2);
605 glVertex3f(-width/2, height/2, depth/2);
606 glVertex3f(-width/2, -height/2, -depth/2);
608 glVertex3f( width/2, height/2, depth/2);
609 glVertex3f( width/2, -height/2, -depth/2);
611 glVertex3f(-width/2, -height/2, depth/2);
612 glVertex3f(-width/2, height/2, -depth/2);
614 glVertex3f( width/2, -height/2, depth/2);
615 glVertex3f( width/2, height/2, -depth/2);
620 /* Construct stars (number of stars is dependent on size of screen) */
622 init_stars (ModeInfo *mi, int width, int height)
624 sws_configuration *sc = &scs[MI_SCREEN(mi)];
626 int size = (width > height ? width : height);
627 int nstars = size * size / 320;
630 int steps = max_size / inc;
632 glDeleteLists (sc->star_list, 1);
633 sc->star_list = glGenLists (1);
634 glNewList (sc->star_list, GL_COMPILE);
636 glEnable(GL_POINT_SMOOTH);
638 for (j = 1; j <= steps; j++)
640 glPointSize(inc * j);
642 for (i = 0; i < nstars / steps; i++)
644 glColor3f (0.6 + frand(0.3),
647 glVertex2f (2 * size * (0.5 - frand(1.0)),
648 2 * size * (0.5 - frand(1.0)));
656 /* Window management, etc
659 reshape_sws (ModeInfo *mi, int width, int height)
661 sws_configuration *sc = &scs[MI_SCREEN(mi)];
663 /* Set up matrices for perspective text display
666 GLfloat desired_aspect = (GLfloat) 3/4;
667 int w = mi->xgwa.width;
668 int h = mi->xgwa.height;
673 int h2 = w * desired_aspect;
674 yoff = (h - h2) / 2; /* Wide window: letterbox at top and bottom. */
675 if (yoff < 0) yoff = 0; /* Tall window: clip off the top. */
680 glMatrixMode (GL_PROJECTION);
681 glViewport (0, yoff, w, h);
683 glMatrixMode (GL_MODELVIEW);
685 gluPerspective (80.0, 1/desired_aspect, 1000, 55000);
686 gluLookAt (0.0, 0.0, 4600.0,
689 glRotatef (-60.0, 1.0, 0.0, 0.0);
692 glRotatef (60.0, 1.0, 0.0, 0.0);
693 glTranslatef (260, 3200, 0);
694 glScalef (1.85, 1.85, 1);
697 /* The above gives us an arena where the bottom edge of the screen is
698 represented by the line (-2100,-3140,0) - ( 2100,-3140,0). */
700 /* Now let's move the origin to the front of the screen. */
701 glTranslatef (0.0, -3140, 0.0);
703 /* And then let's scale so that the bottom of the screen is 1.0 wide. */
704 glScalef (4200, 4200, 4200);
708 /* Compute the height in pixels of the line at the bottom of the screen. */
710 GLdouble mm[17], pm[17];
712 GLfloat x = 0.5, y1 = 0, z = 0;
713 GLfloat y2 = sc->line_height;
714 GLdouble wx=-1, wy1=-1, wy2=-1, wz=-1;
716 glGetDoublev (GL_MODELVIEW_MATRIX, mm);
717 glGetDoublev (GL_PROJECTION_MATRIX, pm);
718 glGetIntegerv (GL_VIEWPORT, vp);
719 gluProject (x, y1, z, mm, pm, vp, &wx, &wy1, &wz);
720 gluProject (x, y2, z, mm, pm, vp, &wx, &wy2, &wz);
721 sc->line_pixel_height = (wy2 - wy1);
725 /* Compute the best looking line thickness for the bottom line.
728 sc->line_thickness = 1.0;
730 sc->line_thickness = (GLfloat) sc->line_pixel_height / FONT_WEIGHT;
732 if (sc->line_thickness < 1.2)
733 sc->line_thickness = 1.0;
738 gl_init (ModeInfo *mi)
740 sws_configuration *sc = &scs[MI_SCREEN(mi)];
742 program = get_string_resource (mi->dpy, "program", "Program");
744 glDisable (GL_LIGHTING);
745 glDisable (GL_DEPTH_TEST);
749 glEnable (GL_LINE_SMOOTH);
750 glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
751 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
755 sc->text_list = glGenLists (1);
756 glNewList (sc->text_list, GL_COMPILE);
759 sc->star_list = glGenLists (1);
760 glNewList (sc->star_list, GL_COMPILE);
763 sc->line_thickness = 1.0;
768 init_sws (ModeInfo *mi)
772 sws_configuration *sc = 0;
775 scs = (sws_configuration *)
776 calloc (MI_NUM_SCREENS(mi), sizeof (sws_configuration));
778 fprintf(stderr, "%s: out of memory\n", progname);
783 sc = &scs[MI_SCREEN(mi)];
785 sc->dpy = MI_DISPLAY(mi);
786 sc = &scs[MI_SCREEN(mi)];
787 sc->lines = (char **) calloc (max_lines+1, sizeof(char *));
789 if ((sc->glx_context = init_GL(mi)) != NULL) {
791 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
792 init_stars (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
798 sc->texfont = load_texture_font (MI_DISPLAY(mi), "font");
799 cw = texture_string_width (sc->texfont, "n", &lh);
802 glEnable(GL_ALPHA_TEST);
803 glEnable (GL_TEXTURE_2D);
805 check_gl_error ("loading font");
807 # ifdef GL_TEXTURE_MAX_ANISOTROPY_EXT
808 /* "Anistropic filtering helps for quadrilateral-angled textures.
809 A sharper image is accomplished by interpolating and filtering
810 multiple samples from one or more mipmaps to better approximate
811 very distorted textures. This is the next level of filtering
812 after trilinear filtering." */
814 glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16);
820 font_height = GLUT_FONT->top - GLUT_FONT->bottom;
821 sc->char_width = glutStrokeWidth (GLUT_FONT, 'z'); /* 'n' seems wide */
824 sc->font_scale = 1.0 / sc->char_width;
827 /* We consider a font that consumes 80 columns to be "18 points".
829 If neither -size nor -columns was specified, default to 60 columns
830 (which is 24 points.)
832 If both were specified, -columns has priority.
838 if (target_columns <= 0 && font_size <= 0)
841 if (target_columns > 0)
842 font_size = base_size * (base_col / (double) target_columns);
843 else if (font_size > 0)
844 target_columns = base_col * (base_size / (double) font_size);
847 sc->line_pixel_width = target_columns * sc->char_width;
849 sc->font_scale /= target_columns;
850 sc->line_height = font_height * sc->font_scale;
853 /* Buffer only two lines of text.
854 If the buffer is too big, there's a significant delay between
855 when the program launches and when the text appears, which can be
856 irritating for time-sensitive output (clock, current music, etc.)
858 sc->buf_size = target_columns * 2;
859 if (sc->buf_size < 80) sc->buf_size = 80;
860 sc->buf = (char *) calloc (1, sc->buf_size);
862 sc->subproc_relaunch_delay = 2 * 1000; /* 2 seconds */
863 sc->total_lines = max_lines-1;
866 star_spin = -star_spin;
868 if (!alignment_str || !*alignment_str ||
869 !strcasecmp(alignment_str, "left"))
871 else if (!strcasecmp(alignment_str, "center") ||
872 !strcasecmp(alignment_str, "middle"))
874 else if (!strcasecmp(alignment_str, "right"))
879 "%s: alignment must be left, center, or right, not \"%s\"\n",
880 progname, alignment_str);
884 launch_text_generator (sc);
886 /* one more reshape, after line_height has been computed */
887 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
892 draw_stars (ModeInfo *mi)
894 sws_configuration *sc = &scs[MI_SCREEN(mi)];
896 glMatrixMode (GL_PROJECTION);
901 glMatrixMode (GL_MODELVIEW);
905 glOrtho (-0.5 * MI_WIDTH(mi), 0.5 * MI_WIDTH(mi),
906 -0.5 * MI_HEIGHT(mi), 0.5 * MI_HEIGHT(mi),
908 glRotatef (sc->star_theta, 0.0, 0.0, 1.0);
909 if (textures_p) glDisable (GL_TEXTURE_2D);
910 glCallList (sc->star_list);
911 if (textures_p) glEnable (GL_TEXTURE_2D);
915 glMatrixMode (GL_PROJECTION);
920 draw_sws (ModeInfo *mi)
922 sws_configuration *sc = &scs[MI_SCREEN(mi)];
923 /* XtAppContext app = XtDisplayToApplicationContext (sc->dpy);*/
924 Display *dpy = MI_DISPLAY(mi);
925 Window window = MI_WINDOW(mi);
928 if (!sc->glx_context)
932 if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
933 XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
936 glDrawBuffer (GL_BACK);
937 glXMakeCurrent (dpy, window, *(sc->glx_context));
939 glClear (GL_COLOR_BUFFER_BIT);
943 glMatrixMode (GL_MODELVIEW);
950 if (textures_p) glDisable (GL_TEXTURE_2D);
952 glColor3f (0.4, 0.4, 0.4);
953 glTranslatef (0,-1, 0);
954 for (i = 0; i < 16; i++)
957 grid (1, 1, sc->char_width * sc->font_scale, sc->line_height, 0);
958 glTranslatef(0, 1, 0);
960 if (textures_p) glEnable (GL_TEXTURE_2D);
964 /* Scroll to current position */
965 glTranslatef (0.0, sc->intra_line_scroll, 0.0);
967 glColor3f (1.0, 1.0, 0.4);
968 glCallList (sc->text_list);
969 mi->polygon_count = sc->polygon_count;
971 sc->intra_line_scroll += sc->line_height / scroll_steps;
973 if (sc->intra_line_scroll >= sc->line_height)
975 sc->intra_line_scroll = 0;
977 /* Drop the oldest line off the end. */
981 /* Scroll the contents of the lines array toward 0. */
982 if (sc->total_lines > 0)
984 for (i = 1; i < sc->total_lines; i++)
985 sc->lines[i-1] = sc->lines[i];
986 sc->lines[--sc->total_lines] = 0;
989 /* Bring in new lines at the end. */
992 if (sc->total_lines < max_lines)
993 /* Oops, we ran out of text... well, insert some blank lines
994 here so that new text still pulls in from the bottom of
995 the screen, isntead of just appearing. */
996 sc->total_lines = max_lines;
998 glDeleteLists (sc->text_list, 1);
999 sc->text_list = glGenLists (1);
1000 glNewList (sc->text_list, GL_COMPILE);
1001 sc->polygon_count = 0;
1003 glScalef (sc->font_scale, sc->font_scale, sc->font_scale);
1004 for (i = 0; i < sc->total_lines; i++)
1006 double fade = (fade_p ? 1.0 * i / sc->total_lines : 1.0);
1007 int offscreen_lines = 2;
1010 double y = ((sc->total_lines - (i + offscreen_lines) - 1)
1013 char *line = sc->lines[i];
1017 double xx = x * 1.4; /* a little more to the left */
1019 sprintf(n, "%d:", i);
1020 draw_string (sc, xx / sc->font_scale, y / sc->font_scale, n);
1023 if (!line || !*line)
1026 if (sc->line_thickness != 1 && !textures_p)
1028 int max_thick_lines = MAX_THICK_LINES;
1029 GLfloat thinnest_line = 1.0;
1030 GLfloat thickest_line = sc->line_thickness;
1031 GLfloat range = thickest_line - thinnest_line;
1034 int j = sc->total_lines - i - 1;
1036 if (j > max_thick_lines)
1037 thickness = thinnest_line;
1039 thickness = (thinnest_line +
1040 (range * ((max_thick_lines - j) /
1041 (GLfloat) max_thick_lines)));
1043 glLineWidth (thickness);
1048 int n = string_width (sc, line);
1049 xoff = 1.0 - (n * sc->font_scale);
1055 glColor3f (fade, fade, 0.5 * fade);
1056 draw_string (sc, (x + xoff) / sc->font_scale, y / sc->font_scale,
1059 sc->polygon_count += strlen (line);
1067 if (mi->fps_p) do_fps (mi);
1069 glXSwapBuffers(dpy, window);
1071 sc->star_theta += star_spin;
1075 release_sws (ModeInfo *mi)
1079 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
1080 sws_configuration *sc = &scs[screen];
1082 XtRemoveInput (sc->pipe_id);
1086 XtRemoveTimeOut (sc->pipe_timer);
1088 /* #### there's more to free here */
1097 XSCREENSAVER_MODULE_2 ("StarWars", starwars, sws)