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 "*font: " DEF_FONT "\n" \
45 "*textLiteral: " DEF_TEXT "\n"
48 # define refresh_sws 0
49 # define sws_handle_event 0
51 #define countof(x) (sizeof((x))/sizeof((*x)))
53 #include "xlockmore.h"
54 #include "textclient.h"
56 #ifdef USE_GL /* whole file */
58 /* Should be in <GL/glext.h> */
59 # ifndef GL_TEXTURE_MAX_ANISOTROPY_EXT
60 # define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
62 # ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT
63 # define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
67 #define DEF_PROGRAM "xscreensaver-text --cols 0" /* don't wrap */
68 #define DEF_LINES "125"
69 #define DEF_STEPS "35"
70 #define DEF_SPIN "0.03"
72 #define DEF_COLUMNS "-1"
73 #define DEF_LINE_WRAP "True"
74 #define DEF_ALIGNMENT "Center"
75 #define DEF_SMOOTH "True"
76 #define DEF_THICK "True"
77 #define DEF_FADE "True"
78 #define DEF_TEXTURES "True"
79 #define DEF_DEBUG "False"
81 /* Utopia 800 needs 64 512x512 textures (4096x4096 bitmap).
82 Utopia 720 needs 16 512x512 textures (2048x2048 bitmap).
83 Utopia 480 needs 16 512x512 textures (2048x2048 bitmap).
84 Utopia 400 needs 4 512x512 textures (1024x1024 bitmap).
85 Utopia 180 needs 1 512x512 texture.
86 Times 240 needs 1 512x512 texture.
88 #define DEF_FONT "-*-utopia-bold-r-normal-*-*-720-*-*-*-*-iso8859-1"
92 #define MAX_THICK_LINES 25
93 #define FONT_WEIGHT 14
96 # define KEEP_ASPECT /* Letterboxing looks dumb on iPhone. */
100 #include "glutstroke.h"
101 #include "glut_roman.h"
102 #define GLUT_FONT (&glutStrokeRoman)
106 GLXContext *glx_context;
108 GLuint text_list, star_list;
109 texture_font_data *texfont;
124 double intra_line_scroll;
126 int line_pixel_width; /* in font units (for wrapping text) */
127 int line_pixel_height; /* in screen units (for computing line thickness) */
128 GLfloat line_thickness;
133 static sws_configuration *scs = NULL;
135 static char *program;
136 static int max_lines;
137 static int scroll_steps;
138 static float star_spin;
139 static float font_size;
140 static int target_columns;
145 static int textures_p;
147 static char *alignment_str;
148 static int alignment;
150 static XrmOptionDescRec opts[] = {
151 {"-program", ".program", XrmoptionSepArg, 0 },
152 {"-lines", ".lines", XrmoptionSepArg, 0 },
153 {"-steps", ".steps", XrmoptionSepArg, 0 },
154 {"-spin", ".spin", XrmoptionSepArg, 0 },
155 {"-size", ".size", XrmoptionSepArg, 0 },
156 {"-columns", ".columns", XrmoptionSepArg, 0 },
157 /*{"-font", ".font", XrmoptionSepArg, 0 },*/
158 {"-fade", ".fade", XrmoptionNoArg, "True" },
159 {"-no-fade", ".fade", XrmoptionNoArg, "False" },
160 {"-textures", ".textures", XrmoptionNoArg, "True" },
161 {"-smooth", ".smooth", XrmoptionNoArg, "True" },
162 {"-no-smooth", ".smooth", XrmoptionNoArg, "False" },
163 {"-thick", ".thick", XrmoptionNoArg, "True" },
164 {"-no-thick", ".thick", XrmoptionNoArg, "False" },
165 {"-no-textures", ".textures", XrmoptionNoArg, "False" },
166 {"-wrap", ".lineWrap", XrmoptionNoArg, "True" },
167 {"-no-wrap", ".lineWrap", XrmoptionNoArg, "False" },
168 {"-nowrap", ".lineWrap", XrmoptionNoArg, "False" },
169 {"-alignment", ".alignment", XrmoptionSepArg, 0 },
170 {"-left", ".alignment", XrmoptionNoArg, "Left" },
171 {"-right", ".alignment", XrmoptionNoArg, "Right" },
172 {"-center", ".alignment", XrmoptionNoArg, "Center" },
173 {"-debug", ".debug", XrmoptionNoArg, "True" },
176 static argtype vars[] = {
177 {&program, "program", "Program", DEF_PROGRAM, t_String},
178 {&max_lines, "lines", "Integer", DEF_LINES, t_Int},
179 {&scroll_steps, "steps", "Integer", DEF_STEPS, t_Int},
180 {&star_spin, "spin", "Float", DEF_SPIN, t_Float},
181 {&font_size, "size", "Float", DEF_SIZE, t_Float},
182 {&target_columns, "columns", "Integer", DEF_COLUMNS, t_Int},
183 {&wrap_p, "lineWrap", "Boolean", DEF_LINE_WRAP, t_Bool},
184 {&alignment_str, "alignment", "Alignment", DEF_ALIGNMENT, t_String},
185 {&smooth_p, "smooth", "Boolean", DEF_SMOOTH, t_Bool},
186 {&thick_p, "thick", "Boolean", DEF_THICK, t_Bool},
187 {&fade_p, "fade", "Boolean", DEF_FADE, t_Bool},
188 {&textures_p, "textures", "Boolean", DEF_TEXTURES, t_Bool},
189 {&debug_p, "debug", "Boolean", DEF_DEBUG, t_Bool},
192 ENTRYPOINT ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
196 /* Tabs are bad, mmmkay? */
199 untabify (const char *string)
201 const char *ostring = string;
202 char *result = (char *) malloc ((strlen(string) * 8) + 1);
212 } while (col % TAB_WIDTH);
215 else if (*string == '\r' || *string == '\n')
220 else if (*string == '\010') /* backspace */
222 if (string > ostring)
237 strip (char *s, Bool leading, Bool trailing)
241 while (L > 0 && (s[L-1] == ' ' || s[L-1] == '\t'))
246 while (*s2 == ' ' || *s2 == '\t')
257 /* The GLUT font only has ASCII characters in them, so do what we can to
258 convert Latin1 characters to the nearest ASCII equivalent...
261 latin1_to_ascii (char *s)
263 unsigned char *us = (unsigned char *) s;
264 const unsigned char ascii[95] = {
265 '!', 'C', '#', '#', 'Y', '|', 'S', '_', 'C', '?', '<', '=', '-', 'R', '_',
266 '?', '?', '2', '3', '\'','u', 'P', '.', ',', '1', 'o', '>', '?', '?', '?',
267 '?', 'A', 'A', 'A', 'A', 'A', 'A', 'E', 'C', 'E', 'E', 'E', 'E', 'I', 'I',
268 'I', 'I', 'D', 'N', 'O', 'O', 'O', 'O', 'O', 'x', '0', 'U', 'U', 'U', 'U',
269 'Y', 'p', 'S', 'a', 'a', 'a', 'a', 'a', 'a', 'e', 'c', 'e', 'e', 'e', 'e',
270 'i', 'i', 'i', 'i', 'o', 'n', 'o', 'o', 'o', 'o', 'o', '/', 'o', 'u', 'u',
271 'u', 'u', 'y', 'p', 'y' };
275 *us = ascii[*us - 161];
284 sw_string_width (sws_configuration *sc, const char *s)
287 return texture_string_width (sc->texfont, s, 0);
289 return glutStrokeLength (GLUT_FONT, (unsigned char *) s);
293 char_width (sws_configuration *sc, char c)
298 return sw_string_width (sc, s);
302 /* Populates the sc->lines list with as many lines as possible.
305 get_more_lines (sws_configuration *sc)
307 /* wrap anyway, if it's absurdly long. */
308 int wrap_pix = (wrap_p ? sc->line_pixel_width : 10000);
315 int target = sc->buf_size - sc->buf_tail - 2;
317 /* Fill as much as we can into sc->buf.
321 int c = textclient_getc (sc->tc);
324 sc->buf[sc->buf_tail++] = (char) c;
325 sc->buf[sc->buf_tail] = 0;
329 while (sc->total_lines < max_lines)
333 if (s >= sc->buf + sc->buf_tail)
334 /* Reached end of buffer before end of line. Bail. */
337 cw = char_width (sc, *s);
339 if (*s == '\r' || *s == '\n' ||
340 col_pix + cw >= wrap_pix)
344 if (*s == '\r' || *s == '\n')
346 if (*s == '\r' && s[1] == '\n') /* swallow CRLF too */
353 /* We wrapped -- try to back up to the previous word boundary. */
356 while (s2 > sc->buf && *s2 != ' ' && *s2 != '\t')
366 sc->lines[sc->total_lines] = (char *) malloc (L+1);
367 memcpy (sc->lines[sc->total_lines], sc->buf, L);
368 sc->lines[sc->total_lines][L] = 0;
371 latin1_to_ascii (sc->lines[sc->total_lines]);
374 char *t = sc->lines[sc->total_lines];
375 char *ut = untabify (t);
376 strip (ut, (alignment == 0), 1); /* if centering, strip
377 leading whitespace too */
378 sc->lines[sc->total_lines] = ut;
384 if (sc->buf_tail > (s - sc->buf))
386 int i = sc->buf_tail - (s - sc->buf);
387 memmove (sc->buf, s, i);
389 sc->buf[sc->buf_tail] = 0;
396 sc->buf[sc->buf_tail] = 0;
407 int tab_pix = TAB_WIDTH * sc->char_width;
408 col = TAB_WIDTH * ((col / TAB_WIDTH) + 1);
409 col_pix = tab_pix * ((col / tab_pix) + 1);
418 draw_string (sws_configuration *sc, GLfloat x, GLfloat y, const char *s)
421 if (!s || !*s) return;
423 glTranslatef (x, y, 0);
426 print_texture_string (sc->texfont, s);
429 glutStrokeCharacter (GLUT_FONT, *s++);
435 GLfloat h = sc->line_height / sc->font_scale;
439 if (textures_p) glDisable (GL_TEXTURE_2D);
441 glColor3f (0.4, 0.4, 0.4);
443 glTranslatef (x, y, 0);
447 w = sw_string_width (sc, c);
448 glBegin (GL_LINE_LOOP);
449 glVertex3f (0, 0, 0);
450 glVertex3f (w, 0, 0);
451 glVertex3f (w, h, 0);
452 glVertex3f (0, h, 0);
454 glTranslatef (w, 0, 0);
457 if (textures_p) glEnable (GL_TEXTURE_2D);
463 grid (double width, double height, double xspacing, double yspacing, double z)
466 for (y = 0; y <= height/2; y += yspacing)
469 glVertex3f(-width/2, y, z);
470 glVertex3f( width/2, y, z);
471 glVertex3f(-width/2, -y, z);
472 glVertex3f( width/2, -y, z);
475 for (x = 0; x <= width/2; x += xspacing)
478 glVertex3f( x, -height/2, z);
479 glVertex3f( x, height/2, z);
480 glVertex3f(-x, -height/2, z);
481 glVertex3f(-x, height/2, z);
486 glVertex3f(-width, 0, z);
487 glVertex3f( width, 0, z);
488 glVertex3f(0, -height, z);
489 glVertex3f(0, height, z);
494 box (double width, double height, double depth)
496 glBegin(GL_LINE_LOOP);
497 glVertex3f(-width/2, -height/2, -depth/2);
498 glVertex3f(-width/2, height/2, -depth/2);
499 glVertex3f( width/2, height/2, -depth/2);
500 glVertex3f( width/2, -height/2, -depth/2);
502 glBegin(GL_LINE_LOOP);
503 glVertex3f(-width/2, -height/2, depth/2);
504 glVertex3f(-width/2, height/2, depth/2);
505 glVertex3f( width/2, height/2, depth/2);
506 glVertex3f( width/2, -height/2, depth/2);
508 glBegin(GL_LINE_LOOP);
509 glVertex3f(-width/2, -height/2, -depth/2);
510 glVertex3f(-width/2, -height/2, depth/2);
511 glVertex3f(-width/2, height/2, depth/2);
512 glVertex3f(-width/2, height/2, -depth/2);
514 glBegin(GL_LINE_LOOP);
515 glVertex3f( width/2, -height/2, -depth/2);
516 glVertex3f( width/2, -height/2, depth/2);
517 glVertex3f( width/2, height/2, depth/2);
518 glVertex3f( width/2, height/2, -depth/2);
522 glVertex3f(-width/2, height/2, depth/2);
523 glVertex3f(-width/2, -height/2, -depth/2);
525 glVertex3f( width/2, height/2, depth/2);
526 glVertex3f( width/2, -height/2, -depth/2);
528 glVertex3f(-width/2, -height/2, depth/2);
529 glVertex3f(-width/2, height/2, -depth/2);
531 glVertex3f( width/2, -height/2, depth/2);
532 glVertex3f( width/2, height/2, -depth/2);
537 /* Construct stars (number of stars is dependent on size of screen) */
539 init_stars (ModeInfo *mi, int width, int height)
541 sws_configuration *sc = &scs[MI_SCREEN(mi)];
543 int size = (width > height ? width : height);
544 int nstars = size * size / 320;
547 int steps = max_size / inc;
549 glDeleteLists (sc->star_list, 1);
550 sc->star_list = glGenLists (1);
551 glNewList (sc->star_list, GL_COMPILE);
553 glEnable(GL_POINT_SMOOTH);
555 for (j = 1; j <= steps; j++)
557 glPointSize(inc * j);
559 for (i = 0; i < nstars / steps; i++)
561 glColor3f (0.6 + frand(0.3),
564 glVertex2f (2 * size * (0.5 - frand(1.0)),
565 2 * size * (0.5 - frand(1.0)));
573 /* Window management, etc
576 reshape_sws (ModeInfo *mi, int width, int height)
578 sws_configuration *sc = &scs[MI_SCREEN(mi)];
580 /* Set up matrices for perspective text display
583 GLfloat desired_aspect = (GLfloat) 3/4;
584 int w = mi->xgwa.width;
585 int h = mi->xgwa.height;
587 GLfloat rot = current_device_rotation();
591 int h2 = w * desired_aspect;
592 yoff = (h - h2) / 2; /* Wide window: letterbox at top and bottom. */
593 if (yoff < 0) yoff = 0; /* Tall window: clip off the top. */
598 glMatrixMode (GL_PROJECTION);
599 glViewport (0, yoff, w, h);
601 glMatrixMode (GL_MODELVIEW);
603 gluPerspective (80.0, 1/desired_aspect, 1000, 55000);
604 gluLookAt (0.0, 0.0, 4600.0,
608 glRotatef(rot, 0, 0, 1);
610 /* Horrible kludge to prevent the text from materializing already
611 on screen on iPhone in landscape mode.
613 if ((rot > 45 && rot < 135) ||
614 (rot < -45 && rot > -135))
620 glRotatef (-60.0, 1.0, 0.0, 0.0);
623 glRotatef (60.0, 1.0, 0.0, 0.0);
624 glTranslatef (260, 3200, 0);
625 glScalef (1.85, 1.85, 1);
628 /* The above gives us an arena where the bottom edge of the screen is
629 represented by the line (-2100,-3140,0) - ( 2100,-3140,0). */
631 /* Now let's move the origin to the front of the screen. */
632 glTranslatef (0.0, -3140, 0.0);
634 /* And then let's scale so that the bottom of the screen is 1.0 wide. */
635 glScalef (4200, 4200, 4200);
639 /* Compute the height in pixels of the line at the bottom of the screen. */
641 GLdouble mm[17], pm[17];
643 GLdouble x = 0.5, y1 = 0, z = 0;
644 GLdouble y2 = sc->line_height;
645 GLdouble wx=-1, wy1=-1, wy2=-1, wz=-1;
647 glGetDoublev (GL_MODELVIEW_MATRIX, mm);
648 glGetDoublev (GL_PROJECTION_MATRIX, pm);
649 glGetIntegerv (GL_VIEWPORT, vp);
650 gluProject (x, y1, z, mm, pm, vp, &wx, &wy1, &wz);
651 gluProject (x, y2, z, mm, pm, vp, &wx, &wy2, &wz);
652 sc->line_pixel_height = (wy2 - wy1);
656 /* Compute the best looking line thickness for the bottom line.
659 sc->line_thickness = 1.0;
661 sc->line_thickness = (GLfloat) sc->line_pixel_height / FONT_WEIGHT;
663 if (sc->line_thickness < 1.2)
664 sc->line_thickness = 1.0;
669 gl_init (ModeInfo *mi)
671 sws_configuration *sc = &scs[MI_SCREEN(mi)];
673 program = get_string_resource (mi->dpy, "program", "Program");
675 glDisable (GL_LIGHTING);
676 glDisable (GL_DEPTH_TEST);
680 glEnable (GL_LINE_SMOOTH);
681 glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
682 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
686 sc->text_list = glGenLists (1);
687 glNewList (sc->text_list, GL_COMPILE);
690 sc->star_list = glGenLists (1);
691 glNewList (sc->star_list, GL_COMPILE);
694 sc->line_thickness = 1.0;
699 init_sws (ModeInfo *mi)
703 sws_configuration *sc = 0;
706 scs = (sws_configuration *)
707 calloc (MI_NUM_SCREENS(mi), sizeof (sws_configuration));
709 fprintf(stderr, "%s: out of memory\n", progname);
714 sc = &scs[MI_SCREEN(mi)];
716 sc->dpy = MI_DISPLAY(mi);
717 sc = &scs[MI_SCREEN(mi)];
718 sc->lines = (char **) calloc (max_lines+1, sizeof(char *));
720 if ((sc->glx_context = init_GL(mi)) != NULL) {
722 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
723 clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */
725 init_stars (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
731 sc->texfont = load_texture_font (MI_DISPLAY(mi), "font");
732 cw = texture_string_width (sc->texfont, "n", &lh);
735 glEnable(GL_ALPHA_TEST);
736 glEnable (GL_TEXTURE_2D);
738 check_gl_error ("loading font");
740 /* "Anistropic filtering helps for quadrilateral-angled textures.
741 A sharper image is accomplished by interpolating and filtering
742 multiple samples from one or more mipmaps to better approximate
743 very distorted textures. This is the next level of filtering
744 after trilinear filtering." */
746 strstr ((char *) glGetString(GL_EXTENSIONS),
747 "GL_EXT_texture_filter_anisotropic"))
749 GLfloat anisotropic = 0.0;
750 glGetFloatv (GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropic);
751 if (anisotropic >= 1.0)
752 glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,
758 font_height = GLUT_FONT->top - GLUT_FONT->bottom;
759 sc->char_width = glutStrokeWidth (GLUT_FONT, 'z'); /* 'n' seems wide */
762 sc->font_scale = 1.0 / sc->char_width;
765 /* We consider a font that consumes 80 columns to be "18 points".
767 If neither -size nor -columns was specified, default to 60 columns
768 (which is 24 points.)
770 If both were specified, -columns has priority.
776 if (target_columns <= 0 && font_size <= 0)
779 if (target_columns > 0)
780 font_size = base_size * (base_col / (double) target_columns);
781 else if (font_size > 0)
782 target_columns = base_col * (base_size / (double) font_size);
785 sc->line_pixel_width = target_columns * sc->char_width;
787 sc->font_scale /= target_columns;
788 sc->line_height = font_height * sc->font_scale;
791 /* Buffer only two lines of text.
792 If the buffer is too big, there's a significant delay between
793 when the program launches and when the text appears, which can be
794 irritating for time-sensitive output (clock, current music, etc.)
796 sc->buf_size = target_columns * 2;
797 if (sc->buf_size < 80) sc->buf_size = 80;
798 sc->buf = (char *) calloc (1, sc->buf_size);
800 sc->total_lines = max_lines-1;
803 star_spin = -star_spin;
805 if (!alignment_str || !*alignment_str ||
806 !strcasecmp(alignment_str, "left"))
808 else if (!strcasecmp(alignment_str, "center") ||
809 !strcasecmp(alignment_str, "middle"))
811 else if (!strcasecmp(alignment_str, "right"))
816 "%s: alignment must be left, center, or right, not \"%s\"\n",
817 progname, alignment_str);
821 sc->tc = textclient_open (sc->dpy);
823 /* one more reshape, after line_height has been computed */
824 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
829 draw_stars (ModeInfo *mi)
831 sws_configuration *sc = &scs[MI_SCREEN(mi)];
833 glMatrixMode (GL_PROJECTION);
838 glMatrixMode (GL_MODELVIEW);
842 glOrtho (-0.5 * MI_WIDTH(mi), 0.5 * MI_WIDTH(mi),
843 -0.5 * MI_HEIGHT(mi), 0.5 * MI_HEIGHT(mi),
845 glRotatef (sc->star_theta, 0.0, 0.0, 1.0);
846 if (textures_p) glDisable (GL_TEXTURE_2D);
848 /* Keep the stars pointing in the same direction after rotation */
849 glRotatef(current_device_rotation(), 0, 0, 1);
851 glCallList (sc->star_list);
852 if (textures_p) glEnable (GL_TEXTURE_2D);
856 glMatrixMode (GL_PROJECTION);
861 draw_sws (ModeInfo *mi)
863 sws_configuration *sc = &scs[MI_SCREEN(mi)];
864 /* XtAppContext app = XtDisplayToApplicationContext (sc->dpy);*/
865 Display *dpy = MI_DISPLAY(mi);
866 Window window = MI_WINDOW(mi);
869 if (!sc->glx_context)
872 glDrawBuffer (GL_BACK);
873 glXMakeCurrent (dpy, window, *(sc->glx_context));
875 glClear (GL_COLOR_BUFFER_BIT);
879 glMatrixMode (GL_MODELVIEW);
883 /* Need to do this every time to get device rotation right */
884 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
891 if (textures_p) glDisable (GL_TEXTURE_2D);
893 glTranslatef (0,-1, 0);
895 glColor3f(1, 0, 0); /* Red line is where text appears */
897 glTranslatef(0, -0.028, 0);
900 glVertex3f(-0.5, 1, 0);
901 glVertex3f( 0.5, 1, 0);
902 glVertex3f(-0.5, -1, 0);
903 glVertex3f( 0.5, -1, 0);
908 glColor3f (0.4, 0.4, 0.4);
909 for (i = 0; i < 16; i++)
912 grid (1, 1, sc->char_width * sc->font_scale, sc->line_height, 0);
913 glTranslatef(0, 1, 0);
915 if (textures_p) glEnable (GL_TEXTURE_2D);
919 /* Scroll to current position */
920 glTranslatef (0.0, sc->intra_line_scroll, 0.0);
922 glColor3f (1.0, 1.0, 0.4);
923 glCallList (sc->text_list);
924 mi->polygon_count = sc->polygon_count;
926 sc->intra_line_scroll += sc->line_height / scroll_steps;
928 if (sc->intra_line_scroll >= sc->line_height)
930 sc->intra_line_scroll = 0;
932 /* Drop the oldest line off the end. */
936 /* Scroll the contents of the lines array toward 0. */
937 if (sc->total_lines > 0)
939 for (i = 1; i < sc->total_lines; i++)
940 sc->lines[i-1] = sc->lines[i];
941 sc->lines[--sc->total_lines] = 0;
944 /* Bring in new lines at the end. */
947 if (sc->total_lines < max_lines)
948 /* Oops, we ran out of text... well, insert some blank lines
949 here so that new text still pulls in from the bottom of
950 the screen, isntead of just appearing. */
951 sc->total_lines = max_lines;
953 glDeleteLists (sc->text_list, 1);
954 sc->text_list = glGenLists (1);
955 glNewList (sc->text_list, GL_COMPILE);
956 sc->polygon_count = 0;
958 glScalef (sc->font_scale, sc->font_scale, sc->font_scale);
959 for (i = 0; i < sc->total_lines; i++)
961 double fade = (fade_p ? 1.0 * i / sc->total_lines : 1.0);
962 int offscreen_lines = 2;
965 double y = ((sc->total_lines - (i + offscreen_lines) - 1)
968 char *line = sc->lines[i];
972 double xx = x * 1.4; /* a little more to the left */
974 sprintf(n, "%d:", i);
975 draw_string (sc, xx / sc->font_scale, y / sc->font_scale, n);
981 if (sc->line_thickness != 1 && !textures_p)
983 int max_thick_lines = MAX_THICK_LINES;
984 GLfloat thinnest_line = 1.0;
985 GLfloat thickest_line = sc->line_thickness;
986 GLfloat range = thickest_line - thinnest_line;
989 int j = sc->total_lines - i - 1;
991 if (j > max_thick_lines)
992 thickness = thinnest_line;
994 thickness = (thinnest_line +
995 (range * ((max_thick_lines - j) /
996 (GLfloat) max_thick_lines)));
998 glLineWidth (thickness);
1003 int n = sw_string_width (sc, line);
1004 xoff = 1.0 - (n * sc->font_scale);
1010 glColor3f (fade, fade, 0.5 * fade);
1011 draw_string (sc, (x + xoff) / sc->font_scale, y / sc->font_scale,
1014 sc->polygon_count += strlen (line);
1022 if (mi->fps_p) do_fps (mi);
1024 glXSwapBuffers(dpy, window);
1026 sc->star_theta += star_spin;
1030 release_sws (ModeInfo *mi)
1034 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
1035 sws_configuration *sc = &scs[screen];
1037 textclient_close (sc->tc);
1039 /* #### there's more to free here */
1049 __extension__ /* don't warn about "string length is greater than the length
1050 ISO C89 compilers are required to support" when including
1051 "starwars.txt" in the defaults... */
1054 XSCREENSAVER_MODULE_2 ("StarWars", starwars, sws)