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 release_sws 0
51 # define sws_handle_event 0
53 #define countof(x) (sizeof((x))/sizeof((*x)))
55 #include "xlockmore.h"
56 #include "textclient.h"
59 #ifdef USE_GL /* whole file */
61 /* Should be in <GL/glext.h> */
62 # ifndef GL_TEXTURE_MAX_ANISOTROPY_EXT
63 # define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
65 # ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT
66 # define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
70 #define DEF_LINES "125"
71 #define DEF_STEPS "35"
72 #define DEF_SPIN "0.03"
74 #define DEF_COLUMNS "-1"
75 #define DEF_LINE_WRAP "True"
76 #define DEF_ALIGNMENT "Center"
77 #define DEF_SMOOTH "True"
78 #define DEF_THICK "True"
79 #define DEF_FADE "True"
80 #define DEF_TEXTURES "True"
81 #define DEF_DEBUG "False"
83 #define DEF_FONT "-*-utopia-bold-r-normal-*-*-360-*-*-*-*-*-*"
87 #define MAX_THICK_LINES 25
88 #define FONT_WEIGHT 14
91 # define KEEP_ASPECT /* Letterboxing looks dumb on iPhone. */
95 #include "glutstroke.h"
96 #include "glut_roman.h"
97 #define GLUT_FONT (&glutStrokeRoman)
101 GLXContext *glx_context;
103 GLuint text_list, star_list;
104 texture_font_data *texfont;
120 double intra_line_scroll;
122 int line_pixel_width; /* in font units (for wrapping text) */
123 int line_pixel_height; /* in screen units (for computing line thickness) */
124 GLfloat line_thickness;
129 static sws_configuration *scs = NULL;
131 static int max_lines;
132 static int scroll_steps;
133 static float star_spin;
134 static float font_size;
135 static int target_columns;
140 static int textures_p;
142 static char *alignment_str;
143 static int alignment;
145 static XrmOptionDescRec opts[] = {
146 {"-lines", ".lines", XrmoptionSepArg, 0 },
147 {"-steps", ".steps", XrmoptionSepArg, 0 },
148 {"-spin", ".spin", XrmoptionSepArg, 0 },
149 {"-size", ".size", XrmoptionSepArg, 0 },
150 {"-columns", ".columns", XrmoptionSepArg, 0 },
151 /*{"-font", ".font", XrmoptionSepArg, 0 },*/
152 {"-fade", ".fade", XrmoptionNoArg, "True" },
153 {"-no-fade", ".fade", XrmoptionNoArg, "False" },
154 {"-textures", ".textures", XrmoptionNoArg, "True" },
155 {"-smooth", ".smooth", XrmoptionNoArg, "True" },
156 {"-no-smooth", ".smooth", XrmoptionNoArg, "False" },
157 {"-thick", ".thick", XrmoptionNoArg, "True" },
158 {"-no-thick", ".thick", XrmoptionNoArg, "False" },
159 {"-no-textures", ".textures", XrmoptionNoArg, "False" },
160 {"-wrap", ".lineWrap", XrmoptionNoArg, "True" },
161 {"-no-wrap", ".lineWrap", XrmoptionNoArg, "False" },
162 {"-nowrap", ".lineWrap", XrmoptionNoArg, "False" },
163 {"-alignment", ".alignment", XrmoptionSepArg, 0 },
164 {"-left", ".alignment", XrmoptionNoArg, "Left" },
165 {"-right", ".alignment", XrmoptionNoArg, "Right" },
166 {"-center", ".alignment", XrmoptionNoArg, "Center" },
167 {"-debug", ".debug", XrmoptionNoArg, "True" },
170 static argtype vars[] = {
171 {&max_lines, "lines", "Integer", DEF_LINES, t_Int},
172 {&scroll_steps, "steps", "Integer", DEF_STEPS, t_Int},
173 {&star_spin, "spin", "Float", DEF_SPIN, t_Float},
174 {&font_size, "size", "Float", DEF_SIZE, t_Float},
175 {&target_columns, "columns", "Integer", DEF_COLUMNS, t_Int},
176 {&wrap_p, "lineWrap", "Boolean", DEF_LINE_WRAP, t_Bool},
177 {&alignment_str, "alignment", "Alignment", DEF_ALIGNMENT, t_String},
178 {&smooth_p, "smooth", "Boolean", DEF_SMOOTH, t_Bool},
179 {&thick_p, "thick", "Boolean", DEF_THICK, t_Bool},
180 {&fade_p, "fade", "Boolean", DEF_FADE, t_Bool},
181 {&textures_p, "textures", "Boolean", DEF_TEXTURES, t_Bool},
182 {&debug_p, "debug", "Boolean", DEF_DEBUG, t_Bool},
185 ENTRYPOINT ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
189 /* Tabs are bad, mmmkay? */
192 untabify (const char *string)
194 const char *ostring = string;
195 char *result = (char *) malloc ((strlen(string) * 8) + 1);
205 } while (col % TAB_WIDTH);
208 else if (*string == '\r' || *string == '\n')
213 else if (*string == '\010') /* backspace */
215 if (string > ostring)
230 strip (char *s, Bool leading, Bool trailing)
234 while (L > 0 && (s[L-1] == ' ' || s[L-1] == '\t'))
239 while (*s2 == ' ' || *s2 == '\t')
251 sw_string_width (sws_configuration *sc, const char *s)
256 texture_string_metrics (sc->texfont, s, &e, 0, 0);
260 return glutStrokeLength (GLUT_FONT, (unsigned char *) s);
265 sw_string_width2 (sws_configuration *sc, const char *s, size_t size)
267 char *s2 = (char *) malloc (size + 1);
270 strncpy (s2, s, size);
273 result = sw_string_width (sc, s2);
280 /* Populates the sc->lines list with as many lines as possible.
283 get_more_lines (sws_configuration *sc)
285 /* wrap anyway, if it's absurdly long. */
286 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;
426 draw_string (sws_configuration *sc, GLfloat x, GLfloat y, const char *s)
429 if (!s || !*s) return;
431 glTranslatef (x, y, 0);
434 print_texture_string (sc->texfont, s);
437 glutStrokeCharacter (GLUT_FONT, *s++);
442 GLfloat h = sc->line_height / sc->font_scale;
443 char **chars = utf8_split (os, 0);
445 char *s2 = (char *) malloc (strlen(s) + 1);
448 if (textures_p) glDisable (GL_TEXTURE_2D);
451 glColor3f (0.2, 0.2, 0.5);
455 for (i = 0; chars[i]; i++)
457 glVertex3f (x + w, y - sc->descent, 0); /* char left */
458 glVertex3f (x + w, y - sc->descent + h, 0);
459 strcat (s2, chars[i]);
460 w = sw_string_width (sc, s2);
464 glVertex3f (x + w, y - sc->descent, 0); /* char right */
465 glVertex3f (x + w, y - sc->descent + h, 0);
467 glVertex3f (x, y - sc->descent + h, 0); /* ascent */
468 glVertex3f (x + w, y - sc->descent + h, 0);
470 glVertex3f (x - sc->char_width, y, 0); /* baseline */
471 glVertex3f (x + sc->char_width + w, y, 0);
473 glVertex3f (x, y - sc->descent, 0); /* descent */
474 glVertex3f (x + w, y - sc->descent, 0);
481 if (textures_p) glEnable (GL_TEXTURE_2D);
487 grid (double width, double height, double xspacing, double yspacing, double z)
490 for (y = 0; y <= height/2; y += yspacing)
493 glVertex3f(-width/2, y, z);
494 glVertex3f( width/2, y, z);
495 glVertex3f(-width/2, -y, z);
496 glVertex3f( width/2, -y, z);
499 for (x = 0; x <= width/2; x += xspacing)
502 glVertex3f( x, -height/2, z);
503 glVertex3f( x, height/2, z);
504 glVertex3f(-x, -height/2, z);
505 glVertex3f(-x, height/2, z);
510 glVertex3f(-width, 0, z);
511 glVertex3f( width, 0, z);
512 glVertex3f(0, -height, z);
513 glVertex3f(0, height, z);
518 box (double width, double height, double depth)
520 glBegin(GL_LINE_LOOP);
521 glVertex3f(-width/2, -height/2, -depth/2);
522 glVertex3f(-width/2, height/2, -depth/2);
523 glVertex3f( width/2, height/2, -depth/2);
524 glVertex3f( width/2, -height/2, -depth/2);
526 glBegin(GL_LINE_LOOP);
527 glVertex3f(-width/2, -height/2, depth/2);
528 glVertex3f(-width/2, height/2, depth/2);
529 glVertex3f( width/2, height/2, depth/2);
530 glVertex3f( width/2, -height/2, depth/2);
532 glBegin(GL_LINE_LOOP);
533 glVertex3f(-width/2, -height/2, -depth/2);
534 glVertex3f(-width/2, -height/2, depth/2);
535 glVertex3f(-width/2, height/2, depth/2);
536 glVertex3f(-width/2, height/2, -depth/2);
538 glBegin(GL_LINE_LOOP);
539 glVertex3f( width/2, -height/2, -depth/2);
540 glVertex3f( width/2, -height/2, depth/2);
541 glVertex3f( width/2, height/2, depth/2);
542 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);
555 glVertex3f( width/2, -height/2, depth/2);
556 glVertex3f( width/2, height/2, -depth/2);
561 /* Construct stars (number of stars is dependent on size of screen) */
563 init_stars (ModeInfo *mi, int width, int height)
565 sws_configuration *sc = &scs[MI_SCREEN(mi)];
567 int size = (width > height ? width : height);
568 int nstars = size * size / 320;
571 int steps = max_size / inc;
573 glDeleteLists (sc->star_list, 1);
574 sc->star_list = glGenLists (1);
575 glNewList (sc->star_list, GL_COMPILE);
577 glEnable(GL_POINT_SMOOTH);
579 for (j = 1; j <= steps; j++)
581 glPointSize(inc * j);
583 for (i = 0; i < nstars / steps; i++)
585 glColor3f (0.6 + frand(0.3),
588 glVertex2f (2 * size * (0.5 - frand(1.0)),
589 2 * size * (0.5 - frand(1.0)));
597 /* Window management, etc
600 reshape_sws (ModeInfo *mi, int width, int height)
602 sws_configuration *sc = &scs[MI_SCREEN(mi)];
604 /* Set up matrices for perspective text display
607 GLfloat desired_aspect = (GLfloat) 3/4;
608 int w = mi->xgwa.width;
609 int h = mi->xgwa.height;
611 GLfloat rot = current_device_rotation();
615 int h2 = w * desired_aspect;
616 yoff = (h - h2) / 2; /* Wide window: letterbox at top and bottom. */
617 if (yoff < 0) yoff = 0; /* Tall window: clip off the top. */
622 glViewport (0, yoff, w, h);
624 glMatrixMode (GL_PROJECTION);
627 glMatrixMode (GL_MODELVIEW);
629 gluPerspective (80.0, 1/desired_aspect, 1000, 55000);
630 gluLookAt (0.0, 0.0, 4600.0,
634 /* Horrible kludge to prevent the text from materializing already
635 on screen on iPhone in landscape mode.
637 if ((rot > 45 && rot < 135) ||
638 (rot < -45 && rot > -135))
644 glRotatef (-60.0, 1.0, 0.0, 0.0);
647 glRotatef (60.0, 1.0, 0.0, 0.0);
648 glTranslatef (260, 3200, 0);
649 glScalef (1.85, 1.85, 1);
652 /* The above gives us an arena where the bottom edge of the screen is
653 represented by the line (-2100,-3140,0) - ( 2100,-3140,0). */
655 /* Now let's move the origin to the front of the screen. */
656 glTranslatef (0.0, -3140, 0.0);
658 /* And then let's scale so that the bottom of the screen is 1.0 wide. */
659 glScalef (4200, 4200, 4200);
663 /* Compute the height in pixels of the line at the bottom of the screen. */
665 GLdouble mm[17], pm[17];
667 GLdouble x = 0.5, y1 = 0, z = 0;
668 GLdouble y2 = sc->line_height;
669 GLdouble wx=-1, wy1=-1, wy2=-1, wz=-1;
671 glGetDoublev (GL_MODELVIEW_MATRIX, mm);
672 glGetDoublev (GL_PROJECTION_MATRIX, pm);
673 glGetIntegerv (GL_VIEWPORT, vp);
674 gluProject (x, y1, z, mm, pm, vp, &wx, &wy1, &wz);
675 gluProject (x, y2, z, mm, pm, vp, &wx, &wy2, &wz);
676 sc->line_pixel_height = (wy2 - wy1);
680 /* Compute the best looking line thickness for the bottom line.
683 sc->line_thickness = 1.0;
685 sc->line_thickness = (GLfloat) sc->line_pixel_height / FONT_WEIGHT;
687 if (sc->line_thickness < 1.2)
688 sc->line_thickness = 1.0;
693 gl_init (ModeInfo *mi)
695 sws_configuration *sc = &scs[MI_SCREEN(mi)];
697 glDisable (GL_LIGHTING);
698 glDisable (GL_DEPTH_TEST);
702 glEnable (GL_LINE_SMOOTH);
703 glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
704 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
708 sc->text_list = glGenLists (1);
709 glNewList (sc->text_list, GL_COMPILE);
712 sc->star_list = glGenLists (1);
713 glNewList (sc->star_list, GL_COMPILE);
716 sc->line_thickness = 1.0;
720 static void free_sws (ModeInfo *mi);
724 init_sws (ModeInfo *mi)
728 sws_configuration *sc = 0;
730 MI_INIT (mi, scs, free_sws);
732 sc = &scs[MI_SCREEN(mi)];
734 sc->dpy = MI_DISPLAY(mi);
735 sc = &scs[MI_SCREEN(mi)];
736 /* Unchecked malloc. :( */
737 sc->lines = (char **) calloc (max_lines+1, sizeof(char *));
738 sc->line_widths = (int *) calloc (max_lines+1, sizeof(int));
740 if ((sc->glx_context = init_GL(mi)) != NULL) {
742 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
743 clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */
745 init_stars (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
751 int cw, ascent, descent;
752 sc->texfont = load_texture_font (MI_DISPLAY(mi), "font");
753 texture_string_metrics (sc->texfont, "n", &e, &ascent, &descent);
756 font_height = ascent + descent;
757 sc->descent = descent;
758 glEnable(GL_ALPHA_TEST);
759 glEnable (GL_TEXTURE_2D);
761 check_gl_error ("loading font");
763 /* "Anistropic filtering helps for quadrilateral-angled textures.
764 A sharper image is accomplished by interpolating and filtering
765 multiple samples from one or more mipmaps to better approximate
766 very distorted textures. This is the next level of filtering
767 after trilinear filtering." */
769 strstr ((char *) glGetString(GL_EXTENSIONS),
770 "GL_EXT_texture_filter_anisotropic"))
772 GLfloat anisotropic = 0.0;
773 glGetFloatv (GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropic);
774 if (anisotropic >= 1.0)
775 glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,
781 font_height = GLUT_FONT->top - GLUT_FONT->bottom;
782 sc->char_width = glutStrokeWidth (GLUT_FONT, 'z'); /* 'n' seems wide */
786 sc->font_scale = 1.0 / sc->char_width;
789 /* We consider a font that consumes 80 columns to be "18 points".
791 If neither -size nor -columns was specified, default to 60 columns
792 (which is 24 points.)
794 If both were specified, -columns has priority.
800 if (target_columns <= 0 && font_size <= 0)
803 if (target_columns > 0)
804 font_size = base_size * (base_col / (double) target_columns);
805 else if (font_size > 0)
806 target_columns = base_col * (base_size / (double) font_size);
809 sc->line_pixel_width = target_columns * sc->char_width;
811 sc->font_scale /= target_columns;
812 sc->line_height = font_height * sc->font_scale;
814 /* Buffer only a few lines of text.
815 If the buffer is too big, there's a significant delay between
816 when the program launches and when the text appears, which can be
817 irritating for time-sensitive output (clock, current music, etc.)
819 I'd like to buffer only 2 lines, but we need to assume that we
820 could get a whole line of N-byte Unicrud, and if we fill the buffer
821 before hitting the end of the line, we stall.
823 sc->buf_size = target_columns * 2 * 4;
824 if (sc->buf_size < 80) sc->buf_size = 80;
825 sc->buf = (char *) calloc (1, sc->buf_size);
827 sc->total_lines = max_lines-1;
830 star_spin = -star_spin;
832 if (!alignment_str || !*alignment_str ||
833 !strcasecmp(alignment_str, "left"))
835 else if (!strcasecmp(alignment_str, "center") ||
836 !strcasecmp(alignment_str, "middle"))
838 else if (!strcasecmp(alignment_str, "right"))
843 "%s: alignment must be left, center, or right, not \"%s\"\n",
844 progname, alignment_str);
848 sc->tc = textclient_open (sc->dpy);
850 /* one more reshape, after line_height has been computed */
851 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
856 draw_stars (ModeInfo *mi)
858 sws_configuration *sc = &scs[MI_SCREEN(mi)];
860 glMatrixMode (GL_PROJECTION);
865 glMatrixMode (GL_MODELVIEW);
869 glOrtho (-0.5 * MI_WIDTH(mi), 0.5 * MI_WIDTH(mi),
870 -0.5 * MI_HEIGHT(mi), 0.5 * MI_HEIGHT(mi),
872 glRotatef (sc->star_theta, 0.0, 0.0, 1.0);
873 if (textures_p) glDisable (GL_TEXTURE_2D);
875 glCallList (sc->star_list);
876 if (textures_p) glEnable (GL_TEXTURE_2D);
880 glMatrixMode (GL_PROJECTION);
885 draw_sws (ModeInfo *mi)
887 sws_configuration *sc = &scs[MI_SCREEN(mi)];
888 /* XtAppContext app = XtDisplayToApplicationContext (sc->dpy);*/
889 Display *dpy = MI_DISPLAY(mi);
890 Window window = MI_WINDOW(mi);
893 if (!sc->glx_context)
896 glDrawBuffer (GL_BACK);
897 glXMakeCurrent (dpy, window, *(sc->glx_context));
899 glClear (GL_COLOR_BUFFER_BIT);
903 glMatrixMode (GL_MODELVIEW);
907 /* Need to do this every time to get device rotation right */
908 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
915 if (textures_p) glDisable (GL_TEXTURE_2D);
917 glTranslatef (0,-1, 0);
919 glColor3f(1, 0, 0); /* Red line is where text appears */
921 glTranslatef(0, -0.028, 0);
924 glVertex3f(-0.5, 1, 0);
925 glVertex3f( 0.5, 1, 0);
926 glVertex3f(-0.5, -1, 0);
927 glVertex3f( 0.5, -1, 0);
932 glColor3f (0.2, 0.2, 0.2);
933 for (i = 0; i < 16; i++)
936 grid (1, 1, sc->char_width * sc->font_scale, sc->line_height, 0);
937 glTranslatef(0, 1, 0);
939 if (textures_p) glEnable (GL_TEXTURE_2D);
941 check_gl_error ("debug render");
944 /* Scroll to current position */
945 glTranslatef (0.0, sc->intra_line_scroll, 0.0);
947 glColor3f (1.0, 1.0, 0.4);
949 mi->polygon_count = 0;
952 glScalef (sc->font_scale, sc->font_scale, sc->font_scale);
953 for (i = 0; i < sc->total_lines; i++)
955 double fade = (fade_p ? 1.0 * i / sc->total_lines : 1.0);
956 int offscreen_lines = 2;
959 double y = ((sc->total_lines - (i + offscreen_lines) - 1)
962 char *line = sc->lines[i];
966 double xx = x * 1.4; /* a little more to the left */
968 sprintf(n, "%d:", i);
969 glColor3f (1.0, 1.0, 1.0);
970 draw_string (sc, xx / sc->font_scale, y / sc->font_scale, n);
976 if (sc->line_thickness != 1 && !textures_p)
978 int max_thick_lines = MAX_THICK_LINES;
979 GLfloat thinnest_line = 1.0;
980 GLfloat thickest_line = sc->line_thickness;
981 GLfloat range = thickest_line - thinnest_line;
984 int j = sc->total_lines - i - 1;
986 if (j > max_thick_lines)
987 thickness = thinnest_line;
989 thickness = (thinnest_line +
990 (range * ((max_thick_lines - j) /
991 (GLfloat) max_thick_lines)));
993 glLineWidth (thickness);
998 int n = sc->line_widths[i];
999 xoff = 1.0 - (n * sc->font_scale);
1005 glColor3f (fade, fade, 0.5 * fade);
1006 draw_string (sc, (x + xoff) / sc->font_scale, y / sc->font_scale,
1009 mi->polygon_count += strlen (line);
1015 sc->intra_line_scroll += sc->line_height / scroll_steps;
1017 if (sc->intra_line_scroll >= sc->line_height)
1019 sc->intra_line_scroll = 0;
1021 /* Drop the oldest line off the end. */
1023 free (sc->lines[0]);
1025 /* Scroll the contents of the lines array toward 0. */
1026 if (sc->total_lines > 0)
1028 for (i = 1; i < sc->total_lines; i++) {
1029 sc->lines[i-1] = sc->lines[i];
1030 sc->line_widths[i-1] = sc->line_widths[i];
1032 sc->lines[--sc->total_lines] = 0;
1035 /* Bring in new lines at the end. */
1036 get_more_lines (sc);
1038 if (sc->total_lines < max_lines)
1039 /* Oops, we ran out of text... well, insert some blank lines
1040 here so that new text still pulls in from the bottom of
1041 the screen, isntead of just appearing. */
1042 sc->total_lines = max_lines;
1047 if (mi->fps_p) do_fps (mi);
1049 glXSwapBuffers(dpy, window);
1051 sc->star_theta += star_spin;
1055 free_sws (ModeInfo *mi)
1057 sws_configuration *sc = &scs[MI_SCREEN(mi)];
1059 textclient_close (sc->tc);
1061 /* #### there's more to free here */
1066 __extension__ /* don't warn about "string length is greater than the length
1067 ISO C89 compilers are required to support" when including
1068 "starwars.txt" in the defaults... */
1071 XSCREENSAVER_MODULE_2 ("StarWars", starwars, sws)