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 */
41 #define DEFAULTS "*delay: 40000 \n" \
42 "*showFPS: False \n" \
45 "*font: " DEF_FONT "\n" \
46 "*textLiteral: " DEF_TEXT "\n"
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"
57 #ifdef USE_GL /* whole file */
59 /* Should be in <GL/glext.h> */
60 # ifndef GL_TEXTURE_MAX_ANISOTROPY_EXT
61 # define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
63 # ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT
64 # define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
68 #define DEF_PROGRAM "xscreensaver-text --cols 0" /* don't wrap */
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 /* Utopia 800 needs 64 512x512 textures (4096x4096 bitmap).
83 Utopia 720 needs 16 512x512 textures (2048x2048 bitmap).
84 Utopia 480 needs 16 512x512 textures (2048x2048 bitmap).
85 Utopia 400 needs 4 512x512 textures (1024x1024 bitmap).
86 Utopia 180 needs 1 512x512 texture.
87 Times 240 needs 1 512x512 texture.
89 #define DEF_FONT "-*-utopia-bold-r-normal-*-*-720-*-*-*-*-iso8859-1"
93 #define MAX_THICK_LINES 25
94 #define FONT_WEIGHT 14
97 # define KEEP_ASPECT /* Letterboxing looks dumb on iPhone. */
101 #include "glutstroke.h"
102 #include "glut_roman.h"
103 #define GLUT_FONT (&glutStrokeRoman)
107 GLXContext *glx_context;
109 GLuint text_list, star_list;
110 texture_font_data *texfont;
125 double intra_line_scroll;
127 int line_pixel_width; /* in font units (for wrapping text) */
128 int line_pixel_height; /* in screen units (for computing line thickness) */
129 GLfloat line_thickness;
134 static sws_configuration *scs = NULL;
136 static char *program;
137 static int max_lines;
138 static int scroll_steps;
139 static float star_spin;
140 static float font_size;
141 static int target_columns;
146 static int textures_p;
148 static char *alignment_str;
149 static int alignment;
151 static XrmOptionDescRec opts[] = {
152 {"-program", ".program", XrmoptionSepArg, 0 },
153 {"-lines", ".lines", XrmoptionSepArg, 0 },
154 {"-steps", ".steps", XrmoptionSepArg, 0 },
155 {"-spin", ".spin", XrmoptionSepArg, 0 },
156 {"-size", ".size", XrmoptionSepArg, 0 },
157 {"-columns", ".columns", XrmoptionSepArg, 0 },
158 /*{"-font", ".font", XrmoptionSepArg, 0 },*/
159 {"-fade", ".fade", XrmoptionNoArg, "True" },
160 {"-no-fade", ".fade", XrmoptionNoArg, "False" },
161 {"-textures", ".textures", XrmoptionNoArg, "True" },
162 {"-smooth", ".smooth", XrmoptionNoArg, "True" },
163 {"-no-smooth", ".smooth", XrmoptionNoArg, "False" },
164 {"-thick", ".thick", XrmoptionNoArg, "True" },
165 {"-no-thick", ".thick", XrmoptionNoArg, "False" },
166 {"-no-textures", ".textures", XrmoptionNoArg, "False" },
167 {"-wrap", ".lineWrap", XrmoptionNoArg, "True" },
168 {"-no-wrap", ".lineWrap", XrmoptionNoArg, "False" },
169 {"-nowrap", ".lineWrap", XrmoptionNoArg, "False" },
170 {"-alignment", ".alignment", XrmoptionSepArg, 0 },
171 {"-left", ".alignment", XrmoptionNoArg, "Left" },
172 {"-right", ".alignment", XrmoptionNoArg, "Right" },
173 {"-center", ".alignment", XrmoptionNoArg, "Center" },
174 {"-debug", ".debug", XrmoptionNoArg, "True" },
177 static argtype vars[] = {
178 {&program, "program", "Program", DEF_PROGRAM, t_String},
179 {&max_lines, "lines", "Integer", DEF_LINES, t_Int},
180 {&scroll_steps, "steps", "Integer", DEF_STEPS, t_Int},
181 {&star_spin, "spin", "Float", DEF_SPIN, t_Float},
182 {&font_size, "size", "Float", DEF_SIZE, t_Float},
183 {&target_columns, "columns", "Integer", DEF_COLUMNS, t_Int},
184 {&wrap_p, "lineWrap", "Boolean", DEF_LINE_WRAP, t_Bool},
185 {&alignment_str, "alignment", "Alignment", DEF_ALIGNMENT, t_String},
186 {&smooth_p, "smooth", "Boolean", DEF_SMOOTH, t_Bool},
187 {&thick_p, "thick", "Boolean", DEF_THICK, t_Bool},
188 {&fade_p, "fade", "Boolean", DEF_FADE, t_Bool},
189 {&textures_p, "textures", "Boolean", DEF_TEXTURES, t_Bool},
190 {&debug_p, "debug", "Boolean", DEF_DEBUG, t_Bool},
193 ENTRYPOINT ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
197 /* Tabs are bad, mmmkay? */
200 untabify (const char *string)
202 const char *ostring = string;
203 char *result = (char *) malloc ((strlen(string) * 8) + 1);
213 } while (col % TAB_WIDTH);
216 else if (*string == '\r' || *string == '\n')
221 else if (*string == '\010') /* backspace */
223 if (string > ostring)
238 strip (char *s, Bool leading, Bool trailing)
242 while (L > 0 && (s[L-1] == ' ' || s[L-1] == '\t'))
247 while (*s2 == ' ' || *s2 == '\t')
258 /* The GLUT font only has ASCII characters in them, so do what we can to
259 convert Latin1 characters to the nearest ASCII equivalent...
262 latin1_to_ascii (char *s)
264 unsigned char *us = (unsigned char *) s;
265 const unsigned char ascii[95] = {
266 '!', 'C', '#', '#', 'Y', '|', 'S', '_', 'C', '?', '<', '=', '-', 'R', '_',
267 '?', '?', '2', '3', '\'','u', 'P', '.', ',', '1', 'o', '>', '?', '?', '?',
268 '?', 'A', 'A', 'A', 'A', 'A', 'A', 'E', 'C', 'E', 'E', 'E', 'E', 'I', 'I',
269 'I', 'I', 'D', 'N', 'O', 'O', 'O', 'O', 'O', 'x', '0', 'U', 'U', 'U', 'U',
270 'Y', 'p', 'S', 'a', 'a', 'a', 'a', 'a', 'a', 'e', 'c', 'e', 'e', 'e', 'e',
271 'i', 'i', 'i', 'i', 'o', 'n', 'o', 'o', 'o', 'o', 'o', '/', 'o', 'u', 'u',
272 'u', 'u', 'y', 'p', 'y' };
276 *us = ascii[*us - 161];
285 string_width (sws_configuration *sc, const char *s)
288 return texture_string_width (sc->texfont, s, 0);
290 return glutStrokeLength (GLUT_FONT, (unsigned char *) s);
294 char_width (sws_configuration *sc, char c)
299 return string_width (sc, s);
303 /* Populates the sc->lines list with as many lines as possible.
306 get_more_lines (sws_configuration *sc)
308 /* wrap anyway, if it's absurdly long. */
309 int wrap_pix = (wrap_p ? sc->line_pixel_width : 10000);
316 int target = sc->buf_size - sc->buf_tail - 2;
318 /* Fill as much as we can into sc->buf.
322 int c = textclient_getc (sc->tc);
325 sc->buf[sc->buf_tail++] = (char) c;
326 sc->buf[sc->buf_tail] = 0;
330 while (sc->total_lines < max_lines)
334 if (s >= sc->buf + sc->buf_tail)
335 /* Reached end of buffer before end of line. Bail. */
338 cw = char_width (sc, *s);
340 if (*s == '\r' || *s == '\n' ||
341 col_pix + cw >= wrap_pix)
345 if (*s == '\r' || *s == '\n')
347 if (*s == '\r' && s[1] == '\n') /* swallow CRLF too */
354 /* We wrapped -- try to back up to the previous word boundary. */
357 while (s2 > sc->buf && *s2 != ' ' && *s2 != '\t')
367 sc->lines[sc->total_lines] = (char *) malloc (L+1);
368 memcpy (sc->lines[sc->total_lines], sc->buf, L);
369 sc->lines[sc->total_lines][L] = 0;
372 latin1_to_ascii (sc->lines[sc->total_lines]);
375 char *t = sc->lines[sc->total_lines];
376 char *ut = untabify (t);
377 strip (ut, (alignment == 0), 1); /* if centering, strip
378 leading whitespace too */
379 sc->lines[sc->total_lines] = ut;
385 if (sc->buf_tail > (s - sc->buf))
387 int i = sc->buf_tail - (s - sc->buf);
388 memmove (sc->buf, s, i);
390 sc->buf[sc->buf_tail] = 0;
397 sc->buf[sc->buf_tail] = 0;
408 int tab_pix = TAB_WIDTH * sc->char_width;
409 col = TAB_WIDTH * ((col / TAB_WIDTH) + 1);
410 col_pix = tab_pix * ((col / tab_pix) + 1);
419 draw_string (sws_configuration *sc, GLfloat x, GLfloat y, const char *s)
422 if (!s || !*s) return;
424 glTranslatef (x, y, 0);
427 print_texture_string (sc->texfont, s);
430 glutStrokeCharacter (GLUT_FONT, *s++);
436 GLfloat h = sc->line_height / sc->font_scale;
440 if (textures_p) glDisable (GL_TEXTURE_2D);
442 glColor3f (0.4, 0.4, 0.4);
444 glTranslatef (x, y, 0);
448 w = string_width (sc, c);
449 glBegin (GL_LINE_LOOP);
450 glVertex3f (0, 0, 0);
451 glVertex3f (w, 0, 0);
452 glVertex3f (w, h, 0);
453 glVertex3f (0, h, 0);
455 glTranslatef (w, 0, 0);
458 if (textures_p) glEnable (GL_TEXTURE_2D);
464 grid (double width, double height, double xspacing, double yspacing, double z)
467 for (y = 0; y <= height/2; y += yspacing)
470 glVertex3f(-width/2, y, z);
471 glVertex3f( width/2, y, z);
472 glVertex3f(-width/2, -y, z);
473 glVertex3f( width/2, -y, z);
476 for (x = 0; x <= width/2; x += xspacing)
479 glVertex3f( x, -height/2, z);
480 glVertex3f( x, height/2, z);
481 glVertex3f(-x, -height/2, z);
482 glVertex3f(-x, height/2, z);
487 glVertex3f(-width, 0, z);
488 glVertex3f( width, 0, z);
489 glVertex3f(0, -height, z);
490 glVertex3f(0, height, z);
495 box (double width, double height, double depth)
497 glBegin(GL_LINE_LOOP);
498 glVertex3f(-width/2, -height/2, -depth/2);
499 glVertex3f(-width/2, height/2, -depth/2);
500 glVertex3f( width/2, height/2, -depth/2);
501 glVertex3f( width/2, -height/2, -depth/2);
503 glBegin(GL_LINE_LOOP);
504 glVertex3f(-width/2, -height/2, depth/2);
505 glVertex3f(-width/2, height/2, depth/2);
506 glVertex3f( width/2, height/2, depth/2);
507 glVertex3f( width/2, -height/2, depth/2);
509 glBegin(GL_LINE_LOOP);
510 glVertex3f(-width/2, -height/2, -depth/2);
511 glVertex3f(-width/2, -height/2, depth/2);
512 glVertex3f(-width/2, height/2, depth/2);
513 glVertex3f(-width/2, height/2, -depth/2);
515 glBegin(GL_LINE_LOOP);
516 glVertex3f( width/2, -height/2, -depth/2);
517 glVertex3f( width/2, -height/2, depth/2);
518 glVertex3f( width/2, height/2, depth/2);
519 glVertex3f( width/2, height/2, -depth/2);
523 glVertex3f(-width/2, height/2, depth/2);
524 glVertex3f(-width/2, -height/2, -depth/2);
526 glVertex3f( width/2, height/2, depth/2);
527 glVertex3f( width/2, -height/2, -depth/2);
529 glVertex3f(-width/2, -height/2, depth/2);
530 glVertex3f(-width/2, height/2, -depth/2);
532 glVertex3f( width/2, -height/2, depth/2);
533 glVertex3f( width/2, height/2, -depth/2);
538 /* Construct stars (number of stars is dependent on size of screen) */
540 init_stars (ModeInfo *mi, int width, int height)
542 sws_configuration *sc = &scs[MI_SCREEN(mi)];
544 int size = (width > height ? width : height);
545 int nstars = size * size / 320;
548 int steps = max_size / inc;
550 glDeleteLists (sc->star_list, 1);
551 sc->star_list = glGenLists (1);
552 glNewList (sc->star_list, GL_COMPILE);
554 glEnable(GL_POINT_SMOOTH);
556 for (j = 1; j <= steps; j++)
558 glPointSize(inc * j);
560 for (i = 0; i < nstars / steps; i++)
562 glColor3f (0.6 + frand(0.3),
565 glVertex2f (2 * size * (0.5 - frand(1.0)),
566 2 * size * (0.5 - frand(1.0)));
574 /* Window management, etc
577 reshape_sws (ModeInfo *mi, int width, int height)
579 sws_configuration *sc = &scs[MI_SCREEN(mi)];
581 /* Set up matrices for perspective text display
584 GLfloat desired_aspect = (GLfloat) 3/4;
585 int w = mi->xgwa.width;
586 int h = mi->xgwa.height;
588 GLfloat rot = current_device_rotation();
592 int h2 = w * desired_aspect;
593 yoff = (h - h2) / 2; /* Wide window: letterbox at top and bottom. */
594 if (yoff < 0) yoff = 0; /* Tall window: clip off the top. */
599 glMatrixMode (GL_PROJECTION);
600 glViewport (0, yoff, w, h);
602 glMatrixMode (GL_MODELVIEW);
604 gluPerspective (80.0, 1/desired_aspect, 1000, 55000);
605 gluLookAt (0.0, 0.0, 4600.0,
609 glRotatef(rot, 0, 0, 1);
611 /* Horrible kludge to prevent the text from materializing already
612 on screen on iPhone in landscape mode.
614 if ((rot > 45 && rot < 135) ||
615 (rot < -45 && rot > -135))
621 glRotatef (-60.0, 1.0, 0.0, 0.0);
624 glRotatef (60.0, 1.0, 0.0, 0.0);
625 glTranslatef (260, 3200, 0);
626 glScalef (1.85, 1.85, 1);
629 /* The above gives us an arena where the bottom edge of the screen is
630 represented by the line (-2100,-3140,0) - ( 2100,-3140,0). */
632 /* Now let's move the origin to the front of the screen. */
633 glTranslatef (0.0, -3140, 0.0);
635 /* And then let's scale so that the bottom of the screen is 1.0 wide. */
636 glScalef (4200, 4200, 4200);
640 /* Compute the height in pixels of the line at the bottom of the screen. */
642 GLdouble mm[17], pm[17];
644 GLdouble x = 0.5, y1 = 0, z = 0;
645 GLdouble y2 = sc->line_height;
646 GLdouble wx=-1, wy1=-1, wy2=-1, wz=-1;
648 glGetDoublev (GL_MODELVIEW_MATRIX, mm);
649 glGetDoublev (GL_PROJECTION_MATRIX, pm);
650 glGetIntegerv (GL_VIEWPORT, vp);
651 gluProject (x, y1, z, mm, pm, vp, &wx, &wy1, &wz);
652 gluProject (x, y2, z, mm, pm, vp, &wx, &wy2, &wz);
653 sc->line_pixel_height = (wy2 - wy1);
657 /* Compute the best looking line thickness for the bottom line.
660 sc->line_thickness = 1.0;
662 sc->line_thickness = (GLfloat) sc->line_pixel_height / FONT_WEIGHT;
664 if (sc->line_thickness < 1.2)
665 sc->line_thickness = 1.0;
670 gl_init (ModeInfo *mi)
672 sws_configuration *sc = &scs[MI_SCREEN(mi)];
674 program = get_string_resource (mi->dpy, "program", "Program");
676 glDisable (GL_LIGHTING);
677 glDisable (GL_DEPTH_TEST);
681 glEnable (GL_LINE_SMOOTH);
682 glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
683 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
687 sc->text_list = glGenLists (1);
688 glNewList (sc->text_list, GL_COMPILE);
691 sc->star_list = glGenLists (1);
692 glNewList (sc->star_list, GL_COMPILE);
695 sc->line_thickness = 1.0;
700 init_sws (ModeInfo *mi)
704 sws_configuration *sc = 0;
707 scs = (sws_configuration *)
708 calloc (MI_NUM_SCREENS(mi), sizeof (sws_configuration));
710 fprintf(stderr, "%s: out of memory\n", progname);
715 sc = &scs[MI_SCREEN(mi)];
717 sc->dpy = MI_DISPLAY(mi);
718 sc = &scs[MI_SCREEN(mi)];
719 sc->lines = (char **) calloc (max_lines+1, sizeof(char *));
721 if ((sc->glx_context = init_GL(mi)) != NULL) {
723 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
724 clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */
726 init_stars (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
732 sc->texfont = load_texture_font (MI_DISPLAY(mi), "font");
733 cw = texture_string_width (sc->texfont, "n", &lh);
736 glEnable(GL_ALPHA_TEST);
737 glEnable (GL_TEXTURE_2D);
739 check_gl_error ("loading font");
741 /* "Anistropic filtering helps for quadrilateral-angled textures.
742 A sharper image is accomplished by interpolating and filtering
743 multiple samples from one or more mipmaps to better approximate
744 very distorted textures. This is the next level of filtering
745 after trilinear filtering." */
747 strstr ((char *) glGetString(GL_EXTENSIONS),
748 "GL_EXT_texture_filter_anisotropic"))
750 GLfloat anisotropic = 0.0;
751 glGetFloatv (GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropic);
752 if (anisotropic >= 1.0)
753 glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,
759 font_height = GLUT_FONT->top - GLUT_FONT->bottom;
760 sc->char_width = glutStrokeWidth (GLUT_FONT, 'z'); /* 'n' seems wide */
763 sc->font_scale = 1.0 / sc->char_width;
766 /* We consider a font that consumes 80 columns to be "18 points".
768 If neither -size nor -columns was specified, default to 60 columns
769 (which is 24 points.)
771 If both were specified, -columns has priority.
777 if (target_columns <= 0 && font_size <= 0)
780 if (target_columns > 0)
781 font_size = base_size * (base_col / (double) target_columns);
782 else if (font_size > 0)
783 target_columns = base_col * (base_size / (double) font_size);
786 sc->line_pixel_width = target_columns * sc->char_width;
788 sc->font_scale /= target_columns;
789 sc->line_height = font_height * sc->font_scale;
792 /* Buffer only two lines of text.
793 If the buffer is too big, there's a significant delay between
794 when the program launches and when the text appears, which can be
795 irritating for time-sensitive output (clock, current music, etc.)
797 sc->buf_size = target_columns * 2;
798 if (sc->buf_size < 80) sc->buf_size = 80;
799 sc->buf = (char *) calloc (1, sc->buf_size);
801 sc->total_lines = max_lines-1;
804 star_spin = -star_spin;
806 if (!alignment_str || !*alignment_str ||
807 !strcasecmp(alignment_str, "left"))
809 else if (!strcasecmp(alignment_str, "center") ||
810 !strcasecmp(alignment_str, "middle"))
812 else if (!strcasecmp(alignment_str, "right"))
817 "%s: alignment must be left, center, or right, not \"%s\"\n",
818 progname, alignment_str);
822 sc->tc = textclient_open (sc->dpy);
824 /* one more reshape, after line_height has been computed */
825 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
830 draw_stars (ModeInfo *mi)
832 sws_configuration *sc = &scs[MI_SCREEN(mi)];
834 glMatrixMode (GL_PROJECTION);
839 glMatrixMode (GL_MODELVIEW);
843 glOrtho (-0.5 * MI_WIDTH(mi), 0.5 * MI_WIDTH(mi),
844 -0.5 * MI_HEIGHT(mi), 0.5 * MI_HEIGHT(mi),
846 glRotatef (sc->star_theta, 0.0, 0.0, 1.0);
847 if (textures_p) glDisable (GL_TEXTURE_2D);
849 /* Keep the stars pointing in the same direction after rotation */
850 glRotatef(current_device_rotation(), 0, 0, 1);
852 glCallList (sc->star_list);
853 if (textures_p) glEnable (GL_TEXTURE_2D);
857 glMatrixMode (GL_PROJECTION);
862 draw_sws (ModeInfo *mi)
864 sws_configuration *sc = &scs[MI_SCREEN(mi)];
865 /* XtAppContext app = XtDisplayToApplicationContext (sc->dpy);*/
866 Display *dpy = MI_DISPLAY(mi);
867 Window window = MI_WINDOW(mi);
870 if (!sc->glx_context)
873 glDrawBuffer (GL_BACK);
874 glXMakeCurrent (dpy, window, *(sc->glx_context));
876 glClear (GL_COLOR_BUFFER_BIT);
880 glMatrixMode (GL_MODELVIEW);
884 /* Need to do this every time to get device rotation right */
885 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
892 if (textures_p) glDisable (GL_TEXTURE_2D);
894 glTranslatef (0,-1, 0);
896 glColor3f(1, 0, 0); /* Red line is where text appears */
898 glTranslatef(0, -0.028, 0);
901 glVertex3f(-0.5, 1, 0);
902 glVertex3f( 0.5, 1, 0);
903 glVertex3f(-0.5, -1, 0);
904 glVertex3f( 0.5, -1, 0);
909 glColor3f (0.4, 0.4, 0.4);
910 for (i = 0; i < 16; i++)
913 grid (1, 1, sc->char_width * sc->font_scale, sc->line_height, 0);
914 glTranslatef(0, 1, 0);
916 if (textures_p) glEnable (GL_TEXTURE_2D);
920 /* Scroll to current position */
921 glTranslatef (0.0, sc->intra_line_scroll, 0.0);
923 glColor3f (1.0, 1.0, 0.4);
924 glCallList (sc->text_list);
925 mi->polygon_count = sc->polygon_count;
927 sc->intra_line_scroll += sc->line_height / scroll_steps;
929 if (sc->intra_line_scroll >= sc->line_height)
931 sc->intra_line_scroll = 0;
933 /* Drop the oldest line off the end. */
937 /* Scroll the contents of the lines array toward 0. */
938 if (sc->total_lines > 0)
940 for (i = 1; i < sc->total_lines; i++)
941 sc->lines[i-1] = sc->lines[i];
942 sc->lines[--sc->total_lines] = 0;
945 /* Bring in new lines at the end. */
948 if (sc->total_lines < max_lines)
949 /* Oops, we ran out of text... well, insert some blank lines
950 here so that new text still pulls in from the bottom of
951 the screen, isntead of just appearing. */
952 sc->total_lines = max_lines;
954 glDeleteLists (sc->text_list, 1);
955 sc->text_list = glGenLists (1);
956 glNewList (sc->text_list, GL_COMPILE);
957 sc->polygon_count = 0;
959 glScalef (sc->font_scale, sc->font_scale, sc->font_scale);
960 for (i = 0; i < sc->total_lines; i++)
962 double fade = (fade_p ? 1.0 * i / sc->total_lines : 1.0);
963 int offscreen_lines = 2;
966 double y = ((sc->total_lines - (i + offscreen_lines) - 1)
969 char *line = sc->lines[i];
973 double xx = x * 1.4; /* a little more to the left */
975 sprintf(n, "%d:", i);
976 draw_string (sc, xx / sc->font_scale, y / sc->font_scale, n);
982 if (sc->line_thickness != 1 && !textures_p)
984 int max_thick_lines = MAX_THICK_LINES;
985 GLfloat thinnest_line = 1.0;
986 GLfloat thickest_line = sc->line_thickness;
987 GLfloat range = thickest_line - thinnest_line;
990 int j = sc->total_lines - i - 1;
992 if (j > max_thick_lines)
993 thickness = thinnest_line;
995 thickness = (thinnest_line +
996 (range * ((max_thick_lines - j) /
997 (GLfloat) max_thick_lines)));
999 glLineWidth (thickness);
1004 int n = string_width (sc, line);
1005 xoff = 1.0 - (n * sc->font_scale);
1011 glColor3f (fade, fade, 0.5 * fade);
1012 draw_string (sc, (x + xoff) / sc->font_scale, y / sc->font_scale,
1015 sc->polygon_count += strlen (line);
1023 if (mi->fps_p) do_fps (mi);
1025 glXSwapBuffers(dpy, window);
1027 sc->star_theta += star_spin;
1031 release_sws (ModeInfo *mi)
1035 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
1036 sws_configuration *sc = &scs[screen];
1038 textclient_close (sc->tc);
1040 /* #### there's more to free here */
1049 XSCREENSAVER_MODULE_2 ("StarWars", starwars, sws)