1 /* starwars, Copyright (c) 1998-2011 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 -texture
30 #endif /* HAVE_CONFIG_H */
40 # include <X11/Intrinsic.h>
44 #define DEFAULTS "*delay: 40000 \n" \
45 "*showFPS: False \n" \
47 "*font: " DEF_FONT "\n"
49 # define refresh_sws 0
50 # define sws_handle_event 0
52 #define countof(x) (sizeof((x))/sizeof((*x)))
54 #include "xlockmore.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
97 #include "glutstroke.h"
98 #include "glut_roman.h"
99 #define GLUT_FONT (&glutStrokeRoman)
103 GLXContext *glx_context;
105 GLuint text_list, star_list;
106 texture_font_data *texfont;
111 XtIntervalId pipe_timer;
112 Time subproc_relaunch_delay;
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 (This bit mostly cribbed from phosphor.c)
288 static void drain_input (sws_configuration *sc);
291 subproc_cb (XtPointer closure, int *source, XtInputId *id)
293 sws_configuration *sc = (sws_configuration *) closure;
299 launch_text_generator (sws_configuration *sc)
301 XtAppContext app = XtDisplayToApplicationContext (sc->dpy);
302 char *oprogram = get_string_resource (sc->dpy, "program", "Program");
303 char *program = (char *) malloc (strlen (oprogram) + 10);
304 strcpy (program, "( ");
305 strcat (program, oprogram);
306 strcat (program, " ) 2>&1");
308 if ((sc->pipe = popen (program, "r")))
311 XtAppAddInput (app, fileno (sc->pipe),
312 (XtPointer) (XtInputReadMask | XtInputExceptMask),
313 subproc_cb, (XtPointer) sc);
323 relaunch_generator_timer (XtPointer closure, XtIntervalId *id)
325 sws_configuration *sc = (sws_configuration *) closure;
326 if (!sc->pipe_timer) abort();
328 launch_text_generator (sc);
332 /* When the subprocess has generated some output, this reads as much as it
333 can into sc->buf at sc->buf_tail.
336 drain_input (sws_configuration *sc)
338 XtAppContext app = XtDisplayToApplicationContext (sc->dpy);
339 if (sc->buf_tail < sc->buf_size - 2)
341 int target = sc->buf_size - sc->buf_tail - 2;
343 ? read (fileno (sc->pipe),
344 (void *) (sc->buf + sc->buf_tail),
350 sc->buf[sc->buf_tail] = 0;
356 XtRemoveInput (sc->pipe_id);
362 /* If the process didn't print a terminating newline, add one. */
363 if (sc->buf_tail > 1 &&
364 sc->buf[sc->buf_tail-1] != '\n')
366 sc->buf[sc->buf_tail++] = '\n';
367 sc->buf[sc->buf_tail] = 0;
370 /* Then add one more, just for giggles. */
371 sc->buf[sc->buf_tail++] = '\n';
372 sc->buf[sc->buf_tail] = 0;
374 /* Set up a timer to re-launch the subproc in a bit. */
375 sc->pipe_timer = XtAppAddTimeOut (app, sc->subproc_relaunch_delay,
376 relaunch_generator_timer,
384 string_width (sws_configuration *sc, const char *s)
387 return texture_string_width (sc->texfont, s, 0);
389 return glutStrokeLength (GLUT_FONT, (unsigned char *) s);
393 char_width (sws_configuration *sc, char c)
398 return string_width (sc, s);
402 /* Populates the sc->lines list with as many lines as are currently in
403 sc->buf (which was filled by drain_input().
406 get_more_lines (sws_configuration *sc)
408 /* wrap anyway, if it's absurdly long. */
409 int wrap_pix = (wrap_p ? sc->line_pixel_width : 10000);
415 while (sc->total_lines < max_lines)
419 if (s >= sc->buf + sc->buf_tail)
420 /* Reached end of buffer before end of line. Bail. */
423 cw = char_width (sc, *s);
425 if (*s == '\r' || *s == '\n' ||
426 col_pix + cw >= wrap_pix)
430 if (*s == '\r' || *s == '\n')
432 if (*s == '\r' && s[1] == '\n') /* swallow CRLF too */
439 /* We wrapped -- try to back up to the previous word boundary. */
442 while (s2 > sc->buf && *s2 != ' ' && *s2 != '\t')
452 sc->lines[sc->total_lines] = (char *) malloc (L+1);
453 memcpy (sc->lines[sc->total_lines], sc->buf, L);
454 sc->lines[sc->total_lines][L] = 0;
457 latin1_to_ascii (sc->lines[sc->total_lines]);
460 char *t = sc->lines[sc->total_lines];
461 char *ut = untabify (t);
462 strip (ut, (alignment == 0), 1); /* if centering, strip
463 leading whitespace too */
464 sc->lines[sc->total_lines] = ut;
470 if (sc->buf_tail > (s - sc->buf))
472 int i = sc->buf_tail - (s - sc->buf);
473 memmove (sc->buf, s, i);
475 sc->buf[sc->buf_tail] = 0;
482 sc->buf[sc->buf_tail] = 0;
493 int tab_pix = TAB_WIDTH * sc->char_width;
494 col = TAB_WIDTH * ((col / TAB_WIDTH) + 1);
495 col_pix = tab_pix * ((col / tab_pix) + 1);
504 draw_string (sws_configuration *sc, GLfloat x, GLfloat y, const char *s)
507 if (!s || !*s) return;
509 glTranslatef (x, y, 0);
512 print_texture_string (sc->texfont, s);
515 glutStrokeCharacter (GLUT_FONT, *s++);
521 GLfloat h = sc->line_height / sc->font_scale;
525 if (textures_p) glDisable (GL_TEXTURE_2D);
527 glColor3f (0.4, 0.4, 0.4);
529 glTranslatef (x, y, 0);
533 w = string_width (sc, c);
534 glBegin (GL_LINE_LOOP);
535 glVertex3f (0, 0, 0);
536 glVertex3f (w, 0, 0);
537 glVertex3f (w, h, 0);
538 glVertex3f (0, h, 0);
540 glTranslatef (w, 0, 0);
543 if (textures_p) glEnable (GL_TEXTURE_2D);
549 grid (double width, double height, double xspacing, double yspacing, double z)
552 for (y = 0; y <= height/2; y += yspacing)
555 glVertex3f(-width/2, y, z);
556 glVertex3f( width/2, y, z);
557 glVertex3f(-width/2, -y, z);
558 glVertex3f( width/2, -y, z);
561 for (x = 0; x <= width/2; x += xspacing)
564 glVertex3f( x, -height/2, z);
565 glVertex3f( x, height/2, z);
566 glVertex3f(-x, -height/2, z);
567 glVertex3f(-x, height/2, z);
572 glVertex3f(-width, 0, z);
573 glVertex3f( width, 0, z);
574 glVertex3f(0, -height, z);
575 glVertex3f(0, height, z);
580 box (double width, double height, double depth)
582 glBegin(GL_LINE_LOOP);
583 glVertex3f(-width/2, -height/2, -depth/2);
584 glVertex3f(-width/2, height/2, -depth/2);
585 glVertex3f( width/2, height/2, -depth/2);
586 glVertex3f( width/2, -height/2, -depth/2);
588 glBegin(GL_LINE_LOOP);
589 glVertex3f(-width/2, -height/2, depth/2);
590 glVertex3f(-width/2, height/2, depth/2);
591 glVertex3f( width/2, height/2, depth/2);
592 glVertex3f( width/2, -height/2, depth/2);
594 glBegin(GL_LINE_LOOP);
595 glVertex3f(-width/2, -height/2, -depth/2);
596 glVertex3f(-width/2, -height/2, depth/2);
597 glVertex3f(-width/2, height/2, depth/2);
598 glVertex3f(-width/2, height/2, -depth/2);
600 glBegin(GL_LINE_LOOP);
601 glVertex3f( width/2, -height/2, -depth/2);
602 glVertex3f( width/2, -height/2, depth/2);
603 glVertex3f( width/2, height/2, depth/2);
604 glVertex3f( width/2, height/2, -depth/2);
608 glVertex3f(-width/2, height/2, depth/2);
609 glVertex3f(-width/2, -height/2, -depth/2);
611 glVertex3f( width/2, height/2, depth/2);
612 glVertex3f( width/2, -height/2, -depth/2);
614 glVertex3f(-width/2, -height/2, depth/2);
615 glVertex3f(-width/2, height/2, -depth/2);
617 glVertex3f( width/2, -height/2, depth/2);
618 glVertex3f( width/2, height/2, -depth/2);
623 /* Construct stars (number of stars is dependent on size of screen) */
625 init_stars (ModeInfo *mi, int width, int height)
627 sws_configuration *sc = &scs[MI_SCREEN(mi)];
629 int size = (width > height ? width : height);
630 int nstars = size * size / 320;
633 int steps = max_size / inc;
635 glDeleteLists (sc->star_list, 1);
636 sc->star_list = glGenLists (1);
637 glNewList (sc->star_list, GL_COMPILE);
639 glEnable(GL_POINT_SMOOTH);
641 for (j = 1; j <= steps; j++)
643 glPointSize(inc * j);
645 for (i = 0; i < nstars / steps; i++)
647 glColor3f (0.6 + frand(0.3),
650 glVertex2f (2 * size * (0.5 - frand(1.0)),
651 2 * size * (0.5 - frand(1.0)));
659 /* Window management, etc
662 reshape_sws (ModeInfo *mi, int width, int height)
664 sws_configuration *sc = &scs[MI_SCREEN(mi)];
666 /* Set up matrices for perspective text display
669 GLfloat desired_aspect = (GLfloat) 3/4;
670 int w = mi->xgwa.width;
671 int h = mi->xgwa.height;
676 int h2 = w * desired_aspect;
677 yoff = (h - h2) / 2; /* Wide window: letterbox at top and bottom. */
678 if (yoff < 0) yoff = 0; /* Tall window: clip off the top. */
683 glMatrixMode (GL_PROJECTION);
684 glViewport (0, yoff, w, h);
686 glMatrixMode (GL_MODELVIEW);
688 gluPerspective (80.0, 1/desired_aspect, 1000, 55000);
689 gluLookAt (0.0, 0.0, 4600.0,
692 glRotatef (-60.0, 1.0, 0.0, 0.0);
695 glRotatef (60.0, 1.0, 0.0, 0.0);
696 glTranslatef (260, 3200, 0);
697 glScalef (1.85, 1.85, 1);
700 /* The above gives us an arena where the bottom edge of the screen is
701 represented by the line (-2100,-3140,0) - ( 2100,-3140,0). */
703 /* Now let's move the origin to the front of the screen. */
704 glTranslatef (0.0, -3140, 0.0);
706 /* And then let's scale so that the bottom of the screen is 1.0 wide. */
707 glScalef (4200, 4200, 4200);
711 /* Compute the height in pixels of the line at the bottom of the screen. */
713 GLdouble mm[17], pm[17];
715 GLfloat x = 0.5, y1 = 0, z = 0;
716 GLfloat y2 = sc->line_height;
717 GLdouble wx=-1, wy1=-1, wy2=-1, wz=-1;
719 glGetDoublev (GL_MODELVIEW_MATRIX, mm);
720 glGetDoublev (GL_PROJECTION_MATRIX, pm);
721 glGetIntegerv (GL_VIEWPORT, vp);
722 gluProject (x, y1, z, mm, pm, vp, &wx, &wy1, &wz);
723 gluProject (x, y2, z, mm, pm, vp, &wx, &wy2, &wz);
724 sc->line_pixel_height = (wy2 - wy1);
728 /* Compute the best looking line thickness for the bottom line.
731 sc->line_thickness = 1.0;
733 sc->line_thickness = (GLfloat) sc->line_pixel_height / FONT_WEIGHT;
735 if (sc->line_thickness < 1.2)
736 sc->line_thickness = 1.0;
741 gl_init (ModeInfo *mi)
743 sws_configuration *sc = &scs[MI_SCREEN(mi)];
745 program = get_string_resource (mi->dpy, "program", "Program");
747 glDisable (GL_LIGHTING);
748 glDisable (GL_DEPTH_TEST);
752 glEnable (GL_LINE_SMOOTH);
753 glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
754 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
758 sc->text_list = glGenLists (1);
759 glNewList (sc->text_list, GL_COMPILE);
762 sc->star_list = glGenLists (1);
763 glNewList (sc->star_list, GL_COMPILE);
766 sc->line_thickness = 1.0;
771 init_sws (ModeInfo *mi)
775 sws_configuration *sc = 0;
778 scs = (sws_configuration *)
779 calloc (MI_NUM_SCREENS(mi), sizeof (sws_configuration));
781 fprintf(stderr, "%s: out of memory\n", progname);
786 sc = &scs[MI_SCREEN(mi)];
788 sc->dpy = MI_DISPLAY(mi);
789 sc = &scs[MI_SCREEN(mi)];
790 sc->lines = (char **) calloc (max_lines+1, sizeof(char *));
792 if ((sc->glx_context = init_GL(mi)) != NULL) {
794 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
795 clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */
797 init_stars (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
803 sc->texfont = load_texture_font (MI_DISPLAY(mi), "font");
804 cw = texture_string_width (sc->texfont, "n", &lh);
807 glEnable(GL_ALPHA_TEST);
808 glEnable (GL_TEXTURE_2D);
810 check_gl_error ("loading font");
812 /* "Anistropic filtering helps for quadrilateral-angled textures.
813 A sharper image is accomplished by interpolating and filtering
814 multiple samples from one or more mipmaps to better approximate
815 very distorted textures. This is the next level of filtering
816 after trilinear filtering." */
818 strstr ((char *) glGetString(GL_EXTENSIONS),
819 "GL_EXT_texture_filter_anisotropic"))
821 GLfloat anisotropic = 0.0;
822 glGetFloatv (GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropic);
823 if (anisotropic >= 1.0)
824 glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,
830 font_height = GLUT_FONT->top - GLUT_FONT->bottom;
831 sc->char_width = glutStrokeWidth (GLUT_FONT, 'z'); /* 'n' seems wide */
834 sc->font_scale = 1.0 / sc->char_width;
837 /* We consider a font that consumes 80 columns to be "18 points".
839 If neither -size nor -columns was specified, default to 60 columns
840 (which is 24 points.)
842 If both were specified, -columns has priority.
848 if (target_columns <= 0 && font_size <= 0)
851 if (target_columns > 0)
852 font_size = base_size * (base_col / (double) target_columns);
853 else if (font_size > 0)
854 target_columns = base_col * (base_size / (double) font_size);
857 sc->line_pixel_width = target_columns * sc->char_width;
859 sc->font_scale /= target_columns;
860 sc->line_height = font_height * sc->font_scale;
863 /* Buffer only two lines of text.
864 If the buffer is too big, there's a significant delay between
865 when the program launches and when the text appears, which can be
866 irritating for time-sensitive output (clock, current music, etc.)
868 sc->buf_size = target_columns * 2;
869 if (sc->buf_size < 80) sc->buf_size = 80;
870 sc->buf = (char *) calloc (1, sc->buf_size);
872 sc->subproc_relaunch_delay = 2 * 1000; /* 2 seconds */
873 sc->total_lines = max_lines-1;
876 star_spin = -star_spin;
878 if (!alignment_str || !*alignment_str ||
879 !strcasecmp(alignment_str, "left"))
881 else if (!strcasecmp(alignment_str, "center") ||
882 !strcasecmp(alignment_str, "middle"))
884 else if (!strcasecmp(alignment_str, "right"))
889 "%s: alignment must be left, center, or right, not \"%s\"\n",
890 progname, alignment_str);
894 launch_text_generator (sc);
896 /* one more reshape, after line_height has been computed */
897 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
902 draw_stars (ModeInfo *mi)
904 sws_configuration *sc = &scs[MI_SCREEN(mi)];
906 glMatrixMode (GL_PROJECTION);
911 glMatrixMode (GL_MODELVIEW);
915 glOrtho (-0.5 * MI_WIDTH(mi), 0.5 * MI_WIDTH(mi),
916 -0.5 * MI_HEIGHT(mi), 0.5 * MI_HEIGHT(mi),
918 glRotatef (sc->star_theta, 0.0, 0.0, 1.0);
919 if (textures_p) glDisable (GL_TEXTURE_2D);
920 glCallList (sc->star_list);
921 if (textures_p) glEnable (GL_TEXTURE_2D);
925 glMatrixMode (GL_PROJECTION);
930 draw_sws (ModeInfo *mi)
932 sws_configuration *sc = &scs[MI_SCREEN(mi)];
933 /* XtAppContext app = XtDisplayToApplicationContext (sc->dpy);*/
934 Display *dpy = MI_DISPLAY(mi);
935 Window window = MI_WINDOW(mi);
938 if (!sc->glx_context)
942 if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
943 XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
946 glDrawBuffer (GL_BACK);
947 glXMakeCurrent (dpy, window, *(sc->glx_context));
949 glClear (GL_COLOR_BUFFER_BIT);
953 glMatrixMode (GL_MODELVIEW);
960 if (textures_p) glDisable (GL_TEXTURE_2D);
962 glColor3f (0.4, 0.4, 0.4);
963 glTranslatef (0,-1, 0);
964 for (i = 0; i < 16; i++)
967 grid (1, 1, sc->char_width * sc->font_scale, sc->line_height, 0);
968 glTranslatef(0, 1, 0);
970 if (textures_p) glEnable (GL_TEXTURE_2D);
974 /* Scroll to current position */
975 glTranslatef (0.0, sc->intra_line_scroll, 0.0);
977 glColor3f (1.0, 1.0, 0.4);
978 glCallList (sc->text_list);
979 mi->polygon_count = sc->polygon_count;
981 sc->intra_line_scroll += sc->line_height / scroll_steps;
983 if (sc->intra_line_scroll >= sc->line_height)
985 sc->intra_line_scroll = 0;
987 /* Drop the oldest line off the end. */
991 /* Scroll the contents of the lines array toward 0. */
992 if (sc->total_lines > 0)
994 for (i = 1; i < sc->total_lines; i++)
995 sc->lines[i-1] = sc->lines[i];
996 sc->lines[--sc->total_lines] = 0;
999 /* Bring in new lines at the end. */
1000 get_more_lines (sc);
1002 if (sc->total_lines < max_lines)
1003 /* Oops, we ran out of text... well, insert some blank lines
1004 here so that new text still pulls in from the bottom of
1005 the screen, isntead of just appearing. */
1006 sc->total_lines = max_lines;
1008 glDeleteLists (sc->text_list, 1);
1009 sc->text_list = glGenLists (1);
1010 glNewList (sc->text_list, GL_COMPILE);
1011 sc->polygon_count = 0;
1013 glScalef (sc->font_scale, sc->font_scale, sc->font_scale);
1014 for (i = 0; i < sc->total_lines; i++)
1016 double fade = (fade_p ? 1.0 * i / sc->total_lines : 1.0);
1017 int offscreen_lines = 2;
1020 double y = ((sc->total_lines - (i + offscreen_lines) - 1)
1023 char *line = sc->lines[i];
1027 double xx = x * 1.4; /* a little more to the left */
1029 sprintf(n, "%d:", i);
1030 draw_string (sc, xx / sc->font_scale, y / sc->font_scale, n);
1033 if (!line || !*line)
1036 if (sc->line_thickness != 1 && !textures_p)
1038 int max_thick_lines = MAX_THICK_LINES;
1039 GLfloat thinnest_line = 1.0;
1040 GLfloat thickest_line = sc->line_thickness;
1041 GLfloat range = thickest_line - thinnest_line;
1044 int j = sc->total_lines - i - 1;
1046 if (j > max_thick_lines)
1047 thickness = thinnest_line;
1049 thickness = (thinnest_line +
1050 (range * ((max_thick_lines - j) /
1051 (GLfloat) max_thick_lines)));
1053 glLineWidth (thickness);
1058 int n = string_width (sc, line);
1059 xoff = 1.0 - (n * sc->font_scale);
1065 glColor3f (fade, fade, 0.5 * fade);
1066 draw_string (sc, (x + xoff) / sc->font_scale, y / sc->font_scale,
1069 sc->polygon_count += strlen (line);
1077 if (mi->fps_p) do_fps (mi);
1079 glXSwapBuffers(dpy, window);
1081 sc->star_theta += star_spin;
1085 release_sws (ModeInfo *mi)
1089 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
1090 sws_configuration *sc = &scs[screen];
1092 XtRemoveInput (sc->pipe_id);
1096 XtRemoveTimeOut (sc->pipe_timer);
1098 /* #### there's more to free here */
1107 XSCREENSAVER_MODULE_2 ("StarWars", starwars, sws)