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
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 release_sws 0
50 # define sws_handle_event xlockmore_no_events
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);
289 int target = sc->buf_size - sc->buf_tail - 2;
291 /* Fill as much as we can into sc->buf.
295 int c = textclient_getc (sc->tc);
298 sc->buf[sc->buf_tail++] = (char) c;
299 sc->buf[sc->buf_tail] = 0;
303 while (sc->total_lines < max_lines)
306 unsigned counter = 0;
308 /* OS X is really slow to calcuate the bounds for a line of text,
311 Really though, the right thing to do is probably to wrap
312 CTLineCreateTruncatedLine, one way or another. */
315 if (next_s >= sc->buf + sc->buf_tail)
321 sw_string_width2 (sc, sc->buf, next_s - sc->buf) >= wrap_pix)
324 counter = 12; /* <-- Adjust to taste. */
328 if (*next_s == '\r' || *next_s == '\n')
337 if (s >= sc->buf + sc->buf_tail)
338 /* Reached end of buffer before end of line. Bail. */
341 /* When checking pixel width, always measure the line from the
342 beginning, or else multi-byte UTF-8 characters, particularly
343 combining diacriticals, aren't measured right. */
345 if (*s == '\r' || *s == '\n' ||
346 (s > sc->buf && sw_string_width2 (sc, sc->buf, s - sc->buf) >= wrap_pix))
350 if (*s == '\r' || *s == '\n')
352 if (*s == '\r' && s[1] == '\n') /* swallow CRLF too */
359 /* We wrapped -- try to back up to the previous word boundary. */
362 while (s2 > sc->buf && *s2 != ' ' && *s2 != '\t')
372 sc->lines[sc->total_lines] = (char *) malloc (L+1);
373 memcpy (sc->lines[sc->total_lines], sc->buf, L);
374 sc->lines[sc->total_lines][L] = 0;
378 /* The GLUT font only has ASCII characters. */
379 char *s1 = utf8_to_latin1 (sc->lines[sc->total_lines], True);
380 free (sc->lines[sc->total_lines]);
381 sc->lines[sc->total_lines] = s1;
385 char *t = sc->lines[sc->total_lines];
386 char *ut = untabify (t);
387 strip (ut, (alignment == 0), 1); /* if centering, strip
388 leading whitespace too */
389 sc->lines[sc->total_lines] = ut;
393 sc->line_widths[sc->total_lines] =
394 sw_string_width(sc, sc->lines[sc->total_lines]);
398 if (sc->buf_tail > (s - sc->buf))
400 int i = sc->buf_tail - (s - sc->buf);
401 memmove (sc->buf, s, i);
403 sc->buf[sc->buf_tail] = 0;
410 sc->buf[sc->buf_tail] = 0;
425 draw_string (sws_configuration *sc, GLfloat x, GLfloat y, const char *s)
428 if (!s || !*s) return;
430 glTranslatef (x, y, 0);
433 print_texture_string (sc->texfont, s);
436 glutStrokeCharacter (GLUT_FONT, *s++);
441 GLfloat h = sc->line_height / sc->font_scale;
442 char **chars = utf8_split (os, 0);
444 char *s2 = (char *) malloc (strlen(s) + 1);
447 if (textures_p) glDisable (GL_TEXTURE_2D);
450 glColor3f (0.2, 0.2, 0.5);
454 for (i = 0; chars[i]; i++)
456 glVertex3f (x + w, y - sc->descent, 0); /* char left */
457 glVertex3f (x + w, y - sc->descent + h, 0);
458 strcat (s2, chars[i]);
459 w = sw_string_width (sc, s2);
463 glVertex3f (x + w, y - sc->descent, 0); /* char right */
464 glVertex3f (x + w, y - sc->descent + h, 0);
466 glVertex3f (x, y - sc->descent + h, 0); /* ascent */
467 glVertex3f (x + w, y - sc->descent + h, 0);
469 glVertex3f (x - sc->char_width, y, 0); /* baseline */
470 glVertex3f (x + sc->char_width + w, y, 0);
472 glVertex3f (x, y - sc->descent, 0); /* descent */
473 glVertex3f (x + w, y - sc->descent, 0);
480 if (textures_p) glEnable (GL_TEXTURE_2D);
486 grid (double width, double height, double xspacing, double yspacing, double z)
489 for (y = 0; y <= height/2; y += yspacing)
492 glVertex3f(-width/2, y, z);
493 glVertex3f( width/2, y, z);
494 glVertex3f(-width/2, -y, z);
495 glVertex3f( width/2, -y, z);
498 for (x = 0; x <= width/2; x += xspacing)
501 glVertex3f( x, -height/2, z);
502 glVertex3f( x, height/2, z);
503 glVertex3f(-x, -height/2, z);
504 glVertex3f(-x, height/2, z);
509 glVertex3f(-width, 0, z);
510 glVertex3f( width, 0, z);
511 glVertex3f(0, -height, z);
512 glVertex3f(0, height, z);
517 box (double width, double height, double depth)
519 glBegin(GL_LINE_LOOP);
520 glVertex3f(-width/2, -height/2, -depth/2);
521 glVertex3f(-width/2, height/2, -depth/2);
522 glVertex3f( width/2, height/2, -depth/2);
523 glVertex3f( width/2, -height/2, -depth/2);
525 glBegin(GL_LINE_LOOP);
526 glVertex3f(-width/2, -height/2, depth/2);
527 glVertex3f(-width/2, height/2, depth/2);
528 glVertex3f( width/2, height/2, depth/2);
529 glVertex3f( width/2, -height/2, depth/2);
531 glBegin(GL_LINE_LOOP);
532 glVertex3f(-width/2, -height/2, -depth/2);
533 glVertex3f(-width/2, -height/2, depth/2);
534 glVertex3f(-width/2, height/2, depth/2);
535 glVertex3f(-width/2, height/2, -depth/2);
537 glBegin(GL_LINE_LOOP);
538 glVertex3f( width/2, -height/2, -depth/2);
539 glVertex3f( width/2, -height/2, depth/2);
540 glVertex3f( width/2, height/2, depth/2);
541 glVertex3f( width/2, height/2, -depth/2);
545 glVertex3f(-width/2, height/2, depth/2);
546 glVertex3f(-width/2, -height/2, -depth/2);
548 glVertex3f( width/2, height/2, depth/2);
549 glVertex3f( width/2, -height/2, -depth/2);
551 glVertex3f(-width/2, -height/2, depth/2);
552 glVertex3f(-width/2, height/2, -depth/2);
554 glVertex3f( width/2, -height/2, depth/2);
555 glVertex3f( width/2, height/2, -depth/2);
560 /* Construct stars (number of stars is dependent on size of screen) */
562 init_stars (ModeInfo *mi, int width, int height)
564 sws_configuration *sc = &scs[MI_SCREEN(mi)];
566 int size = (width > height ? width : height);
567 int nstars = size * size / 320;
570 int steps = max_size / inc;
573 if (MI_WIDTH(mi) > 2560) { /* Retina displays */
575 nstars = (size/scale) * (size/scale) / 320;
578 glDeleteLists (sc->star_list, 1);
579 sc->star_list = glGenLists (1);
580 glNewList (sc->star_list, GL_COMPILE);
582 glEnable(GL_POINT_SMOOTH);
584 for (j = 1; j <= steps; j++)
586 glPointSize(inc * j * scale);
588 for (i = 0; i < nstars / steps; i++)
590 glColor3f (0.6 + frand(0.3),
593 glVertex2f (2 * size * (0.5 - frand(1.0)),
594 2 * size * (0.5 - frand(1.0)));
602 /* Window management, etc
605 reshape_sws (ModeInfo *mi, int width, int height)
607 sws_configuration *sc = &scs[MI_SCREEN(mi)];
609 /* Set up matrices for perspective text display
612 GLfloat desired_aspect = (GLfloat) 3/4;
613 int w = mi->xgwa.width;
614 int h = mi->xgwa.height;
616 GLfloat rot = current_device_rotation();
620 int h2 = w * desired_aspect;
621 yoff = (h - h2) / 2; /* Wide window: letterbox at top and bottom. */
622 if (yoff < 0) yoff = 0; /* Tall window: clip off the top. */
627 glViewport (0, yoff, w, h);
629 glMatrixMode (GL_PROJECTION);
632 glMatrixMode (GL_MODELVIEW);
634 gluPerspective (80.0, 1/desired_aspect, 1000, 55000);
635 gluLookAt (0.0, 0.0, 4600.0,
639 /* Horrible kludge to prevent the text from materializing already
640 on screen on iPhone in landscape mode.
642 if ((rot > 45 && rot < 135) ||
643 (rot < -45 && rot > -135))
649 glRotatef (-60.0, 1.0, 0.0, 0.0);
652 glRotatef (60.0, 1.0, 0.0, 0.0);
653 glTranslatef (260, 3200, 0);
654 glScalef (1.85, 1.85, 1);
657 /* The above gives us an arena where the bottom edge of the screen is
658 represented by the line (-2100,-3140,0) - ( 2100,-3140,0). */
660 /* Now let's move the origin to the front of the screen. */
661 glTranslatef (0.0, -3140, 0.0);
663 /* And then let's scale so that the bottom of the screen is 1.0 wide. */
664 glScalef (4200, 4200, 4200);
668 /* Compute the height in pixels of the line at the bottom of the screen. */
670 GLdouble mm[17], pm[17];
672 GLdouble x = 0.5, y1 = 0, z = 0;
673 GLdouble y2 = sc->line_height;
674 GLdouble wx=-1, wy1=-1, wy2=-1, wz=-1;
676 glGetDoublev (GL_MODELVIEW_MATRIX, mm);
677 glGetDoublev (GL_PROJECTION_MATRIX, pm);
678 glGetIntegerv (GL_VIEWPORT, vp);
679 gluProject (x, y1, z, mm, pm, vp, &wx, &wy1, &wz);
680 gluProject (x, y2, z, mm, pm, vp, &wx, &wy2, &wz);
681 sc->line_pixel_height = (wy2 - wy1);
685 /* Compute the best looking line thickness for the bottom line.
688 sc->line_thickness = 1.0;
690 sc->line_thickness = (GLfloat) sc->line_pixel_height / FONT_WEIGHT;
692 if (sc->line_thickness < 1.2)
693 sc->line_thickness = 1.0;
698 gl_init (ModeInfo *mi)
700 sws_configuration *sc = &scs[MI_SCREEN(mi)];
702 glDisable (GL_LIGHTING);
703 glDisable (GL_DEPTH_TEST);
707 glEnable (GL_LINE_SMOOTH);
708 glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
709 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
713 sc->text_list = glGenLists (1);
714 glNewList (sc->text_list, GL_COMPILE);
717 sc->star_list = glGenLists (1);
718 glNewList (sc->star_list, GL_COMPILE);
721 sc->line_thickness = 1.0;
726 init_sws (ModeInfo *mi)
730 sws_configuration *sc = 0;
734 sc = &scs[MI_SCREEN(mi)];
736 sc->dpy = MI_DISPLAY(mi);
737 sc = &scs[MI_SCREEN(mi)];
738 /* Unchecked malloc. :( */
739 sc->lines = (char **) calloc (max_lines+1, sizeof(char *));
740 sc->line_widths = (int *) calloc (max_lines+1, sizeof(int));
742 if ((sc->glx_context = init_GL(mi)) != NULL) {
744 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
745 clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */
747 init_stars (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
753 int cw, ascent, descent;
754 sc->texfont = load_texture_font (MI_DISPLAY(mi), "font");
755 texture_string_metrics (sc->texfont, "n", &e, &ascent, &descent);
758 font_height = ascent + descent;
759 sc->descent = descent;
760 glEnable(GL_ALPHA_TEST);
761 glEnable (GL_TEXTURE_2D);
763 check_gl_error ("loading font");
765 /* "Anistropic filtering helps for quadrilateral-angled textures.
766 A sharper image is accomplished by interpolating and filtering
767 multiple samples from one or more mipmaps to better approximate
768 very distorted textures. This is the next level of filtering
769 after trilinear filtering." */
771 strstr ((char *) glGetString(GL_EXTENSIONS),
772 "GL_EXT_texture_filter_anisotropic"))
774 GLfloat anisotropic = 0.0;
775 glGetFloatv (GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropic);
776 if (anisotropic >= 1.0)
777 glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,
783 font_height = GLUT_FONT->top - GLUT_FONT->bottom;
784 sc->char_width = glutStrokeWidth (GLUT_FONT, 'z'); /* 'n' seems wide */
788 sc->font_scale = 1.0 / sc->char_width;
791 /* We consider a font that consumes 80 columns to be "18 points".
793 If neither -size nor -columns was specified, default to 60 columns
794 (which is 24 points.)
796 If both were specified, -columns has priority.
802 if (target_columns <= 0 && font_size <= 0)
805 if (target_columns > 0)
806 font_size = base_size * (base_col / (double) target_columns);
807 else if (font_size > 0)
808 target_columns = base_col * (base_size / (double) font_size);
811 sc->line_pixel_width = target_columns * sc->char_width;
813 sc->font_scale /= target_columns;
814 sc->line_height = font_height * sc->font_scale;
816 /* Buffer only a few lines of text.
817 If the buffer is too big, there's a significant delay between
818 when the program launches and when the text appears, which can be
819 irritating for time-sensitive output (clock, current music, etc.)
821 I'd like to buffer only 2 lines, but we need to assume that we
822 could get a whole line of N-byte Unicrud, and if we fill the buffer
823 before hitting the end of the line, we stall.
825 sc->buf_size = target_columns * 2 * 4;
826 if (sc->buf_size < 80) sc->buf_size = 80;
827 sc->buf = (char *) calloc (1, sc->buf_size);
829 sc->total_lines = max_lines-1;
832 star_spin = -star_spin;
834 if (!alignment_str || !*alignment_str ||
835 !strcasecmp(alignment_str, "left"))
837 else if (!strcasecmp(alignment_str, "center") ||
838 !strcasecmp(alignment_str, "middle"))
840 else if (!strcasecmp(alignment_str, "right"))
845 "%s: alignment must be left, center, or right, not \"%s\"\n",
846 progname, alignment_str);
850 sc->tc = textclient_open (sc->dpy);
852 /* one more reshape, after line_height has been computed */
853 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
858 draw_stars (ModeInfo *mi)
860 sws_configuration *sc = &scs[MI_SCREEN(mi)];
862 glMatrixMode (GL_PROJECTION);
867 glMatrixMode (GL_MODELVIEW);
871 glOrtho (-0.5 * MI_WIDTH(mi), 0.5 * MI_WIDTH(mi),
872 -0.5 * MI_HEIGHT(mi), 0.5 * MI_HEIGHT(mi),
874 glRotatef (sc->star_theta, 0.0, 0.0, 1.0);
875 if (textures_p) glDisable (GL_TEXTURE_2D);
877 glCallList (sc->star_list);
878 if (textures_p) glEnable (GL_TEXTURE_2D);
882 glMatrixMode (GL_PROJECTION);
887 draw_sws (ModeInfo *mi)
889 sws_configuration *sc = &scs[MI_SCREEN(mi)];
890 /* XtAppContext app = XtDisplayToApplicationContext (sc->dpy);*/
891 Display *dpy = MI_DISPLAY(mi);
892 Window window = MI_WINDOW(mi);
895 if (!sc->glx_context)
898 glDrawBuffer (GL_BACK);
899 glXMakeCurrent (dpy, window, *(sc->glx_context));
901 glClear (GL_COLOR_BUFFER_BIT);
905 glMatrixMode (GL_MODELVIEW);
909 /* Need to do this every time to get device rotation right */
910 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
917 if (textures_p) glDisable (GL_TEXTURE_2D);
919 glTranslatef (0,-1, 0);
921 glColor3f(1, 0, 0); /* Red line is where text appears */
923 glTranslatef(0, -0.028, 0);
926 glVertex3f(-0.5, 1, 0);
927 glVertex3f( 0.5, 1, 0);
928 glVertex3f(-0.5, -1, 0);
929 glVertex3f( 0.5, -1, 0);
934 glColor3f (0.2, 0.2, 0.2);
935 for (i = 0; i < 16; i++)
938 grid (1, 1, sc->char_width * sc->font_scale, sc->line_height, 0);
939 glTranslatef(0, 1, 0);
941 if (textures_p) glEnable (GL_TEXTURE_2D);
943 check_gl_error ("debug render");
946 /* Scroll to current position */
947 glTranslatef (0.0, sc->intra_line_scroll, 0.0);
949 glColor3f (1.0, 1.0, 0.4);
951 mi->polygon_count = 0;
954 glScalef (sc->font_scale, sc->font_scale, sc->font_scale);
955 for (i = 0; i < sc->total_lines; i++)
957 double fade = (fade_p ? 1.0 * i / sc->total_lines : 1.0);
958 int offscreen_lines = 2;
961 double y = ((sc->total_lines - (i + offscreen_lines) - 1)
964 char *line = sc->lines[i];
968 double xx = x * 1.4; /* a little more to the left */
970 sprintf(n, "%d:", i);
971 glColor3f (1.0, 1.0, 1.0);
972 draw_string (sc, xx / sc->font_scale, y / sc->font_scale, n);
978 if (sc->line_thickness != 1 && !textures_p)
980 int max_thick_lines = MAX_THICK_LINES;
981 GLfloat thinnest_line = 1.0;
982 GLfloat thickest_line = sc->line_thickness;
983 GLfloat range = thickest_line - thinnest_line;
986 int j = sc->total_lines - i - 1;
988 if (j > max_thick_lines)
989 thickness = thinnest_line;
991 thickness = (thinnest_line +
992 (range * ((max_thick_lines - j) /
993 (GLfloat) max_thick_lines)));
995 glLineWidth (thickness);
1000 int n = sc->line_widths[i];
1001 xoff = 1.0 - (n * sc->font_scale);
1007 glColor3f (fade, fade, 0.5 * fade);
1008 draw_string (sc, (x + xoff) / sc->font_scale, y / sc->font_scale,
1011 mi->polygon_count += strlen (line);
1017 sc->intra_line_scroll += sc->line_height / scroll_steps;
1019 if (sc->intra_line_scroll >= sc->line_height)
1021 sc->intra_line_scroll = 0;
1023 /* Drop the oldest line off the end. */
1025 free (sc->lines[0]);
1027 /* Scroll the contents of the lines array toward 0. */
1028 if (sc->total_lines > 0)
1030 for (i = 1; i < sc->total_lines; i++) {
1031 sc->lines[i-1] = sc->lines[i];
1032 sc->line_widths[i-1] = sc->line_widths[i];
1034 sc->lines[--sc->total_lines] = 0;
1037 /* Bring in new lines at the end. */
1038 get_more_lines (sc);
1040 if (sc->total_lines < max_lines)
1041 /* Oops, we ran out of text... well, insert some blank lines
1042 here so that new text still pulls in from the bottom of
1043 the screen, isntead of just appearing. */
1044 sc->total_lines = max_lines;
1049 if (mi->fps_p) do_fps (mi);
1051 glXSwapBuffers(dpy, window);
1053 sc->star_theta += star_spin;
1057 free_sws (ModeInfo *mi)
1059 sws_configuration *sc = &scs[MI_SCREEN(mi)];
1061 textclient_close (sc->tc);
1063 /* #### there's more to free here */
1068 __extension__ /* don't warn about "string length is greater than the length
1069 ISO C89 compilers are required to support" when including
1070 "starwars.txt" in the defaults... */
1073 XSCREENSAVER_MODULE_2 ("StarWars", starwars, sws)