1 /* starwars, Copyright (c) 1998-2015 Jamie Zawinski <jwz@jwz.org> and
2 * Claudio Matsuoka <claudio@helllabs.org>
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
12 * Star Wars -- Phosphor meets a well-known scroller from a galaxy far,
15 * Feb 2000 Claudio Matsuoka First version.
16 * Jan 2001 Jamie Zawinski Rewrote large sections to add the ability to
17 * run a subprocess, customization of the font
18 * size and other parameters, etc.
19 * Feb 2001 jepler@inetnebr.com Added anti-aliased lines, and fade-to-black.
20 * Feb 2005 Jamie Zawinski Added texture fonts.
25 * starwars -program 'cat starwars.txt' -columns 25 -no-wrap
30 #endif /* HAVE_CONFIG_H */
40 #define DEFAULTS "*delay: 40000 \n" \
41 "*showFPS: False \n" \
44 "*texFontCacheSize: 300\n" \
45 "*font: " DEF_FONT "\n" \
46 "*textLiteral: " DEF_TEXT "\n" \
47 "*program: xscreensaver-text --cols 0" /* don't wrap */
49 # define refresh_sws 0
50 # define sws_handle_event 0
52 #define countof(x) (sizeof((x))/sizeof((*x)))
54 #include "xlockmore.h"
55 #include "textclient.h"
58 #ifdef USE_GL /* whole file */
60 /* Should be in <GL/glext.h> */
61 # ifndef GL_TEXTURE_MAX_ANISOTROPY_EXT
62 # define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
64 # ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT
65 # define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
69 #define DEF_LINES "125"
70 #define DEF_STEPS "35"
71 #define DEF_SPIN "0.03"
73 #define DEF_COLUMNS "-1"
74 #define DEF_LINE_WRAP "True"
75 #define DEF_ALIGNMENT "Center"
76 #define DEF_SMOOTH "True"
77 #define DEF_THICK "True"
78 #define DEF_FADE "True"
79 #define DEF_TEXTURES "True"
80 #define DEF_DEBUG "False"
82 #define DEF_FONT "-*-utopia-bold-r-normal-*-*-360-*-*-*-*-*-*"
86 #define MAX_THICK_LINES 25
87 #define FONT_WEIGHT 14
90 # define KEEP_ASPECT /* Letterboxing looks dumb on iPhone. */
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;
119 double intra_line_scroll;
121 int line_pixel_width; /* in font units (for wrapping text) */
122 int line_pixel_height; /* in screen units (for computing line thickness) */
123 GLfloat line_thickness;
128 static sws_configuration *scs = NULL;
130 static int max_lines;
131 static int scroll_steps;
132 static float star_spin;
133 static float font_size;
134 static int target_columns;
139 static int textures_p;
141 static char *alignment_str;
142 static int alignment;
144 static XrmOptionDescRec opts[] = {
145 {"-lines", ".lines", XrmoptionSepArg, 0 },
146 {"-steps", ".steps", XrmoptionSepArg, 0 },
147 {"-spin", ".spin", XrmoptionSepArg, 0 },
148 {"-size", ".size", XrmoptionSepArg, 0 },
149 {"-columns", ".columns", XrmoptionSepArg, 0 },
150 /*{"-font", ".font", XrmoptionSepArg, 0 },*/
151 {"-fade", ".fade", XrmoptionNoArg, "True" },
152 {"-no-fade", ".fade", XrmoptionNoArg, "False" },
153 {"-textures", ".textures", XrmoptionNoArg, "True" },
154 {"-smooth", ".smooth", XrmoptionNoArg, "True" },
155 {"-no-smooth", ".smooth", XrmoptionNoArg, "False" },
156 {"-thick", ".thick", XrmoptionNoArg, "True" },
157 {"-no-thick", ".thick", XrmoptionNoArg, "False" },
158 {"-no-textures", ".textures", XrmoptionNoArg, "False" },
159 {"-wrap", ".lineWrap", XrmoptionNoArg, "True" },
160 {"-no-wrap", ".lineWrap", XrmoptionNoArg, "False" },
161 {"-nowrap", ".lineWrap", XrmoptionNoArg, "False" },
162 {"-alignment", ".alignment", XrmoptionSepArg, 0 },
163 {"-left", ".alignment", XrmoptionNoArg, "Left" },
164 {"-right", ".alignment", XrmoptionNoArg, "Right" },
165 {"-center", ".alignment", XrmoptionNoArg, "Center" },
166 {"-debug", ".debug", XrmoptionNoArg, "True" },
169 static argtype vars[] = {
170 {&max_lines, "lines", "Integer", DEF_LINES, t_Int},
171 {&scroll_steps, "steps", "Integer", DEF_STEPS, t_Int},
172 {&star_spin, "spin", "Float", DEF_SPIN, t_Float},
173 {&font_size, "size", "Float", DEF_SIZE, t_Float},
174 {&target_columns, "columns", "Integer", DEF_COLUMNS, t_Int},
175 {&wrap_p, "lineWrap", "Boolean", DEF_LINE_WRAP, t_Bool},
176 {&alignment_str, "alignment", "Alignment", DEF_ALIGNMENT, t_String},
177 {&smooth_p, "smooth", "Boolean", DEF_SMOOTH, t_Bool},
178 {&thick_p, "thick", "Boolean", DEF_THICK, t_Bool},
179 {&fade_p, "fade", "Boolean", DEF_FADE, t_Bool},
180 {&textures_p, "textures", "Boolean", DEF_TEXTURES, t_Bool},
181 {&debug_p, "debug", "Boolean", DEF_DEBUG, t_Bool},
184 ENTRYPOINT ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
188 /* Tabs are bad, mmmkay? */
191 untabify (const char *string)
193 const char *ostring = string;
194 char *result = (char *) malloc ((strlen(string) * 8) + 1);
204 } while (col % TAB_WIDTH);
207 else if (*string == '\r' || *string == '\n')
212 else if (*string == '\010') /* backspace */
214 if (string > ostring)
229 strip (char *s, Bool leading, Bool trailing)
233 while (L > 0 && (s[L-1] == ' ' || s[L-1] == '\t'))
238 while (*s2 == ' ' || *s2 == '\t')
250 sw_string_width (sws_configuration *sc, const char *s)
255 texture_string_metrics (sc->texfont, s, &e, 0, 0);
259 return glutStrokeLength (GLUT_FONT, (unsigned char *) s);
264 sw_string_width2 (sws_configuration *sc, const char *s, size_t size)
266 char *s2 = (char *) malloc (size + 1);
269 strncpy (s2, s, size);
272 result = sw_string_width (sc, s2);
279 /* Populates the sc->lines list with as many lines as possible.
282 get_more_lines (sws_configuration *sc)
284 /* wrap anyway, if it's absurdly long. */
285 int wrap_pix = (wrap_p ? sc->line_pixel_width : 10000);
290 int target = sc->buf_size - sc->buf_tail - 2;
292 /* Fill as much as we can into sc->buf.
296 int c = textclient_getc (sc->tc);
299 sc->buf[sc->buf_tail++] = (char) c;
300 sc->buf[sc->buf_tail] = 0;
304 while (sc->total_lines < max_lines)
307 unsigned counter = 0;
309 /* OS X is really slow to calcuate the bounds for a line of text,
312 Really though, the right thing to do is probably to wrap
313 CTLineCreateTruncatedLine, one way or another. */
316 if (next_s >= sc->buf + sc->buf_tail)
322 sw_string_width2 (sc, sc->buf, next_s - sc->buf) >= wrap_pix)
325 counter = 12; /* <-- Adjust to taste. */
329 if (*next_s == '\r' || *next_s == '\n')
338 if (s >= sc->buf + sc->buf_tail)
339 /* Reached end of buffer before end of line. Bail. */
342 /* When checking pixel width, always measure the line from the
343 beginning, or else multi-byte UTF-8 characters, particularly
344 combining diacriticals, aren't measured right. */
346 if (*s == '\r' || *s == '\n' ||
347 (s > sc->buf && sw_string_width2 (sc, sc->buf, s - sc->buf) >= wrap_pix))
351 if (*s == '\r' || *s == '\n')
353 if (*s == '\r' && s[1] == '\n') /* swallow CRLF too */
360 /* We wrapped -- try to back up to the previous word boundary. */
363 while (s2 > sc->buf && *s2 != ' ' && *s2 != '\t')
373 sc->lines[sc->total_lines] = (char *) malloc (L+1);
374 memcpy (sc->lines[sc->total_lines], sc->buf, L);
375 sc->lines[sc->total_lines][L] = 0;
379 /* The GLUT font only has ASCII characters. */
380 char *s1 = utf8_to_latin1 (sc->lines[sc->total_lines], True);
381 free (sc->lines[sc->total_lines]);
382 sc->lines[sc->total_lines] = s1;
386 char *t = sc->lines[sc->total_lines];
387 char *ut = untabify (t);
388 strip (ut, (alignment == 0), 1); /* if centering, strip
389 leading whitespace too */
390 sc->lines[sc->total_lines] = ut;
394 sc->line_widths[sc->total_lines] =
395 sw_string_width(sc, sc->lines[sc->total_lines]);
399 if (sc->buf_tail > (s - sc->buf))
401 int i = sc->buf_tail - (s - sc->buf);
402 memmove (sc->buf, s, i);
404 sc->buf[sc->buf_tail] = 0;
411 sc->buf[sc->buf_tail] = 0;
427 draw_string (sws_configuration *sc, GLfloat x, GLfloat y, const char *s)
430 if (!s || !*s) return;
432 glTranslatef (x, y, 0);
435 print_texture_string (sc->texfont, s);
438 glutStrokeCharacter (GLUT_FONT, *s++);
443 GLfloat h = sc->line_height / sc->font_scale;
444 char **chars = utf8_split (os, 0);
446 char *s2 = (char *) malloc (strlen(s) + 1);
449 if (textures_p) glDisable (GL_TEXTURE_2D);
452 glColor3f (0.2, 0.2, 0.5);
456 for (i = 0; chars[i]; i++)
458 glVertex3f (x + w, y - sc->descent, 0); /* char left */
459 glVertex3f (x + w, y - sc->descent + h, 0);
460 strcat (s2, chars[i]);
461 w = sw_string_width (sc, s2);
465 glVertex3f (x + w, y - sc->descent, 0); /* char right */
466 glVertex3f (x + w, y - sc->descent + h, 0);
468 glVertex3f (x, y - sc->descent + h, 0); /* ascent */
469 glVertex3f (x + w, y - sc->descent + h, 0);
471 glVertex3f (x - sc->char_width, y, 0); /* baseline */
472 glVertex3f (x + sc->char_width + w, y, 0);
474 glVertex3f (x, y - sc->descent, 0); /* descent */
475 glVertex3f (x + w, y - sc->descent, 0);
482 if (textures_p) glEnable (GL_TEXTURE_2D);
488 grid (double width, double height, double xspacing, double yspacing, double z)
491 for (y = 0; y <= height/2; y += yspacing)
494 glVertex3f(-width/2, y, z);
495 glVertex3f( width/2, y, z);
496 glVertex3f(-width/2, -y, z);
497 glVertex3f( width/2, -y, z);
500 for (x = 0; x <= width/2; x += xspacing)
503 glVertex3f( x, -height/2, z);
504 glVertex3f( x, height/2, z);
505 glVertex3f(-x, -height/2, z);
506 glVertex3f(-x, height/2, z);
511 glVertex3f(-width, 0, z);
512 glVertex3f( width, 0, z);
513 glVertex3f(0, -height, z);
514 glVertex3f(0, height, z);
519 box (double width, double height, double depth)
521 glBegin(GL_LINE_LOOP);
522 glVertex3f(-width/2, -height/2, -depth/2);
523 glVertex3f(-width/2, height/2, -depth/2);
524 glVertex3f( width/2, height/2, -depth/2);
525 glVertex3f( width/2, -height/2, -depth/2);
527 glBegin(GL_LINE_LOOP);
528 glVertex3f(-width/2, -height/2, depth/2);
529 glVertex3f(-width/2, height/2, depth/2);
530 glVertex3f( width/2, height/2, depth/2);
531 glVertex3f( width/2, -height/2, depth/2);
533 glBegin(GL_LINE_LOOP);
534 glVertex3f(-width/2, -height/2, -depth/2);
535 glVertex3f(-width/2, -height/2, depth/2);
536 glVertex3f(-width/2, height/2, depth/2);
537 glVertex3f(-width/2, height/2, -depth/2);
539 glBegin(GL_LINE_LOOP);
540 glVertex3f( width/2, -height/2, -depth/2);
541 glVertex3f( width/2, -height/2, depth/2);
542 glVertex3f( width/2, height/2, depth/2);
543 glVertex3f( width/2, height/2, -depth/2);
547 glVertex3f(-width/2, height/2, depth/2);
548 glVertex3f(-width/2, -height/2, -depth/2);
550 glVertex3f( width/2, height/2, depth/2);
551 glVertex3f( width/2, -height/2, -depth/2);
553 glVertex3f(-width/2, -height/2, depth/2);
554 glVertex3f(-width/2, height/2, -depth/2);
556 glVertex3f( width/2, -height/2, depth/2);
557 glVertex3f( width/2, height/2, -depth/2);
562 /* Construct stars (number of stars is dependent on size of screen) */
564 init_stars (ModeInfo *mi, int width, int height)
566 sws_configuration *sc = &scs[MI_SCREEN(mi)];
568 int size = (width > height ? width : height);
569 int nstars = size * size / 320;
572 int steps = max_size / inc;
574 glDeleteLists (sc->star_list, 1);
575 sc->star_list = glGenLists (1);
576 glNewList (sc->star_list, GL_COMPILE);
578 glEnable(GL_POINT_SMOOTH);
580 for (j = 1; j <= steps; j++)
582 glPointSize(inc * j);
584 for (i = 0; i < nstars / steps; i++)
586 glColor3f (0.6 + frand(0.3),
589 glVertex2f (2 * size * (0.5 - frand(1.0)),
590 2 * size * (0.5 - frand(1.0)));
598 /* Window management, etc
601 reshape_sws (ModeInfo *mi, int width, int height)
603 sws_configuration *sc = &scs[MI_SCREEN(mi)];
605 /* Set up matrices for perspective text display
608 GLfloat desired_aspect = (GLfloat) 3/4;
609 int w = mi->xgwa.width;
610 int h = mi->xgwa.height;
612 GLfloat rot = current_device_rotation();
616 int h2 = w * desired_aspect;
617 yoff = (h - h2) / 2; /* Wide window: letterbox at top and bottom. */
618 if (yoff < 0) yoff = 0; /* Tall window: clip off the top. */
623 glMatrixMode (GL_PROJECTION);
624 glViewport (0, yoff, w, h);
626 glMatrixMode (GL_MODELVIEW);
628 gluPerspective (80.0, 1/desired_aspect, 1000, 55000);
629 gluLookAt (0.0, 0.0, 4600.0,
633 glRotatef(rot, 0, 0, 1);
635 /* Horrible kludge to prevent the text from materializing already
636 on screen on iPhone in landscape mode.
638 if ((rot > 45 && rot < 135) ||
639 (rot < -45 && rot > -135))
645 glRotatef (-60.0, 1.0, 0.0, 0.0);
648 glRotatef (60.0, 1.0, 0.0, 0.0);
649 glTranslatef (260, 3200, 0);
650 glScalef (1.85, 1.85, 1);
653 /* The above gives us an arena where the bottom edge of the screen is
654 represented by the line (-2100,-3140,0) - ( 2100,-3140,0). */
656 /* Now let's move the origin to the front of the screen. */
657 glTranslatef (0.0, -3140, 0.0);
659 /* And then let's scale so that the bottom of the screen is 1.0 wide. */
660 glScalef (4200, 4200, 4200);
664 /* Compute the height in pixels of the line at the bottom of the screen. */
666 GLdouble mm[17], pm[17];
668 GLdouble x = 0.5, y1 = 0, z = 0;
669 GLdouble y2 = sc->line_height;
670 GLdouble wx=-1, wy1=-1, wy2=-1, wz=-1;
672 glGetDoublev (GL_MODELVIEW_MATRIX, mm);
673 glGetDoublev (GL_PROJECTION_MATRIX, pm);
674 glGetIntegerv (GL_VIEWPORT, vp);
675 gluProject (x, y1, z, mm, pm, vp, &wx, &wy1, &wz);
676 gluProject (x, y2, z, mm, pm, vp, &wx, &wy2, &wz);
677 sc->line_pixel_height = (wy2 - wy1);
681 /* Compute the best looking line thickness for the bottom line.
684 sc->line_thickness = 1.0;
686 sc->line_thickness = (GLfloat) sc->line_pixel_height / FONT_WEIGHT;
688 if (sc->line_thickness < 1.2)
689 sc->line_thickness = 1.0;
694 gl_init (ModeInfo *mi)
696 sws_configuration *sc = &scs[MI_SCREEN(mi)];
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 = 0;
729 scs = (sws_configuration *)
730 calloc (MI_NUM_SCREENS(mi), sizeof (sws_configuration));
732 fprintf(stderr, "%s: out of memory\n", progname);
737 sc = &scs[MI_SCREEN(mi)];
739 sc->dpy = MI_DISPLAY(mi);
740 sc = &scs[MI_SCREEN(mi)];
741 /* Unchecked malloc. :( */
742 sc->lines = (char **) calloc (max_lines+1, sizeof(char *));
743 sc->line_widths = (int *) calloc (max_lines+1, sizeof(int));
745 if ((sc->glx_context = init_GL(mi)) != NULL) {
747 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
748 clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */
750 init_stars (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
756 int cw, ascent, descent;
757 sc->texfont = load_texture_font (MI_DISPLAY(mi), "font");
758 texture_string_metrics (sc->texfont, "n", &e, &ascent, &descent);
761 font_height = ascent + descent;
762 sc->descent = descent;
763 glEnable(GL_ALPHA_TEST);
764 glEnable (GL_TEXTURE_2D);
766 check_gl_error ("loading font");
768 /* "Anistropic filtering helps for quadrilateral-angled textures.
769 A sharper image is accomplished by interpolating and filtering
770 multiple samples from one or more mipmaps to better approximate
771 very distorted textures. This is the next level of filtering
772 after trilinear filtering." */
774 strstr ((char *) glGetString(GL_EXTENSIONS),
775 "GL_EXT_texture_filter_anisotropic"))
777 GLfloat anisotropic = 0.0;
778 glGetFloatv (GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropic);
779 if (anisotropic >= 1.0)
780 glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,
786 font_height = GLUT_FONT->top - GLUT_FONT->bottom;
787 sc->char_width = glutStrokeWidth (GLUT_FONT, 'z'); /* 'n' seems wide */
791 sc->font_scale = 1.0 / sc->char_width;
794 /* We consider a font that consumes 80 columns to be "18 points".
796 If neither -size nor -columns was specified, default to 60 columns
797 (which is 24 points.)
799 If both were specified, -columns has priority.
805 if (target_columns <= 0 && font_size <= 0)
808 if (target_columns > 0)
809 font_size = base_size * (base_col / (double) target_columns);
810 else if (font_size > 0)
811 target_columns = base_col * (base_size / (double) font_size);
814 sc->line_pixel_width = target_columns * sc->char_width;
816 sc->font_scale /= target_columns;
817 sc->line_height = font_height * sc->font_scale;
819 /* Buffer only a few lines of text.
820 If the buffer is too big, there's a significant delay between
821 when the program launches and when the text appears, which can be
822 irritating for time-sensitive output (clock, current music, etc.)
824 I'd like to buffer only 2 lines, but we need to assume that we
825 could get a whole line of N-byte Unicrud, and if we fill the buffer
826 before hitting the end of the line, we stall.
828 sc->buf_size = target_columns * 2 * 4;
829 if (sc->buf_size < 80) sc->buf_size = 80;
830 sc->buf = (char *) calloc (1, sc->buf_size);
832 sc->total_lines = max_lines-1;
835 star_spin = -star_spin;
837 if (!alignment_str || !*alignment_str ||
838 !strcasecmp(alignment_str, "left"))
840 else if (!strcasecmp(alignment_str, "center") ||
841 !strcasecmp(alignment_str, "middle"))
843 else if (!strcasecmp(alignment_str, "right"))
848 "%s: alignment must be left, center, or right, not \"%s\"\n",
849 progname, alignment_str);
853 sc->tc = textclient_open (sc->dpy);
855 /* one more reshape, after line_height has been computed */
856 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
861 draw_stars (ModeInfo *mi)
863 sws_configuration *sc = &scs[MI_SCREEN(mi)];
865 glMatrixMode (GL_PROJECTION);
870 glMatrixMode (GL_MODELVIEW);
874 glOrtho (-0.5 * MI_WIDTH(mi), 0.5 * MI_WIDTH(mi),
875 -0.5 * MI_HEIGHT(mi), 0.5 * MI_HEIGHT(mi),
877 glRotatef (sc->star_theta, 0.0, 0.0, 1.0);
878 if (textures_p) glDisable (GL_TEXTURE_2D);
880 /* Keep the stars pointing in the same direction after rotation */
881 glRotatef(current_device_rotation(), 0, 0, 1);
883 glCallList (sc->star_list);
884 if (textures_p) glEnable (GL_TEXTURE_2D);
888 glMatrixMode (GL_PROJECTION);
893 draw_sws (ModeInfo *mi)
895 sws_configuration *sc = &scs[MI_SCREEN(mi)];
896 /* XtAppContext app = XtDisplayToApplicationContext (sc->dpy);*/
897 Display *dpy = MI_DISPLAY(mi);
898 Window window = MI_WINDOW(mi);
901 if (!sc->glx_context)
904 glDrawBuffer (GL_BACK);
905 glXMakeCurrent (dpy, window, *(sc->glx_context));
907 glClear (GL_COLOR_BUFFER_BIT);
911 glMatrixMode (GL_MODELVIEW);
915 /* Need to do this every time to get device rotation right */
916 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
923 if (textures_p) glDisable (GL_TEXTURE_2D);
925 glTranslatef (0,-1, 0);
927 glColor3f(1, 0, 0); /* Red line is where text appears */
929 glTranslatef(0, -0.028, 0);
932 glVertex3f(-0.5, 1, 0);
933 glVertex3f( 0.5, 1, 0);
934 glVertex3f(-0.5, -1, 0);
935 glVertex3f( 0.5, -1, 0);
940 glColor3f (0.2, 0.2, 0.2);
941 for (i = 0; i < 16; i++)
944 grid (1, 1, sc->char_width * sc->font_scale, sc->line_height, 0);
945 glTranslatef(0, 1, 0);
947 if (textures_p) glEnable (GL_TEXTURE_2D);
949 check_gl_error ("debug render");
952 /* Scroll to current position */
953 glTranslatef (0.0, sc->intra_line_scroll, 0.0);
955 glColor3f (1.0, 1.0, 0.4);
957 mi->polygon_count = 0;
960 glScalef (sc->font_scale, sc->font_scale, sc->font_scale);
961 for (i = 0; i < sc->total_lines; i++)
963 double fade = (fade_p ? 1.0 * i / sc->total_lines : 1.0);
964 int offscreen_lines = 2;
967 double y = ((sc->total_lines - (i + offscreen_lines) - 1)
970 char *line = sc->lines[i];
974 double xx = x * 1.4; /* a little more to the left */
976 sprintf(n, "%d:", i);
977 glColor3f (1.0, 1.0, 1.0);
978 draw_string (sc, xx / sc->font_scale, y / sc->font_scale, n);
984 if (sc->line_thickness != 1 && !textures_p)
986 int max_thick_lines = MAX_THICK_LINES;
987 GLfloat thinnest_line = 1.0;
988 GLfloat thickest_line = sc->line_thickness;
989 GLfloat range = thickest_line - thinnest_line;
992 int j = sc->total_lines - i - 1;
994 if (j > max_thick_lines)
995 thickness = thinnest_line;
997 thickness = (thinnest_line +
998 (range * ((max_thick_lines - j) /
999 (GLfloat) max_thick_lines)));
1001 glLineWidth (thickness);
1006 int n = sc->line_widths[i];
1007 xoff = 1.0 - (n * sc->font_scale);
1013 glColor3f (fade, fade, 0.5 * fade);
1014 draw_string (sc, (x + xoff) / sc->font_scale, y / sc->font_scale,
1017 mi->polygon_count += strlen (line);
1023 sc->intra_line_scroll += sc->line_height / scroll_steps;
1025 if (sc->intra_line_scroll >= sc->line_height)
1027 sc->intra_line_scroll = 0;
1029 /* Drop the oldest line off the end. */
1031 free (sc->lines[0]);
1033 /* Scroll the contents of the lines array toward 0. */
1034 if (sc->total_lines > 0)
1036 for (i = 1; i < sc->total_lines; i++) {
1037 sc->lines[i-1] = sc->lines[i];
1038 sc->line_widths[i-1] = sc->line_widths[i];
1040 sc->lines[--sc->total_lines] = 0;
1043 /* Bring in new lines at the end. */
1044 get_more_lines (sc);
1046 if (sc->total_lines < max_lines)
1047 /* Oops, we ran out of text... well, insert some blank lines
1048 here so that new text still pulls in from the bottom of
1049 the screen, isntead of just appearing. */
1050 sc->total_lines = max_lines;
1055 if (mi->fps_p) do_fps (mi);
1057 glXSwapBuffers(dpy, window);
1059 sc->star_theta += star_spin;
1063 release_sws (ModeInfo *mi)
1067 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
1068 sws_configuration *sc = &scs[screen];
1070 textclient_close (sc->tc);
1072 /* #### there's more to free here */
1082 __extension__ /* don't warn about "string length is greater than the length
1083 ISO C89 compilers are required to support" when including
1084 "starwars.txt" in the defaults... */
1087 XSCREENSAVER_MODULE_2 ("StarWars", starwars, sws)