1 /* starwars, Copyright (c) 1998-2018 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
28 #define DEFAULTS "*delay: 40000 \n" \
29 "*showFPS: False \n" \
32 "*texFontCacheSize: 300\n" \
33 "*font: " DEF_FONT "\n" \
34 "*textLiteral: " DEF_TEXT "\n" \
35 "*program: xscreensaver-text --cols 0" /* don't wrap */
37 # define release_sws 0
38 # define sws_handle_event xlockmore_no_events
40 #include "xlockmore.h"
41 #include "textclient.h"
52 #ifdef USE_GL /* whole file */
54 /* Should be in <GL/glext.h> */
55 # ifndef GL_TEXTURE_MAX_ANISOTROPY_EXT
56 # define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
58 # ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT
59 # define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
63 #define DEF_LINES "125"
64 #define DEF_STEPS "35"
65 #define DEF_SPIN "0.03"
66 #define DEF_SATURATION "0.3"
68 #define DEF_COLUMNS "-1"
69 #define DEF_LINE_WRAP "True"
70 #define DEF_ALIGNMENT "Center"
71 #define DEF_SMOOTH "True"
72 #define DEF_THICK "True"
73 #define DEF_FADE "True"
74 #define DEF_TEXTURES "True"
75 #define DEF_DEBUG "False"
76 #define DEF_FONT "sans-serif 36"
80 #define MAX_THICK_LINES 25
81 #define FONT_WEIGHT 14
84 # define KEEP_ASPECT /* Letterboxing looks dumb on iPhone. */
88 #include "glutstroke.h"
89 #include "glut_roman.h"
90 #define GLUT_FONT (&glutStrokeRoman)
94 GLXContext *glx_context;
96 GLuint text_list, star_list;
97 texture_font_data *texfont;
113 double intra_line_scroll;
115 int line_pixel_width; /* in font units (for wrapping text) */
116 int line_pixel_height; /* in screen units (for computing line thickness) */
117 GLfloat line_thickness;
122 static sws_configuration *scs = NULL;
124 static int max_lines;
125 static int scroll_steps;
126 static float star_spin;
127 static float star_saturation;
128 static float font_size;
129 static int target_columns;
134 static int textures_p;
136 static char *alignment_str;
137 static int alignment;
139 static XrmOptionDescRec opts[] = {
140 {"-lines", ".lines", XrmoptionSepArg, 0 },
141 {"-steps", ".steps", XrmoptionSepArg, 0 },
142 {"-spin", ".spin", XrmoptionSepArg, 0 },
143 {"-saturation", ".saturation",XrmoptionSepArg, 0 },
144 {"-size", ".size", XrmoptionSepArg, 0 },
145 {"-columns", ".columns", XrmoptionSepArg, 0 },
146 /*{"-font", ".font", XrmoptionSepArg, 0 },*/
147 {"-program", ".program", XrmoptionSepArg, 0 },
148 {"-fade", ".fade", XrmoptionNoArg, "True" },
149 {"-no-fade", ".fade", XrmoptionNoArg, "False" },
150 {"-textures", ".textures", XrmoptionNoArg, "True" },
151 {"-smooth", ".smooth", XrmoptionNoArg, "True" },
152 {"-no-smooth", ".smooth", XrmoptionNoArg, "False" },
153 {"-thick", ".thick", XrmoptionNoArg, "True" },
154 {"-no-thick", ".thick", XrmoptionNoArg, "False" },
155 {"-no-textures", ".textures", XrmoptionNoArg, "False" },
156 {"-wrap", ".lineWrap", XrmoptionNoArg, "True" },
157 {"-no-wrap", ".lineWrap", XrmoptionNoArg, "False" },
158 {"-nowrap", ".lineWrap", XrmoptionNoArg, "False" },
159 {"-alignment", ".alignment", XrmoptionSepArg, 0 },
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 {&max_lines, "lines", "Integer", DEF_LINES, t_Int},
168 {&scroll_steps, "steps", "Integer", DEF_STEPS, t_Int},
169 {&star_spin, "spin", "Float", DEF_SPIN, t_Float},
170 {&star_saturation,"saturation","Float", DEF_SATURATION,t_Float},
171 {&font_size, "size", "Float", DEF_SIZE, t_Float},
172 {&target_columns, "columns", "Integer", DEF_COLUMNS, t_Int},
173 {&wrap_p, "lineWrap", "Boolean", DEF_LINE_WRAP, t_Bool},
174 {&alignment_str, "alignment", "Alignment", DEF_ALIGNMENT, 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 ENTRYPOINT 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')
248 sw_string_width (sws_configuration *sc, const char *s)
253 texture_string_metrics (sc->texfont, s, &e, 0, 0);
257 return glutStrokeLength (GLUT_FONT, (unsigned char *) s);
262 sw_string_width2 (sws_configuration *sc, const char *s, size_t size)
264 char *s2 = (char *) malloc (size + 1);
267 strncpy (s2, s, size);
270 result = sw_string_width (sc, s2);
277 /* Populates the sc->lines list with as many lines as possible.
280 get_more_lines (sws_configuration *sc)
282 /* wrap anyway, if it's absurdly long. */
283 int wrap_pix = (wrap_p ? sc->line_pixel_width : 10000);
287 int target = sc->buf_size - sc->buf_tail - 2;
289 /* Fill as much as we can into sc->buf.
293 int c = textclient_getc (sc->tc);
296 sc->buf[sc->buf_tail++] = (char) c;
297 sc->buf[sc->buf_tail] = 0;
301 while (sc->total_lines < max_lines)
304 unsigned counter = 0;
306 /* OS X is really slow to calcuate the bounds for a line of text,
309 Really though, the right thing to do is probably to wrap
310 CTLineCreateTruncatedLine, one way or another. */
313 if (next_s >= sc->buf + sc->buf_tail)
319 sw_string_width2 (sc, sc->buf, next_s - sc->buf) >= wrap_pix)
322 counter = 12; /* <-- Adjust to taste. */
326 if (*next_s == '\r' || *next_s == '\n')
335 if (s >= sc->buf + sc->buf_tail)
336 /* Reached end of buffer before end of line. Bail. */
339 /* When checking pixel width, always measure the line from the
340 beginning, or else multi-byte UTF-8 characters, particularly
341 combining diacriticals, aren't measured right. */
343 if (*s == '\r' || *s == '\n' ||
344 (s > sc->buf && sw_string_width2 (sc, sc->buf, s - sc->buf) >= wrap_pix))
348 if (*s == '\r' || *s == '\n')
350 if (*s == '\r' && s[1] == '\n') /* swallow CRLF too */
357 /* We wrapped -- try to back up to the previous word boundary. */
360 while (s2 > sc->buf && *s2 != ' ' && *s2 != '\t')
370 sc->lines[sc->total_lines] = (char *) malloc (L+1);
371 memcpy (sc->lines[sc->total_lines], sc->buf, L);
372 sc->lines[sc->total_lines][L] = 0;
376 /* The GLUT font only has ASCII characters. */
377 char *s1 = utf8_to_latin1 (sc->lines[sc->total_lines], True);
378 free (sc->lines[sc->total_lines]);
379 sc->lines[sc->total_lines] = s1;
383 char *t = sc->lines[sc->total_lines];
384 char *ut = untabify (t);
385 strip (ut, (alignment == 0), 1); /* if centering, strip
386 leading whitespace too */
387 sc->lines[sc->total_lines] = ut;
391 sc->line_widths[sc->total_lines] =
392 sw_string_width(sc, sc->lines[sc->total_lines]);
396 if (sc->buf_tail > (s - sc->buf))
398 int i = sc->buf_tail - (s - sc->buf);
399 memmove (sc->buf, s, i);
401 sc->buf[sc->buf_tail] = 0;
408 sc->buf[sc->buf_tail] = 0;
423 draw_string (sws_configuration *sc, GLfloat x, GLfloat y, const char *s)
426 if (!s || !*s) return;
428 glTranslatef (x, y, 0);
431 print_texture_string (sc->texfont, s);
434 glutStrokeCharacter (GLUT_FONT, *s++);
439 GLfloat h = sc->line_height / sc->font_scale;
440 char **chars = utf8_split (os, 0);
442 char *s2 = (char *) malloc (strlen(s) + 1);
445 if (textures_p) glDisable (GL_TEXTURE_2D);
448 glColor3f (0.2, 0.2, 0.5);
452 for (i = 0; chars[i]; i++)
454 glVertex3f (x + w, y - sc->descent, 0); /* char left */
455 glVertex3f (x + w, y - sc->descent + h, 0);
456 strcat (s2, chars[i]);
457 w = sw_string_width (sc, s2);
461 glVertex3f (x + w, y - sc->descent, 0); /* char right */
462 glVertex3f (x + w, y - sc->descent + h, 0);
464 glVertex3f (x, y - sc->descent + h, 0); /* ascent */
465 glVertex3f (x + w, y - sc->descent + h, 0);
467 glVertex3f (x - sc->char_width, y, 0); /* baseline */
468 glVertex3f (x + sc->char_width + w, y, 0);
470 glVertex3f (x, y - sc->descent, 0); /* descent */
471 glVertex3f (x + w, y - sc->descent, 0);
478 if (textures_p) glEnable (GL_TEXTURE_2D);
484 grid (double width, double height, double xspacing, double yspacing, double z)
487 for (y = 0; y <= height/2; y += yspacing)
490 glVertex3f(-width/2, y, z);
491 glVertex3f( width/2, y, z);
492 glVertex3f(-width/2, -y, z);
493 glVertex3f( width/2, -y, z);
496 for (x = 0; x <= width/2; x += xspacing)
499 glVertex3f( x, -height/2, z);
500 glVertex3f( x, height/2, z);
501 glVertex3f(-x, -height/2, z);
502 glVertex3f(-x, height/2, z);
507 glVertex3f(-width, 0, z);
508 glVertex3f( width, 0, z);
509 glVertex3f(0, -height, z);
510 glVertex3f(0, height, z);
515 box (double width, double height, double depth)
517 glBegin(GL_LINE_LOOP);
518 glVertex3f(-width/2, -height/2, -depth/2);
519 glVertex3f(-width/2, height/2, -depth/2);
520 glVertex3f( width/2, height/2, -depth/2);
521 glVertex3f( width/2, -height/2, -depth/2);
523 glBegin(GL_LINE_LOOP);
524 glVertex3f(-width/2, -height/2, depth/2);
525 glVertex3f(-width/2, height/2, depth/2);
526 glVertex3f( width/2, height/2, depth/2);
527 glVertex3f( width/2, -height/2, depth/2);
529 glBegin(GL_LINE_LOOP);
530 glVertex3f(-width/2, -height/2, -depth/2);
531 glVertex3f(-width/2, -height/2, depth/2);
532 glVertex3f(-width/2, height/2, depth/2);
533 glVertex3f(-width/2, height/2, -depth/2);
535 glBegin(GL_LINE_LOOP);
536 glVertex3f( width/2, -height/2, -depth/2);
537 glVertex3f( width/2, -height/2, depth/2);
538 glVertex3f( width/2, height/2, depth/2);
539 glVertex3f( width/2, height/2, -depth/2);
543 glVertex3f(-width/2, height/2, depth/2);
544 glVertex3f(-width/2, -height/2, -depth/2);
546 glVertex3f( width/2, height/2, depth/2);
547 glVertex3f( width/2, -height/2, -depth/2);
549 glVertex3f(-width/2, -height/2, depth/2);
550 glVertex3f(-width/2, height/2, -depth/2);
552 glVertex3f( width/2, -height/2, depth/2);
553 glVertex3f( width/2, height/2, -depth/2);
558 /* Construct stars (number of stars is dependent on size of screen) */
560 init_stars (ModeInfo *mi, int width, int height)
562 sws_configuration *sc = &scs[MI_SCREEN(mi)];
564 int size = (width > height ? width : height);
565 int nstars = size * size / 320;
568 int steps = max_size / inc;
571 if (MI_WIDTH(mi) > 2560) { /* Retina displays */
573 nstars = (size/scale) * (size/scale) / 320;
576 glDeleteLists (sc->star_list, 1);
577 sc->star_list = glGenLists (1);
578 glNewList (sc->star_list, GL_COMPILE);
580 glEnable(GL_POINT_SMOOTH);
582 for (j = 1; j <= steps; j++)
584 glPointSize(inc * j * scale);
586 for (i = 0; i < nstars / steps; i++)
588 GLfloat brightness = 0.9 - star_saturation;
589 glColor3f (brightness + frand(star_saturation),
590 brightness + frand(star_saturation),
591 brightness + frand(star_saturation));
592 glVertex2f (2 * size * (0.5 - frand(1.0)),
593 2 * size * (0.5 - frand(1.0)));
601 /* Window management, etc
604 reshape_sws (ModeInfo *mi, int width, int height)
606 sws_configuration *sc = &scs[MI_SCREEN(mi)];
608 /* Set up matrices for perspective text display
611 GLfloat desired_aspect = (GLfloat) 3/4;
612 int w = mi->xgwa.width;
613 int h = mi->xgwa.height;
615 GLfloat rot = current_device_rotation();
619 int h2 = w * desired_aspect;
620 yoff = (h - h2) / 2; /* Wide window: letterbox at top and bottom. */
621 if (yoff < 0) yoff = 0; /* Tall window: clip off the top. */
626 glViewport (0, yoff, w, h);
628 glMatrixMode (GL_PROJECTION);
631 glMatrixMode (GL_MODELVIEW);
633 gluPerspective (80.0, 1/desired_aspect, 1000, 55000);
634 gluLookAt (0.0, 0.0, 4600.0,
638 /* Horrible kludge to prevent the text from materializing already
639 on screen on iPhone in landscape mode.
641 if ((rot > 45 && rot < 135) ||
642 (rot < -45 && rot > -135))
648 glRotatef (-60.0, 1.0, 0.0, 0.0);
651 glRotatef (60.0, 1.0, 0.0, 0.0);
652 glTranslatef (260, 3200, 0);
653 glScalef (1.85, 1.85, 1);
656 /* The above gives us an arena where the bottom edge of the screen is
657 represented by the line (-2100,-3140,0) - ( 2100,-3140,0). */
659 /* Now let's move the origin to the front of the screen. */
660 glTranslatef (0.0, -3140, 0.0);
662 /* And then let's scale so that the bottom of the screen is 1.0 wide. */
663 glScalef (4200, 4200, 4200);
667 /* Compute the height in pixels of the line at the bottom of the screen. */
669 GLdouble mm[17], pm[17];
671 GLdouble x = 0.5, y1 = 0, z = 0;
672 GLdouble y2 = sc->line_height;
673 GLdouble wx=-1, wy1=-1, wy2=-1, wz=-1;
675 glGetDoublev (GL_MODELVIEW_MATRIX, mm);
676 glGetDoublev (GL_PROJECTION_MATRIX, pm);
677 glGetIntegerv (GL_VIEWPORT, vp);
678 gluProject (x, y1, z, mm, pm, vp, &wx, &wy1, &wz);
679 gluProject (x, y2, z, mm, pm, vp, &wx, &wy2, &wz);
680 sc->line_pixel_height = (wy2 - wy1);
684 /* Compute the best looking line thickness for the bottom line.
687 sc->line_thickness = 1.0;
689 sc->line_thickness = (GLfloat) sc->line_pixel_height / FONT_WEIGHT;
691 if (sc->line_thickness < 1.2)
692 sc->line_thickness = 1.0;
697 gl_init (ModeInfo *mi)
699 sws_configuration *sc = &scs[MI_SCREEN(mi)];
701 glDisable (GL_LIGHTING);
702 glDisable (GL_DEPTH_TEST);
706 glEnable (GL_LINE_SMOOTH);
707 glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
708 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
712 sc->text_list = glGenLists (1);
713 glNewList (sc->text_list, GL_COMPILE);
716 sc->star_list = glGenLists (1);
717 glNewList (sc->star_list, GL_COMPILE);
720 sc->line_thickness = 1.0;
725 init_sws (ModeInfo *mi)
729 sws_configuration *sc = 0;
733 sc = &scs[MI_SCREEN(mi)];
735 sc->dpy = MI_DISPLAY(mi);
736 sc = &scs[MI_SCREEN(mi)];
737 /* Unchecked malloc. :( */
738 sc->lines = (char **) calloc (max_lines+1, sizeof(char *));
739 sc->line_widths = (int *) calloc (max_lines+1, sizeof(int));
741 if ((sc->glx_context = init_GL(mi)) != NULL) {
743 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
744 clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */
746 init_stars (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
752 int cw, ascent, descent;
753 sc->texfont = load_texture_font (MI_DISPLAY(mi), "font");
754 texture_string_metrics (sc->texfont, "n", &e, &ascent, &descent);
757 font_height = ascent + descent;
758 sc->descent = descent;
759 glEnable(GL_ALPHA_TEST);
760 glEnable (GL_TEXTURE_2D);
762 check_gl_error ("loading font");
764 /* "Anistropic filtering helps for quadrilateral-angled textures.
765 A sharper image is accomplished by interpolating and filtering
766 multiple samples from one or more mipmaps to better approximate
767 very distorted textures. This is the next level of filtering
768 after trilinear filtering." */
770 strstr ((char *) glGetString(GL_EXTENSIONS),
771 "GL_EXT_texture_filter_anisotropic"))
773 GLfloat anisotropic = 0.0;
774 glGetFloatv (GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropic);
775 if (anisotropic >= 1.0)
776 glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,
782 font_height = GLUT_FONT->top - GLUT_FONT->bottom;
783 sc->char_width = glutStrokeWidth (GLUT_FONT, 'z'); /* 'n' seems wide */
787 sc->font_scale = 1.0 / sc->char_width;
790 /* We consider a font that consumes 80 columns to be "18 points".
792 If neither -size nor -columns was specified, default to 60 columns
793 (which is 24 points.)
795 If both were specified, -columns has priority.
801 if (target_columns <= 0 && font_size <= 0)
804 if (target_columns > 0)
805 font_size = base_size * (base_col / (double) target_columns);
806 else if (font_size > 0)
807 target_columns = base_col * (base_size / (double) font_size);
810 sc->line_pixel_width = target_columns * sc->char_width;
812 sc->font_scale /= target_columns;
813 sc->line_height = font_height * sc->font_scale;
815 /* Buffer only a few lines of text.
816 If the buffer is too big, there's a significant delay between
817 when the program launches and when the text appears, which can be
818 irritating for time-sensitive output (clock, current music, etc.)
820 I'd like to buffer only 2 lines, but we need to assume that we
821 could get a whole line of N-byte Unicrud, and if we fill the buffer
822 before hitting the end of the line, we stall.
824 sc->buf_size = target_columns * 2 * 4;
825 if (sc->buf_size < 80) sc->buf_size = 80;
826 sc->buf = (char *) calloc (1, sc->buf_size);
828 sc->total_lines = max_lines-1;
831 star_spin = -star_spin;
833 if (!alignment_str || !*alignment_str ||
834 !strcasecmp(alignment_str, "left"))
836 else if (!strcasecmp(alignment_str, "center") ||
837 !strcasecmp(alignment_str, "middle"))
839 else if (!strcasecmp(alignment_str, "right"))
844 "%s: alignment must be left, center, or right, not \"%s\"\n",
845 progname, alignment_str);
849 sc->tc = textclient_open (sc->dpy);
851 /* one more reshape, after line_height has been computed */
852 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
857 draw_stars (ModeInfo *mi)
859 sws_configuration *sc = &scs[MI_SCREEN(mi)];
861 glMatrixMode (GL_PROJECTION);
866 glMatrixMode (GL_MODELVIEW);
870 glOrtho (-0.5 * MI_WIDTH(mi), 0.5 * MI_WIDTH(mi),
871 -0.5 * MI_HEIGHT(mi), 0.5 * MI_HEIGHT(mi),
873 glRotatef (sc->star_theta, 0.0, 0.0, 1.0);
874 if (textures_p) glDisable (GL_TEXTURE_2D);
876 glCallList (sc->star_list);
877 if (textures_p) glEnable (GL_TEXTURE_2D);
881 glMatrixMode (GL_PROJECTION);
886 draw_sws (ModeInfo *mi)
888 sws_configuration *sc = &scs[MI_SCREEN(mi)];
889 /* XtAppContext app = XtDisplayToApplicationContext (sc->dpy);*/
890 Display *dpy = MI_DISPLAY(mi);
891 Window window = MI_WINDOW(mi);
894 if (!sc->glx_context)
897 glDrawBuffer (GL_BACK);
898 glXMakeCurrent (dpy, window, *sc->glx_context);
900 glClear (GL_COLOR_BUFFER_BIT);
904 glMatrixMode (GL_MODELVIEW);
908 /* Need to do this every time to get device rotation right */
909 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
916 if (textures_p) glDisable (GL_TEXTURE_2D);
918 glTranslatef (0,-1, 0);
920 glColor3f(1, 0, 0); /* Red line is where text appears */
922 glTranslatef(0, -0.028, 0);
925 glVertex3f(-0.5, 1, 0);
926 glVertex3f( 0.5, 1, 0);
927 glVertex3f(-0.5, -1, 0);
928 glVertex3f( 0.5, -1, 0);
933 glColor3f (0.2, 0.2, 0.2);
934 for (i = 0; i < 16; i++)
937 grid (1, 1, sc->char_width * sc->font_scale, sc->line_height, 0);
938 glTranslatef(0, 1, 0);
940 if (textures_p) glEnable (GL_TEXTURE_2D);
942 check_gl_error ("debug render");
945 /* Scroll to current position */
946 glTranslatef (0.0, sc->intra_line_scroll, 0.0);
948 glColor3f (1.0, 1.0, 0.4);
950 mi->polygon_count = 0;
953 glScalef (sc->font_scale, sc->font_scale, sc->font_scale);
954 for (i = 0; i < sc->total_lines; i++)
956 double fade = (fade_p ? 1.0 * i / sc->total_lines : 1.0);
957 int offscreen_lines = 2;
960 double y = ((sc->total_lines - (i + offscreen_lines) - 1)
963 char *line = sc->lines[i];
967 double xx = x * 1.4; /* a little more to the left */
969 sprintf(n, "%d:", i);
970 glColor3f (1.0, 1.0, 1.0);
971 draw_string (sc, xx / sc->font_scale, y / sc->font_scale, n);
977 if (sc->line_thickness != 1 && !textures_p)
979 int max_thick_lines = MAX_THICK_LINES;
980 GLfloat thinnest_line = 1.0;
981 GLfloat thickest_line = sc->line_thickness;
982 GLfloat range = thickest_line - thinnest_line;
985 int j = sc->total_lines - i - 1;
987 if (j > max_thick_lines)
988 thickness = thinnest_line;
990 thickness = (thinnest_line +
991 (range * ((max_thick_lines - j) /
992 (GLfloat) max_thick_lines)));
994 glLineWidth (thickness);
999 int n = sc->line_widths[i];
1000 xoff = 1.0 - (n * sc->font_scale);
1006 glColor3f (fade, fade, 0.5 * fade);
1007 draw_string (sc, (x + xoff) / sc->font_scale, y / sc->font_scale,
1010 mi->polygon_count += strlen (line);
1016 sc->intra_line_scroll += sc->line_height / scroll_steps;
1018 if (sc->intra_line_scroll >= sc->line_height)
1020 sc->intra_line_scroll = 0;
1022 /* Drop the oldest line off the end. */
1024 free (sc->lines[0]);
1026 /* Scroll the contents of the lines array toward 0. */
1027 if (sc->total_lines > 0)
1029 for (i = 1; i < sc->total_lines; i++) {
1030 sc->lines[i-1] = sc->lines[i];
1031 sc->line_widths[i-1] = sc->line_widths[i];
1033 sc->lines[--sc->total_lines] = 0;
1036 /* Bring in new lines at the end. */
1037 get_more_lines (sc);
1039 if (sc->total_lines < max_lines)
1040 /* Oops, we ran out of text... well, insert some blank lines
1041 here so that new text still pulls in from the bottom of
1042 the screen, isntead of just appearing. */
1043 sc->total_lines = max_lines;
1048 if (mi->fps_p) do_fps (mi);
1050 glXSwapBuffers(dpy, window);
1052 sc->star_theta += star_spin;
1056 free_sws (ModeInfo *mi)
1058 sws_configuration *sc = &scs[MI_SCREEN(mi)];
1060 if (!sc->glx_context) return;
1061 glXMakeCurrent (MI_DISPLAY(mi), MI_WINDOW(mi), *sc->glx_context);
1062 if (sc->tc) textclient_close (sc->tc);
1063 if (sc->texfont) free_texture_font (sc->texfont);
1064 if (sc->buf) free (sc->buf);
1065 if (sc->line_widths) free (sc->line_widths);
1066 for (i = 0; i < sc->total_lines; i++)
1067 if (sc->lines[i]) free (sc->lines[i]);
1068 if (sc->lines) free (sc->lines);
1069 if (glIsList(sc->star_list)) glDeleteLists(sc->star_list, 1);
1070 if (glIsList(sc->text_list)) glDeleteLists(sc->text_list, 1);
1075 __extension__ /* don't warn about "string length is greater than the length
1076 ISO C89 compilers are required to support" when including
1077 "starwars.txt" in the defaults... */
1080 XSCREENSAVER_MODULE_2 ("StarWars", starwars, sws)