1 /* starwars, Copyright (c) 1998-2008 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 <sys/utsname.h>
41 #endif /* HAVE_UNAME */
44 # include <X11/Intrinsic.h>
48 #define DEFAULTS "*delay: 40000 \n" \
49 "*showFPS: False \n" \
51 "*font: " DEF_FONT "\n"
53 # define refresh_sws 0
54 # define sws_handle_event 0
56 #define countof(x) (sizeof((x))/sizeof((*x)))
58 #include "xlockmore.h"
60 #ifdef USE_GL /* whole file */
62 /* Should be in <GL/glext.h> */
63 # ifndef GL_TEXTURE_MAX_ANISOTROPY_EXT
64 # define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
66 # ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT
67 # define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
71 #define DEF_PROGRAM "xscreensaver-text --cols 0" /* don't wrap */
72 #define DEF_LINES "125"
73 #define DEF_STEPS "35"
74 #define DEF_SPIN "0.03"
76 #define DEF_COLUMNS "-1"
77 #define DEF_LINE_WRAP "True"
78 #define DEF_ALIGNMENT "Center"
79 #define DEF_SMOOTH "True"
80 #define DEF_THICK "True"
81 #define DEF_FADE "True"
82 #define DEF_TEXTURES "True"
83 #define DEF_DEBUG "False"
85 /* Utopia 800 needs 64 512x512 textures (4096x4096 bitmap).
86 Utopia 720 needs 16 512x512 textures (2048x2048 bitmap).
87 Utopia 480 needs 16 512x512 textures (2048x2048 bitmap).
88 Utopia 400 needs 4 512x512 textures (1024x1024 bitmap).
89 Utopia 180 needs 1 512x512 texture.
90 Times 240 needs 1 512x512 texture.
92 #define DEF_FONT "-*-utopia-bold-r-normal-*-*-720-*-*-*-*-iso8859-1"
96 #define MAX_THICK_LINES 25
97 #define FONT_WEIGHT 14
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;
115 XtIntervalId pipe_timer;
116 Time subproc_relaunch_delay;
129 double intra_line_scroll;
131 int line_pixel_width; /* in font units (for wrapping text) */
132 int line_pixel_height; /* in screen units (for computing line thickness) */
133 GLfloat line_thickness;
138 static sws_configuration *scs = NULL;
140 static char *program;
141 static int max_lines;
142 static int scroll_steps;
143 static float star_spin;
144 static float font_size;
145 static int target_columns;
150 static int textures_p;
152 static char *alignment_str;
153 static int alignment;
155 static XrmOptionDescRec opts[] = {
156 {"-program", ".program", XrmoptionSepArg, 0 },
157 {"-lines", ".lines", XrmoptionSepArg, 0 },
158 {"-steps", ".steps", XrmoptionSepArg, 0 },
159 {"-spin", ".spin", XrmoptionSepArg, 0 },
160 {"-size", ".size", XrmoptionSepArg, 0 },
161 {"-columns", ".columns", XrmoptionSepArg, 0 },
162 /*{"-font", ".font", XrmoptionSepArg, 0 },*/
163 {"-fade", ".fade", XrmoptionNoArg, "True" },
164 {"-no-fade", ".fade", XrmoptionNoArg, "False" },
165 {"-textures", ".textures", XrmoptionNoArg, "True" },
166 {"-smooth", ".smooth", XrmoptionNoArg, "True" },
167 {"-no-smooth", ".smooth", XrmoptionNoArg, "False" },
168 {"-thick", ".thick", XrmoptionNoArg, "True" },
169 {"-no-thick", ".thick", XrmoptionNoArg, "False" },
170 {"-no-textures", ".textures", XrmoptionNoArg, "False" },
171 {"-wrap", ".lineWrap", XrmoptionNoArg, "True" },
172 {"-no-wrap", ".lineWrap", XrmoptionNoArg, "False" },
173 {"-nowrap", ".lineWrap", XrmoptionNoArg, "False" },
174 {"-alignment", ".alignment", XrmoptionSepArg, 0 },
175 {"-left", ".alignment", XrmoptionNoArg, "Left" },
176 {"-right", ".alignment", XrmoptionNoArg, "Right" },
177 {"-center", ".alignment", XrmoptionNoArg, "Center" },
178 {"-debug", ".debug", XrmoptionNoArg, "True" },
181 static argtype vars[] = {
182 {&program, "program", "Program", DEF_PROGRAM, t_String},
183 {&max_lines, "lines", "Integer", DEF_LINES, t_Int},
184 {&scroll_steps, "steps", "Integer", DEF_STEPS, t_Int},
185 {&star_spin, "spin", "Float", DEF_SPIN, t_Float},
186 {&font_size, "size", "Float", DEF_SIZE, t_Float},
187 {&target_columns, "columns", "Integer", DEF_COLUMNS, t_Int},
188 {&wrap_p, "lineWrap", "Boolean", DEF_LINE_WRAP, t_Bool},
189 {&alignment_str, "alignment", "Alignment", DEF_ALIGNMENT, t_String},
190 {&smooth_p, "smooth", "Boolean", DEF_SMOOTH, t_Bool},
191 {&thick_p, "thick", "Boolean", DEF_THICK, t_Bool},
192 {&fade_p, "fade", "Boolean", DEF_FADE, t_Bool},
193 {&textures_p, "textures", "Boolean", DEF_TEXTURES, t_Bool},
194 {&debug_p, "debug", "Boolean", DEF_DEBUG, t_Bool},
197 ENTRYPOINT ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
201 /* Tabs are bad, mmmkay? */
204 untabify (const char *string)
206 const char *ostring = string;
207 char *result = (char *) malloc ((strlen(string) * 8) + 1);
217 } while (col % TAB_WIDTH);
220 else if (*string == '\r' || *string == '\n')
225 else if (*string == '\010') /* backspace */
227 if (string > ostring)
242 strip (char *s, Bool leading, Bool trailing)
246 while (L > 0 && (s[L-1] == ' ' || s[L-1] == '\t'))
251 while (*s2 == ' ' || *s2 == '\t')
262 /* The GLUT font only has ASCII characters in them, so do what we can to
263 convert Latin1 characters to the nearest ASCII equivalent...
266 latin1_to_ascii (char *s)
268 unsigned char *us = (unsigned char *) s;
269 const unsigned char ascii[95] = {
270 '!', 'C', '#', '#', 'Y', '|', 'S', '_', 'C', '?', '<', '=', '-', 'R', '_',
271 '?', '?', '2', '3', '\'','u', 'P', '.', ',', '1', 'o', '>', '?', '?', '?',
272 '?', 'A', 'A', 'A', 'A', 'A', 'A', 'E', 'C', 'E', 'E', 'E', 'E', 'I', 'I',
273 'I', 'I', 'D', 'N', 'O', 'O', 'O', 'O', 'O', 'x', '0', 'U', 'U', 'U', 'U',
274 'Y', 'p', 'S', 'a', 'a', 'a', 'a', 'a', 'a', 'e', 'c', 'e', 'e', 'e', 'e',
275 'i', 'i', 'i', 'i', 'o', 'n', 'o', 'o', 'o', 'o', 'o', '/', 'o', 'u', 'u',
276 'u', 'u', 'y', 'p', 'y' };
280 *us = ascii[*us - 161];
289 (This bit mostly cribbed from phosphor.c)
292 static void drain_input (sws_configuration *sc);
295 subproc_cb (XtPointer closure, int *source, XtInputId *id)
297 sws_configuration *sc = (sws_configuration *) closure;
303 launch_text_generator (sws_configuration *sc)
305 XtAppContext app = XtDisplayToApplicationContext (sc->dpy);
306 char *oprogram = get_string_resource (sc->dpy, "program", "Program");
307 char *program = (char *) malloc (strlen (oprogram) + 10);
308 strcpy (program, "( ");
309 strcat (program, oprogram);
310 strcat (program, " ) 2>&1");
312 if ((sc->pipe = popen (program, "r")))
315 XtAppAddInput (app, fileno (sc->pipe),
316 (XtPointer) (XtInputReadMask | XtInputExceptMask),
317 subproc_cb, (XtPointer) sc);
327 relaunch_generator_timer (XtPointer closure, XtIntervalId *id)
329 sws_configuration *sc = (sws_configuration *) closure;
330 if (!sc->pipe_timer) abort();
332 launch_text_generator (sc);
336 /* When the subprocess has generated some output, this reads as much as it
337 can into sc->buf at sc->buf_tail.
340 drain_input (sws_configuration *sc)
342 XtAppContext app = XtDisplayToApplicationContext (sc->dpy);
343 if (sc->buf_tail < sc->buf_size - 2)
345 int target = sc->buf_size - sc->buf_tail - 2;
347 ? read (fileno (sc->pipe),
348 (void *) (sc->buf + sc->buf_tail),
354 sc->buf[sc->buf_tail] = 0;
360 XtRemoveInput (sc->pipe_id);
366 /* If the process didn't print a terminating newline, add one. */
367 if (sc->buf_tail > 1 &&
368 sc->buf[sc->buf_tail-1] != '\n')
370 sc->buf[sc->buf_tail++] = '\n';
371 sc->buf[sc->buf_tail] = 0;
374 /* Then add one more, just for giggles. */
375 sc->buf[sc->buf_tail++] = '\n';
376 sc->buf[sc->buf_tail] = 0;
378 /* Set up a timer to re-launch the subproc in a bit. */
379 sc->pipe_timer = XtAppAddTimeOut (app, sc->subproc_relaunch_delay,
380 relaunch_generator_timer,
388 string_width (sws_configuration *sc, const char *s)
391 return texture_string_width (sc->texfont, s, 0);
393 return glutStrokeLength (GLUT_FONT, (unsigned char *) s);
397 char_width (sws_configuration *sc, char c)
402 return string_width (sc, s);
406 /* Populates the sc->lines list with as many lines as are currently in
407 sc->buf (which was filled by drain_input().
410 get_more_lines (sws_configuration *sc)
412 /* wrap anyway, if it's absurdly long. */
413 int wrap_pix = (wrap_p ? sc->line_pixel_width : 10000);
419 while (sc->total_lines < max_lines)
423 if (s >= sc->buf + sc->buf_tail)
424 /* Reached end of buffer before end of line. Bail. */
427 cw = char_width (sc, *s);
429 if (*s == '\r' || *s == '\n' ||
430 col_pix + cw >= wrap_pix)
434 if (*s == '\r' || *s == '\n')
436 if (*s == '\r' && s[1] == '\n') /* swallow CRLF too */
443 /* We wrapped -- try to back up to the previous word boundary. */
446 while (s2 > sc->buf && *s2 != ' ' && *s2 != '\t')
456 sc->lines[sc->total_lines] = (char *) malloc (L+1);
457 memcpy (sc->lines[sc->total_lines], sc->buf, L);
458 sc->lines[sc->total_lines][L] = 0;
461 latin1_to_ascii (sc->lines[sc->total_lines]);
464 char *t = sc->lines[sc->total_lines];
465 char *ut = untabify (t);
466 strip (ut, (alignment == 0), 1); /* if centering, strip
467 leading whitespace too */
468 sc->lines[sc->total_lines] = ut;
474 if (sc->buf_tail > (s - sc->buf))
476 int i = sc->buf_tail - (s - sc->buf);
477 memmove (sc->buf, s, i);
479 sc->buf[sc->buf_tail] = 0;
486 sc->buf[sc->buf_tail] = 0;
497 int tab_pix = TAB_WIDTH * sc->char_width;
498 col = TAB_WIDTH * ((col / TAB_WIDTH) + 1);
499 col_pix = tab_pix * ((col / tab_pix) + 1);
508 draw_string (sws_configuration *sc, GLfloat x, GLfloat y, const char *s)
511 if (!s || !*s) return;
513 glTranslatef (x, y, 0);
516 print_texture_string (sc->texfont, s);
519 glutStrokeCharacter (GLUT_FONT, *s++);
525 GLfloat h = sc->line_height / sc->font_scale;
529 if (textures_p) glDisable (GL_TEXTURE_2D);
531 glColor3f (0.4, 0.4, 0.4);
533 glTranslatef (x, y, 0);
537 w = string_width (sc, c);
538 glBegin (GL_LINE_LOOP);
539 glVertex3f (0, 0, 0);
540 glVertex3f (w, 0, 0);
541 glVertex3f (w, h, 0);
542 glVertex3f (0, h, 0);
544 glTranslatef (w, 0, 0);
547 if (textures_p) glEnable (GL_TEXTURE_2D);
553 grid (double width, double height, double xspacing, double yspacing, double z)
556 for (y = 0; y <= height/2; y += yspacing)
559 glVertex3f(-width/2, y, z);
560 glVertex3f( width/2, y, z);
561 glVertex3f(-width/2, -y, z);
562 glVertex3f( width/2, -y, z);
565 for (x = 0; x <= width/2; x += xspacing)
568 glVertex3f( x, -height/2, z);
569 glVertex3f( x, height/2, z);
570 glVertex3f(-x, -height/2, z);
571 glVertex3f(-x, height/2, z);
576 glVertex3f(-width, 0, z);
577 glVertex3f( width, 0, z);
578 glVertex3f(0, -height, z);
579 glVertex3f(0, height, z);
584 box (double width, double height, double depth)
586 glBegin(GL_LINE_LOOP);
587 glVertex3f(-width/2, -height/2, -depth/2);
588 glVertex3f(-width/2, height/2, -depth/2);
589 glVertex3f( width/2, height/2, -depth/2);
590 glVertex3f( width/2, -height/2, -depth/2);
592 glBegin(GL_LINE_LOOP);
593 glVertex3f(-width/2, -height/2, depth/2);
594 glVertex3f(-width/2, height/2, depth/2);
595 glVertex3f( width/2, height/2, depth/2);
596 glVertex3f( width/2, -height/2, depth/2);
598 glBegin(GL_LINE_LOOP);
599 glVertex3f(-width/2, -height/2, -depth/2);
600 glVertex3f(-width/2, -height/2, depth/2);
601 glVertex3f(-width/2, height/2, depth/2);
602 glVertex3f(-width/2, height/2, -depth/2);
604 glBegin(GL_LINE_LOOP);
605 glVertex3f( width/2, -height/2, -depth/2);
606 glVertex3f( width/2, -height/2, depth/2);
607 glVertex3f( width/2, height/2, depth/2);
608 glVertex3f( width/2, height/2, -depth/2);
612 glVertex3f(-width/2, height/2, depth/2);
613 glVertex3f(-width/2, -height/2, -depth/2);
615 glVertex3f( width/2, height/2, depth/2);
616 glVertex3f( width/2, -height/2, -depth/2);
618 glVertex3f(-width/2, -height/2, depth/2);
619 glVertex3f(-width/2, height/2, -depth/2);
621 glVertex3f( width/2, -height/2, depth/2);
622 glVertex3f( width/2, height/2, -depth/2);
627 /* Construct stars (number of stars is dependent on size of screen) */
629 init_stars (ModeInfo *mi, int width, int height)
631 sws_configuration *sc = &scs[MI_SCREEN(mi)];
633 int size = (width > height ? width : height);
634 int nstars = size * size / 320;
637 int steps = max_size / inc;
639 glDeleteLists (sc->star_list, 1);
640 sc->star_list = glGenLists (1);
641 glNewList (sc->star_list, GL_COMPILE);
643 glEnable(GL_POINT_SMOOTH);
645 for (j = 1; j <= steps; j++)
647 glPointSize(inc * j);
649 for (i = 0; i < nstars / steps; i++)
651 glColor3f (0.6 + frand(0.3),
654 glVertex2f (2 * size * (0.5 - frand(1.0)),
655 2 * size * (0.5 - frand(1.0)));
663 /* Window management, etc
666 reshape_sws (ModeInfo *mi, int width, int height)
668 sws_configuration *sc = &scs[MI_SCREEN(mi)];
670 /* Set up matrices for perspective text display
673 GLfloat desired_aspect = (GLfloat) 3/4;
674 int w = mi->xgwa.width;
675 int h = mi->xgwa.height;
680 int h2 = w * desired_aspect;
681 yoff = (h - h2) / 2; /* Wide window: letterbox at top and bottom. */
682 if (yoff < 0) yoff = 0; /* Tall window: clip off the top. */
687 glMatrixMode (GL_PROJECTION);
688 glViewport (0, yoff, w, h);
690 glMatrixMode (GL_MODELVIEW);
692 gluPerspective (80.0, 1/desired_aspect, 1000, 55000);
693 gluLookAt (0.0, 0.0, 4600.0,
696 glRotatef (-60.0, 1.0, 0.0, 0.0);
699 glRotatef (60.0, 1.0, 0.0, 0.0);
700 glTranslatef (260, 3200, 0);
701 glScalef (1.85, 1.85, 1);
704 /* The above gives us an arena where the bottom edge of the screen is
705 represented by the line (-2100,-3140,0) - ( 2100,-3140,0). */
707 /* Now let's move the origin to the front of the screen. */
708 glTranslatef (0.0, -3140, 0.0);
710 /* And then let's scale so that the bottom of the screen is 1.0 wide. */
711 glScalef (4200, 4200, 4200);
715 /* Compute the height in pixels of the line at the bottom of the screen. */
717 GLdouble mm[17], pm[17];
719 GLfloat x = 0.5, y1 = 0, z = 0;
720 GLfloat y2 = sc->line_height;
721 GLdouble wx=-1, wy1=-1, wy2=-1, wz=-1;
723 glGetDoublev (GL_MODELVIEW_MATRIX, mm);
724 glGetDoublev (GL_PROJECTION_MATRIX, pm);
725 glGetIntegerv (GL_VIEWPORT, vp);
726 gluProject (x, y1, z, mm, pm, vp, &wx, &wy1, &wz);
727 gluProject (x, y2, z, mm, pm, vp, &wx, &wy2, &wz);
728 sc->line_pixel_height = (wy2 - wy1);
732 /* Compute the best looking line thickness for the bottom line.
735 sc->line_thickness = 1.0;
737 sc->line_thickness = (GLfloat) sc->line_pixel_height / FONT_WEIGHT;
739 if (sc->line_thickness < 1.2)
740 sc->line_thickness = 1.0;
745 gl_init (ModeInfo *mi)
747 sws_configuration *sc = &scs[MI_SCREEN(mi)];
749 program = get_string_resource (mi->dpy, "program", "Program");
751 glDisable (GL_LIGHTING);
752 glDisable (GL_DEPTH_TEST);
756 glEnable (GL_LINE_SMOOTH);
757 glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
758 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
762 sc->text_list = glGenLists (1);
763 glNewList (sc->text_list, GL_COMPILE);
766 sc->star_list = glGenLists (1);
767 glNewList (sc->star_list, GL_COMPILE);
770 sc->line_thickness = 1.0;
775 init_sws (ModeInfo *mi)
779 sws_configuration *sc = 0;
782 scs = (sws_configuration *)
783 calloc (MI_NUM_SCREENS(mi), sizeof (sws_configuration));
785 fprintf(stderr, "%s: out of memory\n", progname);
790 sc = &scs[MI_SCREEN(mi)];
792 sc->dpy = MI_DISPLAY(mi);
793 sc = &scs[MI_SCREEN(mi)];
794 sc->lines = (char **) calloc (max_lines+1, sizeof(char *));
796 if ((sc->glx_context = init_GL(mi)) != NULL) {
798 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
799 init_stars (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
805 sc->texfont = load_texture_font (MI_DISPLAY(mi), "font");
806 cw = texture_string_width (sc->texfont, "n", &lh);
809 glEnable(GL_ALPHA_TEST);
810 glEnable (GL_TEXTURE_2D);
812 check_gl_error ("loading font");
814 /* "Anistropic filtering helps for quadrilateral-angled textures.
815 A sharper image is accomplished by interpolating and filtering
816 multiple samples from one or more mipmaps to better approximate
817 very distorted textures. This is the next level of filtering
818 after trilinear filtering." */
820 strstr ((char *) glGetString(GL_EXTENSIONS),
821 "GL_EXT_texture_filter_anisotropic"))
823 GLfloat anisotropic = 0.0;
824 glGetFloatv (GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropic);
825 if (anisotropic >= 1.0)
826 glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,
832 font_height = GLUT_FONT->top - GLUT_FONT->bottom;
833 sc->char_width = glutStrokeWidth (GLUT_FONT, 'z'); /* 'n' seems wide */
836 sc->font_scale = 1.0 / sc->char_width;
839 /* We consider a font that consumes 80 columns to be "18 points".
841 If neither -size nor -columns was specified, default to 60 columns
842 (which is 24 points.)
844 If both were specified, -columns has priority.
850 if (target_columns <= 0 && font_size <= 0)
853 if (target_columns > 0)
854 font_size = base_size * (base_col / (double) target_columns);
855 else if (font_size > 0)
856 target_columns = base_col * (base_size / (double) font_size);
859 sc->line_pixel_width = target_columns * sc->char_width;
861 sc->font_scale /= target_columns;
862 sc->line_height = font_height * sc->font_scale;
865 /* Buffer only two lines of text.
866 If the buffer is too big, there's a significant delay between
867 when the program launches and when the text appears, which can be
868 irritating for time-sensitive output (clock, current music, etc.)
870 sc->buf_size = target_columns * 2;
871 if (sc->buf_size < 80) sc->buf_size = 80;
872 sc->buf = (char *) calloc (1, sc->buf_size);
874 sc->subproc_relaunch_delay = 2 * 1000; /* 2 seconds */
875 sc->total_lines = max_lines-1;
878 star_spin = -star_spin;
880 if (!alignment_str || !*alignment_str ||
881 !strcasecmp(alignment_str, "left"))
883 else if (!strcasecmp(alignment_str, "center") ||
884 !strcasecmp(alignment_str, "middle"))
886 else if (!strcasecmp(alignment_str, "right"))
891 "%s: alignment must be left, center, or right, not \"%s\"\n",
892 progname, alignment_str);
896 launch_text_generator (sc);
898 /* one more reshape, after line_height has been computed */
899 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
904 draw_stars (ModeInfo *mi)
906 sws_configuration *sc = &scs[MI_SCREEN(mi)];
908 glMatrixMode (GL_PROJECTION);
913 glMatrixMode (GL_MODELVIEW);
917 glOrtho (-0.5 * MI_WIDTH(mi), 0.5 * MI_WIDTH(mi),
918 -0.5 * MI_HEIGHT(mi), 0.5 * MI_HEIGHT(mi),
920 glRotatef (sc->star_theta, 0.0, 0.0, 1.0);
921 if (textures_p) glDisable (GL_TEXTURE_2D);
922 glCallList (sc->star_list);
923 if (textures_p) glEnable (GL_TEXTURE_2D);
927 glMatrixMode (GL_PROJECTION);
932 draw_sws (ModeInfo *mi)
934 sws_configuration *sc = &scs[MI_SCREEN(mi)];
935 /* XtAppContext app = XtDisplayToApplicationContext (sc->dpy);*/
936 Display *dpy = MI_DISPLAY(mi);
937 Window window = MI_WINDOW(mi);
940 if (!sc->glx_context)
944 if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
945 XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
948 glDrawBuffer (GL_BACK);
949 glXMakeCurrent (dpy, window, *(sc->glx_context));
951 glClear (GL_COLOR_BUFFER_BIT);
955 glMatrixMode (GL_MODELVIEW);
962 if (textures_p) glDisable (GL_TEXTURE_2D);
964 glColor3f (0.4, 0.4, 0.4);
965 glTranslatef (0,-1, 0);
966 for (i = 0; i < 16; i++)
969 grid (1, 1, sc->char_width * sc->font_scale, sc->line_height, 0);
970 glTranslatef(0, 1, 0);
972 if (textures_p) glEnable (GL_TEXTURE_2D);
976 /* Scroll to current position */
977 glTranslatef (0.0, sc->intra_line_scroll, 0.0);
979 glColor3f (1.0, 1.0, 0.4);
980 glCallList (sc->text_list);
981 mi->polygon_count = sc->polygon_count;
983 sc->intra_line_scroll += sc->line_height / scroll_steps;
985 if (sc->intra_line_scroll >= sc->line_height)
987 sc->intra_line_scroll = 0;
989 /* Drop the oldest line off the end. */
993 /* Scroll the contents of the lines array toward 0. */
994 if (sc->total_lines > 0)
996 for (i = 1; i < sc->total_lines; i++)
997 sc->lines[i-1] = sc->lines[i];
998 sc->lines[--sc->total_lines] = 0;
1001 /* Bring in new lines at the end. */
1002 get_more_lines (sc);
1004 if (sc->total_lines < max_lines)
1005 /* Oops, we ran out of text... well, insert some blank lines
1006 here so that new text still pulls in from the bottom of
1007 the screen, isntead of just appearing. */
1008 sc->total_lines = max_lines;
1010 glDeleteLists (sc->text_list, 1);
1011 sc->text_list = glGenLists (1);
1012 glNewList (sc->text_list, GL_COMPILE);
1013 sc->polygon_count = 0;
1015 glScalef (sc->font_scale, sc->font_scale, sc->font_scale);
1016 for (i = 0; i < sc->total_lines; i++)
1018 double fade = (fade_p ? 1.0 * i / sc->total_lines : 1.0);
1019 int offscreen_lines = 2;
1022 double y = ((sc->total_lines - (i + offscreen_lines) - 1)
1025 char *line = sc->lines[i];
1029 double xx = x * 1.4; /* a little more to the left */
1031 sprintf(n, "%d:", i);
1032 draw_string (sc, xx / sc->font_scale, y / sc->font_scale, n);
1035 if (!line || !*line)
1038 if (sc->line_thickness != 1 && !textures_p)
1040 int max_thick_lines = MAX_THICK_LINES;
1041 GLfloat thinnest_line = 1.0;
1042 GLfloat thickest_line = sc->line_thickness;
1043 GLfloat range = thickest_line - thinnest_line;
1046 int j = sc->total_lines - i - 1;
1048 if (j > max_thick_lines)
1049 thickness = thinnest_line;
1051 thickness = (thinnest_line +
1052 (range * ((max_thick_lines - j) /
1053 (GLfloat) max_thick_lines)));
1055 glLineWidth (thickness);
1060 int n = string_width (sc, line);
1061 xoff = 1.0 - (n * sc->font_scale);
1067 glColor3f (fade, fade, 0.5 * fade);
1068 draw_string (sc, (x + xoff) / sc->font_scale, y / sc->font_scale,
1071 sc->polygon_count += strlen (line);
1079 if (mi->fps_p) do_fps (mi);
1081 glXSwapBuffers(dpy, window);
1083 sc->star_theta += star_spin;
1087 release_sws (ModeInfo *mi)
1091 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
1092 sws_configuration *sc = &scs[screen];
1094 XtRemoveInput (sc->pipe_id);
1098 XtRemoveTimeOut (sc->pipe_timer);
1100 /* #### there's more to free here */
1109 XSCREENSAVER_MODULE_2 ("StarWars", starwars, sws)