1 /* starwars, Copyright (c) 1998-2014 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;
117 double intra_line_scroll;
119 int line_pixel_width; /* in font units (for wrapping text) */
120 int line_pixel_height; /* in screen units (for computing line thickness) */
121 GLfloat line_thickness;
126 static sws_configuration *scs = NULL;
128 static int max_lines;
129 static int scroll_steps;
130 static float star_spin;
131 static float font_size;
132 static int target_columns;
137 static int textures_p;
139 static char *alignment_str;
140 static int alignment;
142 static XrmOptionDescRec opts[] = {
143 {"-lines", ".lines", XrmoptionSepArg, 0 },
144 {"-steps", ".steps", XrmoptionSepArg, 0 },
145 {"-spin", ".spin", XrmoptionSepArg, 0 },
146 {"-size", ".size", XrmoptionSepArg, 0 },
147 {"-columns", ".columns", XrmoptionSepArg, 0 },
148 /*{"-font", ".font", XrmoptionSepArg, 0 },*/
149 {"-fade", ".fade", XrmoptionNoArg, "True" },
150 {"-no-fade", ".fade", XrmoptionNoArg, "False" },
151 {"-textures", ".textures", XrmoptionNoArg, "True" },
152 {"-smooth", ".smooth", XrmoptionNoArg, "True" },
153 {"-no-smooth", ".smooth", XrmoptionNoArg, "False" },
154 {"-thick", ".thick", XrmoptionNoArg, "True" },
155 {"-no-thick", ".thick", XrmoptionNoArg, "False" },
156 {"-no-textures", ".textures", XrmoptionNoArg, "False" },
157 {"-wrap", ".lineWrap", XrmoptionNoArg, "True" },
158 {"-no-wrap", ".lineWrap", XrmoptionNoArg, "False" },
159 {"-nowrap", ".lineWrap", XrmoptionNoArg, "False" },
160 {"-alignment", ".alignment", XrmoptionSepArg, 0 },
161 {"-left", ".alignment", XrmoptionNoArg, "Left" },
162 {"-right", ".alignment", XrmoptionNoArg, "Right" },
163 {"-center", ".alignment", XrmoptionNoArg, "Center" },
164 {"-debug", ".debug", XrmoptionNoArg, "True" },
167 static argtype vars[] = {
168 {&max_lines, "lines", "Integer", DEF_LINES, t_Int},
169 {&scroll_steps, "steps", "Integer", DEF_STEPS, t_Int},
170 {&star_spin, "spin", "Float", DEF_SPIN, 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)
251 return texture_string_width (sc->texfont, s, 0);
253 return glutStrokeLength (GLUT_FONT, (unsigned char *) s);
257 char_width (sws_configuration *sc, char c)
262 return sw_string_width (sc, s);
266 /* Populates the sc->lines list with as many lines as possible.
269 get_more_lines (sws_configuration *sc)
271 /* wrap anyway, if it's absurdly long. */
272 int wrap_pix = (wrap_p ? sc->line_pixel_width : 10000);
279 int target = sc->buf_size - sc->buf_tail - 2;
281 /* Fill as much as we can into sc->buf.
285 int c = textclient_getc (sc->tc);
288 sc->buf[sc->buf_tail++] = (char) c;
289 sc->buf[sc->buf_tail] = 0;
293 while (sc->total_lines < max_lines)
297 if (s >= sc->buf + sc->buf_tail)
298 /* Reached end of buffer before end of line. Bail. */
301 cw = char_width (sc, *s);
303 if (*s == '\r' || *s == '\n' ||
304 col_pix + cw >= wrap_pix)
308 if (*s == '\r' || *s == '\n')
310 if (*s == '\r' && s[1] == '\n') /* swallow CRLF too */
317 /* We wrapped -- try to back up to the previous word boundary. */
320 while (s2 > sc->buf && *s2 != ' ' && *s2 != '\t')
330 sc->lines[sc->total_lines] = (char *) malloc (L+1);
331 memcpy (sc->lines[sc->total_lines], sc->buf, L);
332 sc->lines[sc->total_lines][L] = 0;
336 /* The GLUT font only has ASCII characters. */
337 char *s1 = utf8_to_latin1 (sc->lines[sc->total_lines], True);
338 free (sc->lines[sc->total_lines]);
339 sc->lines[sc->total_lines] = s1;
343 char *t = sc->lines[sc->total_lines];
344 char *ut = untabify (t);
345 strip (ut, (alignment == 0), 1); /* if centering, strip
346 leading whitespace too */
347 sc->lines[sc->total_lines] = ut;
353 if (sc->buf_tail > (s - sc->buf))
355 int i = sc->buf_tail - (s - sc->buf);
356 memmove (sc->buf, s, i);
358 sc->buf[sc->buf_tail] = 0;
365 sc->buf[sc->buf_tail] = 0;
376 int tab_pix = TAB_WIDTH * sc->char_width;
377 col = TAB_WIDTH * ((col / TAB_WIDTH) + 1);
378 col_pix = tab_pix * ((col / tab_pix) + 1);
387 draw_string (sws_configuration *sc, GLfloat x, GLfloat y, const char *s)
390 if (!s || !*s) return;
392 glTranslatef (x, y, 0);
395 print_texture_string (sc->texfont, s);
398 glutStrokeCharacter (GLUT_FONT, *s++);
403 GLfloat h = sc->line_height / sc->font_scale;
404 char **chars = utf8_split (os, 0);
407 if (textures_p) glDisable (GL_TEXTURE_2D);
409 glColor3f (0.4, 0.4, 0.4);
411 glTranslatef (x, y, 0);
412 for (i = 0; chars[i]; i++)
414 GLfloat w = sw_string_width (sc, chars[i]);
416 glBegin (GL_LINE_LOOP);
417 glVertex3f (0, 0, 0);
418 glVertex3f (w, 0, 0);
419 glVertex3f (w, h, 0);
420 glVertex3f (0, h, 0);
422 glTranslatef (w, 0, 0);
426 if (textures_p) glEnable (GL_TEXTURE_2D);
432 grid (double width, double height, double xspacing, double yspacing, double z)
435 for (y = 0; y <= height/2; y += yspacing)
438 glVertex3f(-width/2, y, z);
439 glVertex3f( width/2, y, z);
440 glVertex3f(-width/2, -y, z);
441 glVertex3f( width/2, -y, z);
444 for (x = 0; x <= width/2; x += xspacing)
447 glVertex3f( x, -height/2, z);
448 glVertex3f( x, height/2, z);
449 glVertex3f(-x, -height/2, z);
450 glVertex3f(-x, height/2, z);
455 glVertex3f(-width, 0, z);
456 glVertex3f( width, 0, z);
457 glVertex3f(0, -height, z);
458 glVertex3f(0, height, z);
463 box (double width, double height, double depth)
465 glBegin(GL_LINE_LOOP);
466 glVertex3f(-width/2, -height/2, -depth/2);
467 glVertex3f(-width/2, height/2, -depth/2);
468 glVertex3f( width/2, height/2, -depth/2);
469 glVertex3f( width/2, -height/2, -depth/2);
471 glBegin(GL_LINE_LOOP);
472 glVertex3f(-width/2, -height/2, depth/2);
473 glVertex3f(-width/2, height/2, depth/2);
474 glVertex3f( width/2, height/2, depth/2);
475 glVertex3f( width/2, -height/2, depth/2);
477 glBegin(GL_LINE_LOOP);
478 glVertex3f(-width/2, -height/2, -depth/2);
479 glVertex3f(-width/2, -height/2, depth/2);
480 glVertex3f(-width/2, height/2, depth/2);
481 glVertex3f(-width/2, height/2, -depth/2);
483 glBegin(GL_LINE_LOOP);
484 glVertex3f( width/2, -height/2, -depth/2);
485 glVertex3f( width/2, -height/2, depth/2);
486 glVertex3f( width/2, height/2, depth/2);
487 glVertex3f( width/2, height/2, -depth/2);
491 glVertex3f(-width/2, height/2, depth/2);
492 glVertex3f(-width/2, -height/2, -depth/2);
494 glVertex3f( width/2, height/2, depth/2);
495 glVertex3f( width/2, -height/2, -depth/2);
497 glVertex3f(-width/2, -height/2, depth/2);
498 glVertex3f(-width/2, height/2, -depth/2);
500 glVertex3f( width/2, -height/2, depth/2);
501 glVertex3f( width/2, height/2, -depth/2);
506 /* Construct stars (number of stars is dependent on size of screen) */
508 init_stars (ModeInfo *mi, int width, int height)
510 sws_configuration *sc = &scs[MI_SCREEN(mi)];
512 int size = (width > height ? width : height);
513 int nstars = size * size / 320;
516 int steps = max_size / inc;
518 glDeleteLists (sc->star_list, 1);
519 sc->star_list = glGenLists (1);
520 glNewList (sc->star_list, GL_COMPILE);
522 glEnable(GL_POINT_SMOOTH);
524 for (j = 1; j <= steps; j++)
526 glPointSize(inc * j);
528 for (i = 0; i < nstars / steps; i++)
530 glColor3f (0.6 + frand(0.3),
533 glVertex2f (2 * size * (0.5 - frand(1.0)),
534 2 * size * (0.5 - frand(1.0)));
542 /* Window management, etc
545 reshape_sws (ModeInfo *mi, int width, int height)
547 sws_configuration *sc = &scs[MI_SCREEN(mi)];
549 /* Set up matrices for perspective text display
552 GLfloat desired_aspect = (GLfloat) 3/4;
553 int w = mi->xgwa.width;
554 int h = mi->xgwa.height;
556 GLfloat rot = current_device_rotation();
560 int h2 = w * desired_aspect;
561 yoff = (h - h2) / 2; /* Wide window: letterbox at top and bottom. */
562 if (yoff < 0) yoff = 0; /* Tall window: clip off the top. */
567 glMatrixMode (GL_PROJECTION);
568 glViewport (0, yoff, w, h);
570 glMatrixMode (GL_MODELVIEW);
572 gluPerspective (80.0, 1/desired_aspect, 1000, 55000);
573 gluLookAt (0.0, 0.0, 4600.0,
577 glRotatef(rot, 0, 0, 1);
579 /* Horrible kludge to prevent the text from materializing already
580 on screen on iPhone in landscape mode.
582 if ((rot > 45 && rot < 135) ||
583 (rot < -45 && rot > -135))
589 glRotatef (-60.0, 1.0, 0.0, 0.0);
592 glRotatef (60.0, 1.0, 0.0, 0.0);
593 glTranslatef (260, 3200, 0);
594 glScalef (1.85, 1.85, 1);
597 /* The above gives us an arena where the bottom edge of the screen is
598 represented by the line (-2100,-3140,0) - ( 2100,-3140,0). */
600 /* Now let's move the origin to the front of the screen. */
601 glTranslatef (0.0, -3140, 0.0);
603 /* And then let's scale so that the bottom of the screen is 1.0 wide. */
604 glScalef (4200, 4200, 4200);
608 /* Compute the height in pixels of the line at the bottom of the screen. */
610 GLdouble mm[17], pm[17];
612 GLdouble x = 0.5, y1 = 0, z = 0;
613 GLdouble y2 = sc->line_height;
614 GLdouble wx=-1, wy1=-1, wy2=-1, wz=-1;
616 glGetDoublev (GL_MODELVIEW_MATRIX, mm);
617 glGetDoublev (GL_PROJECTION_MATRIX, pm);
618 glGetIntegerv (GL_VIEWPORT, vp);
619 gluProject (x, y1, z, mm, pm, vp, &wx, &wy1, &wz);
620 gluProject (x, y2, z, mm, pm, vp, &wx, &wy2, &wz);
621 sc->line_pixel_height = (wy2 - wy1);
625 /* Compute the best looking line thickness for the bottom line.
628 sc->line_thickness = 1.0;
630 sc->line_thickness = (GLfloat) sc->line_pixel_height / FONT_WEIGHT;
632 if (sc->line_thickness < 1.2)
633 sc->line_thickness = 1.0;
638 gl_init (ModeInfo *mi)
640 sws_configuration *sc = &scs[MI_SCREEN(mi)];
642 glDisable (GL_LIGHTING);
643 glDisable (GL_DEPTH_TEST);
647 glEnable (GL_LINE_SMOOTH);
648 glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
649 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
653 sc->text_list = glGenLists (1);
654 glNewList (sc->text_list, GL_COMPILE);
657 sc->star_list = glGenLists (1);
658 glNewList (sc->star_list, GL_COMPILE);
661 sc->line_thickness = 1.0;
666 init_sws (ModeInfo *mi)
670 sws_configuration *sc = 0;
673 scs = (sws_configuration *)
674 calloc (MI_NUM_SCREENS(mi), sizeof (sws_configuration));
676 fprintf(stderr, "%s: out of memory\n", progname);
681 sc = &scs[MI_SCREEN(mi)];
683 sc->dpy = MI_DISPLAY(mi);
684 sc = &scs[MI_SCREEN(mi)];
685 sc->lines = (char **) calloc (max_lines+1, sizeof(char *));
687 if ((sc->glx_context = init_GL(mi)) != NULL) {
689 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
690 clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */
692 init_stars (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
698 sc->texfont = load_texture_font (MI_DISPLAY(mi), "font");
699 cw = texture_string_width (sc->texfont, "n", &lh);
702 glEnable(GL_ALPHA_TEST);
703 glEnable (GL_TEXTURE_2D);
705 check_gl_error ("loading font");
707 /* "Anistropic filtering helps for quadrilateral-angled textures.
708 A sharper image is accomplished by interpolating and filtering
709 multiple samples from one or more mipmaps to better approximate
710 very distorted textures. This is the next level of filtering
711 after trilinear filtering." */
713 strstr ((char *) glGetString(GL_EXTENSIONS),
714 "GL_EXT_texture_filter_anisotropic"))
716 GLfloat anisotropic = 0.0;
717 glGetFloatv (GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropic);
718 if (anisotropic >= 1.0)
719 glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,
725 font_height = GLUT_FONT->top - GLUT_FONT->bottom;
726 sc->char_width = glutStrokeWidth (GLUT_FONT, 'z'); /* 'n' seems wide */
729 sc->font_scale = 1.0 / sc->char_width;
732 /* We consider a font that consumes 80 columns to be "18 points".
734 If neither -size nor -columns was specified, default to 60 columns
735 (which is 24 points.)
737 If both were specified, -columns has priority.
743 if (target_columns <= 0 && font_size <= 0)
746 if (target_columns > 0)
747 font_size = base_size * (base_col / (double) target_columns);
748 else if (font_size > 0)
749 target_columns = base_col * (base_size / (double) font_size);
752 sc->line_pixel_width = target_columns * sc->char_width;
754 sc->font_scale /= target_columns;
755 sc->line_height = font_height * sc->font_scale;
758 /* Buffer only a few lines of text.
759 If the buffer is too big, there's a significant delay between
760 when the program launches and when the text appears, which can be
761 irritating for time-sensitive output (clock, current music, etc.)
763 I'd like to buffer only 2 lines, but we need to assume that we
764 could get a whole line of N-byte Unicrud, and if we fill the buffer
765 before hitting the end of the line, we stall.
767 sc->buf_size = target_columns * 2 * 4;
768 if (sc->buf_size < 80) sc->buf_size = 80;
769 sc->buf = (char *) calloc (1, sc->buf_size);
771 sc->total_lines = max_lines-1;
774 star_spin = -star_spin;
776 if (!alignment_str || !*alignment_str ||
777 !strcasecmp(alignment_str, "left"))
779 else if (!strcasecmp(alignment_str, "center") ||
780 !strcasecmp(alignment_str, "middle"))
782 else if (!strcasecmp(alignment_str, "right"))
787 "%s: alignment must be left, center, or right, not \"%s\"\n",
788 progname, alignment_str);
792 sc->tc = textclient_open (sc->dpy);
794 /* one more reshape, after line_height has been computed */
795 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
800 draw_stars (ModeInfo *mi)
802 sws_configuration *sc = &scs[MI_SCREEN(mi)];
804 glMatrixMode (GL_PROJECTION);
809 glMatrixMode (GL_MODELVIEW);
813 glOrtho (-0.5 * MI_WIDTH(mi), 0.5 * MI_WIDTH(mi),
814 -0.5 * MI_HEIGHT(mi), 0.5 * MI_HEIGHT(mi),
816 glRotatef (sc->star_theta, 0.0, 0.0, 1.0);
817 if (textures_p) glDisable (GL_TEXTURE_2D);
819 /* Keep the stars pointing in the same direction after rotation */
820 glRotatef(current_device_rotation(), 0, 0, 1);
822 glCallList (sc->star_list);
823 if (textures_p) glEnable (GL_TEXTURE_2D);
827 glMatrixMode (GL_PROJECTION);
832 draw_sws (ModeInfo *mi)
834 sws_configuration *sc = &scs[MI_SCREEN(mi)];
835 /* XtAppContext app = XtDisplayToApplicationContext (sc->dpy);*/
836 Display *dpy = MI_DISPLAY(mi);
837 Window window = MI_WINDOW(mi);
840 if (!sc->glx_context)
843 glDrawBuffer (GL_BACK);
844 glXMakeCurrent (dpy, window, *(sc->glx_context));
846 glClear (GL_COLOR_BUFFER_BIT);
850 glMatrixMode (GL_MODELVIEW);
854 /* Need to do this every time to get device rotation right */
855 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
862 if (textures_p) glDisable (GL_TEXTURE_2D);
864 glTranslatef (0,-1, 0);
866 glColor3f(1, 0, 0); /* Red line is where text appears */
868 glTranslatef(0, -0.028, 0);
871 glVertex3f(-0.5, 1, 0);
872 glVertex3f( 0.5, 1, 0);
873 glVertex3f(-0.5, -1, 0);
874 glVertex3f( 0.5, -1, 0);
879 glColor3f (0.4, 0.4, 0.4);
880 for (i = 0; i < 16; i++)
883 grid (1, 1, sc->char_width * sc->font_scale, sc->line_height, 0);
884 glTranslatef(0, 1, 0);
886 if (textures_p) glEnable (GL_TEXTURE_2D);
888 check_gl_error ("debug render");
891 /* Scroll to current position */
892 glTranslatef (0.0, sc->intra_line_scroll, 0.0);
894 glColor3f (1.0, 1.0, 0.4);
896 mi->polygon_count = 0;
899 glScalef (sc->font_scale, sc->font_scale, sc->font_scale);
900 for (i = 0; i < sc->total_lines; i++)
902 double fade = (fade_p ? 1.0 * i / sc->total_lines : 1.0);
903 int offscreen_lines = 2;
906 double y = ((sc->total_lines - (i + offscreen_lines) - 1)
909 char *line = sc->lines[i];
913 double xx = x * 1.4; /* a little more to the left */
915 sprintf(n, "%d:", i);
916 draw_string (sc, xx / sc->font_scale, y / sc->font_scale, n);
922 if (sc->line_thickness != 1 && !textures_p)
924 int max_thick_lines = MAX_THICK_LINES;
925 GLfloat thinnest_line = 1.0;
926 GLfloat thickest_line = sc->line_thickness;
927 GLfloat range = thickest_line - thinnest_line;
930 int j = sc->total_lines - i - 1;
932 if (j > max_thick_lines)
933 thickness = thinnest_line;
935 thickness = (thinnest_line +
936 (range * ((max_thick_lines - j) /
937 (GLfloat) max_thick_lines)));
939 glLineWidth (thickness);
944 int n = sw_string_width (sc, line);
945 xoff = 1.0 - (n * sc->font_scale);
951 glColor3f (fade, fade, 0.5 * fade);
952 draw_string (sc, (x + xoff) / sc->font_scale, y / sc->font_scale,
955 mi->polygon_count += strlen (line);
961 sc->intra_line_scroll += sc->line_height / scroll_steps;
963 if (sc->intra_line_scroll >= sc->line_height)
965 sc->intra_line_scroll = 0;
967 /* Drop the oldest line off the end. */
971 /* Scroll the contents of the lines array toward 0. */
972 if (sc->total_lines > 0)
974 for (i = 1; i < sc->total_lines; i++)
975 sc->lines[i-1] = sc->lines[i];
976 sc->lines[--sc->total_lines] = 0;
979 /* Bring in new lines at the end. */
982 if (sc->total_lines < max_lines)
983 /* Oops, we ran out of text... well, insert some blank lines
984 here so that new text still pulls in from the bottom of
985 the screen, isntead of just appearing. */
986 sc->total_lines = max_lines;
991 if (mi->fps_p) do_fps (mi);
993 glXSwapBuffers(dpy, window);
995 sc->star_theta += star_spin;
999 release_sws (ModeInfo *mi)
1003 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
1004 sws_configuration *sc = &scs[screen];
1006 textclient_close (sc->tc);
1008 /* #### there's more to free here */
1018 __extension__ /* don't warn about "string length is greater than the length
1019 ISO C89 compilers are required to support" when including
1020 "starwars.txt" in the defaults... */
1023 XSCREENSAVER_MODULE_2 ("StarWars", starwars, sws)