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);
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;
572 glDeleteLists (sc->star_list, 1);
573 sc->star_list = glGenLists (1);
574 glNewList (sc->star_list, GL_COMPILE);
576 glEnable(GL_POINT_SMOOTH);
578 for (j = 1; j <= steps; j++)
580 glPointSize(inc * j);
582 for (i = 0; i < nstars / steps; i++)
584 glColor3f (0.6 + frand(0.3),
587 glVertex2f (2 * size * (0.5 - frand(1.0)),
588 2 * size * (0.5 - frand(1.0)));
596 /* Window management, etc
599 reshape_sws (ModeInfo *mi, int width, int height)
601 sws_configuration *sc = &scs[MI_SCREEN(mi)];
603 /* Set up matrices for perspective text display
606 GLfloat desired_aspect = (GLfloat) 3/4;
607 int w = mi->xgwa.width;
608 int h = mi->xgwa.height;
610 GLfloat rot = current_device_rotation();
614 int h2 = w * desired_aspect;
615 yoff = (h - h2) / 2; /* Wide window: letterbox at top and bottom. */
616 if (yoff < 0) yoff = 0; /* Tall window: clip off the top. */
621 glViewport (0, yoff, w, h);
623 glMatrixMode (GL_PROJECTION);
626 glMatrixMode (GL_MODELVIEW);
628 gluPerspective (80.0, 1/desired_aspect, 1000, 55000);
629 gluLookAt (0.0, 0.0, 4600.0,
633 /* Horrible kludge to prevent the text from materializing already
634 on screen on iPhone in landscape mode.
636 if ((rot > 45 && rot < 135) ||
637 (rot < -45 && rot > -135))
643 glRotatef (-60.0, 1.0, 0.0, 0.0);
646 glRotatef (60.0, 1.0, 0.0, 0.0);
647 glTranslatef (260, 3200, 0);
648 glScalef (1.85, 1.85, 1);
651 /* The above gives us an arena where the bottom edge of the screen is
652 represented by the line (-2100,-3140,0) - ( 2100,-3140,0). */
654 /* Now let's move the origin to the front of the screen. */
655 glTranslatef (0.0, -3140, 0.0);
657 /* And then let's scale so that the bottom of the screen is 1.0 wide. */
658 glScalef (4200, 4200, 4200);
662 /* Compute the height in pixels of the line at the bottom of the screen. */
664 GLdouble mm[17], pm[17];
666 GLdouble x = 0.5, y1 = 0, z = 0;
667 GLdouble y2 = sc->line_height;
668 GLdouble wx=-1, wy1=-1, wy2=-1, wz=-1;
670 glGetDoublev (GL_MODELVIEW_MATRIX, mm);
671 glGetDoublev (GL_PROJECTION_MATRIX, pm);
672 glGetIntegerv (GL_VIEWPORT, vp);
673 gluProject (x, y1, z, mm, pm, vp, &wx, &wy1, &wz);
674 gluProject (x, y2, z, mm, pm, vp, &wx, &wy2, &wz);
675 sc->line_pixel_height = (wy2 - wy1);
679 /* Compute the best looking line thickness for the bottom line.
682 sc->line_thickness = 1.0;
684 sc->line_thickness = (GLfloat) sc->line_pixel_height / FONT_WEIGHT;
686 if (sc->line_thickness < 1.2)
687 sc->line_thickness = 1.0;
692 gl_init (ModeInfo *mi)
694 sws_configuration *sc = &scs[MI_SCREEN(mi)];
696 glDisable (GL_LIGHTING);
697 glDisable (GL_DEPTH_TEST);
701 glEnable (GL_LINE_SMOOTH);
702 glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
703 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
707 sc->text_list = glGenLists (1);
708 glNewList (sc->text_list, GL_COMPILE);
711 sc->star_list = glGenLists (1);
712 glNewList (sc->star_list, GL_COMPILE);
715 sc->line_thickness = 1.0;
720 init_sws (ModeInfo *mi)
724 sws_configuration *sc = 0;
727 scs = (sws_configuration *)
728 calloc (MI_NUM_SCREENS(mi), sizeof (sws_configuration));
730 fprintf(stderr, "%s: out of memory\n", progname);
735 sc = &scs[MI_SCREEN(mi)];
737 sc->dpy = MI_DISPLAY(mi);
738 sc = &scs[MI_SCREEN(mi)];
739 /* Unchecked malloc. :( */
740 sc->lines = (char **) calloc (max_lines+1, sizeof(char *));
741 sc->line_widths = (int *) calloc (max_lines+1, sizeof(int));
743 if ((sc->glx_context = init_GL(mi)) != NULL) {
745 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
746 clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */
748 init_stars (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
754 int cw, ascent, descent;
755 sc->texfont = load_texture_font (MI_DISPLAY(mi), "font");
756 texture_string_metrics (sc->texfont, "n", &e, &ascent, &descent);
759 font_height = ascent + descent;
760 sc->descent = descent;
761 glEnable(GL_ALPHA_TEST);
762 glEnable (GL_TEXTURE_2D);
764 check_gl_error ("loading font");
766 /* "Anistropic filtering helps for quadrilateral-angled textures.
767 A sharper image is accomplished by interpolating and filtering
768 multiple samples from one or more mipmaps to better approximate
769 very distorted textures. This is the next level of filtering
770 after trilinear filtering." */
772 strstr ((char *) glGetString(GL_EXTENSIONS),
773 "GL_EXT_texture_filter_anisotropic"))
775 GLfloat anisotropic = 0.0;
776 glGetFloatv (GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropic);
777 if (anisotropic >= 1.0)
778 glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,
784 font_height = GLUT_FONT->top - GLUT_FONT->bottom;
785 sc->char_width = glutStrokeWidth (GLUT_FONT, 'z'); /* 'n' seems wide */
789 sc->font_scale = 1.0 / sc->char_width;
792 /* We consider a font that consumes 80 columns to be "18 points".
794 If neither -size nor -columns was specified, default to 60 columns
795 (which is 24 points.)
797 If both were specified, -columns has priority.
803 if (target_columns <= 0 && font_size <= 0)
806 if (target_columns > 0)
807 font_size = base_size * (base_col / (double) target_columns);
808 else if (font_size > 0)
809 target_columns = base_col * (base_size / (double) font_size);
812 sc->line_pixel_width = target_columns * sc->char_width;
814 sc->font_scale /= target_columns;
815 sc->line_height = font_height * sc->font_scale;
817 /* Buffer only a few lines of text.
818 If the buffer is too big, there's a significant delay between
819 when the program launches and when the text appears, which can be
820 irritating for time-sensitive output (clock, current music, etc.)
822 I'd like to buffer only 2 lines, but we need to assume that we
823 could get a whole line of N-byte Unicrud, and if we fill the buffer
824 before hitting the end of the line, we stall.
826 sc->buf_size = target_columns * 2 * 4;
827 if (sc->buf_size < 80) sc->buf_size = 80;
828 sc->buf = (char *) calloc (1, sc->buf_size);
830 sc->total_lines = max_lines-1;
833 star_spin = -star_spin;
835 if (!alignment_str || !*alignment_str ||
836 !strcasecmp(alignment_str, "left"))
838 else if (!strcasecmp(alignment_str, "center") ||
839 !strcasecmp(alignment_str, "middle"))
841 else if (!strcasecmp(alignment_str, "right"))
846 "%s: alignment must be left, center, or right, not \"%s\"\n",
847 progname, alignment_str);
851 sc->tc = textclient_open (sc->dpy);
853 /* one more reshape, after line_height has been computed */
854 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
859 draw_stars (ModeInfo *mi)
861 sws_configuration *sc = &scs[MI_SCREEN(mi)];
863 glMatrixMode (GL_PROJECTION);
868 glMatrixMode (GL_MODELVIEW);
872 glOrtho (-0.5 * MI_WIDTH(mi), 0.5 * MI_WIDTH(mi),
873 -0.5 * MI_HEIGHT(mi), 0.5 * MI_HEIGHT(mi),
875 glRotatef (sc->star_theta, 0.0, 0.0, 1.0);
876 if (textures_p) glDisable (GL_TEXTURE_2D);
878 glCallList (sc->star_list);
879 if (textures_p) glEnable (GL_TEXTURE_2D);
883 glMatrixMode (GL_PROJECTION);
888 draw_sws (ModeInfo *mi)
890 sws_configuration *sc = &scs[MI_SCREEN(mi)];
891 /* XtAppContext app = XtDisplayToApplicationContext (sc->dpy);*/
892 Display *dpy = MI_DISPLAY(mi);
893 Window window = MI_WINDOW(mi);
896 if (!sc->glx_context)
899 glDrawBuffer (GL_BACK);
900 glXMakeCurrent (dpy, window, *(sc->glx_context));
902 glClear (GL_COLOR_BUFFER_BIT);
906 glMatrixMode (GL_MODELVIEW);
910 /* Need to do this every time to get device rotation right */
911 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
918 if (textures_p) glDisable (GL_TEXTURE_2D);
920 glTranslatef (0,-1, 0);
922 glColor3f(1, 0, 0); /* Red line is where text appears */
924 glTranslatef(0, -0.028, 0);
927 glVertex3f(-0.5, 1, 0);
928 glVertex3f( 0.5, 1, 0);
929 glVertex3f(-0.5, -1, 0);
930 glVertex3f( 0.5, -1, 0);
935 glColor3f (0.2, 0.2, 0.2);
936 for (i = 0; i < 16; i++)
939 grid (1, 1, sc->char_width * sc->font_scale, sc->line_height, 0);
940 glTranslatef(0, 1, 0);
942 if (textures_p) glEnable (GL_TEXTURE_2D);
944 check_gl_error ("debug render");
947 /* Scroll to current position */
948 glTranslatef (0.0, sc->intra_line_scroll, 0.0);
950 glColor3f (1.0, 1.0, 0.4);
952 mi->polygon_count = 0;
955 glScalef (sc->font_scale, sc->font_scale, sc->font_scale);
956 for (i = 0; i < sc->total_lines; i++)
958 double fade = (fade_p ? 1.0 * i / sc->total_lines : 1.0);
959 int offscreen_lines = 2;
962 double y = ((sc->total_lines - (i + offscreen_lines) - 1)
965 char *line = sc->lines[i];
969 double xx = x * 1.4; /* a little more to the left */
971 sprintf(n, "%d:", i);
972 glColor3f (1.0, 1.0, 1.0);
973 draw_string (sc, xx / sc->font_scale, y / sc->font_scale, n);
979 if (sc->line_thickness != 1 && !textures_p)
981 int max_thick_lines = MAX_THICK_LINES;
982 GLfloat thinnest_line = 1.0;
983 GLfloat thickest_line = sc->line_thickness;
984 GLfloat range = thickest_line - thinnest_line;
987 int j = sc->total_lines - i - 1;
989 if (j > max_thick_lines)
990 thickness = thinnest_line;
992 thickness = (thinnest_line +
993 (range * ((max_thick_lines - j) /
994 (GLfloat) max_thick_lines)));
996 glLineWidth (thickness);
1001 int n = sc->line_widths[i];
1002 xoff = 1.0 - (n * sc->font_scale);
1008 glColor3f (fade, fade, 0.5 * fade);
1009 draw_string (sc, (x + xoff) / sc->font_scale, y / sc->font_scale,
1012 mi->polygon_count += strlen (line);
1018 sc->intra_line_scroll += sc->line_height / scroll_steps;
1020 if (sc->intra_line_scroll >= sc->line_height)
1022 sc->intra_line_scroll = 0;
1024 /* Drop the oldest line off the end. */
1026 free (sc->lines[0]);
1028 /* Scroll the contents of the lines array toward 0. */
1029 if (sc->total_lines > 0)
1031 for (i = 1; i < sc->total_lines; i++) {
1032 sc->lines[i-1] = sc->lines[i];
1033 sc->line_widths[i-1] = sc->line_widths[i];
1035 sc->lines[--sc->total_lines] = 0;
1038 /* Bring in new lines at the end. */
1039 get_more_lines (sc);
1041 if (sc->total_lines < max_lines)
1042 /* Oops, we ran out of text... well, insert some blank lines
1043 here so that new text still pulls in from the bottom of
1044 the screen, isntead of just appearing. */
1045 sc->total_lines = max_lines;
1050 if (mi->fps_p) do_fps (mi);
1052 glXSwapBuffers(dpy, window);
1054 sc->star_theta += star_spin;
1058 release_sws (ModeInfo *mi)
1062 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
1063 sws_configuration *sc = &scs[screen];
1065 textclient_close (sc->tc);
1067 /* #### there's more to free here */
1077 __extension__ /* don't warn about "string length is greater than the length
1078 ISO C89 compilers are required to support" when including
1079 "starwars.txt" in the defaults... */
1082 XSCREENSAVER_MODULE_2 ("StarWars", starwars, sws)