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 <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 init_stars (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
801 sc->texfont = load_texture_font (MI_DISPLAY(mi), "font");
802 cw = texture_string_width (sc->texfont, "n", &lh);
805 glEnable(GL_ALPHA_TEST);
806 glEnable (GL_TEXTURE_2D);
808 check_gl_error ("loading font");
810 /* "Anistropic filtering helps for quadrilateral-angled textures.
811 A sharper image is accomplished by interpolating and filtering
812 multiple samples from one or more mipmaps to better approximate
813 very distorted textures. This is the next level of filtering
814 after trilinear filtering." */
816 strstr ((char *) glGetString(GL_EXTENSIONS),
817 "GL_EXT_texture_filter_anisotropic"))
819 GLfloat anisotropic = 0.0;
820 glGetFloatv (GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropic);
821 if (anisotropic >= 1.0)
822 glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,
828 font_height = GLUT_FONT->top - GLUT_FONT->bottom;
829 sc->char_width = glutStrokeWidth (GLUT_FONT, 'z'); /* 'n' seems wide */
832 sc->font_scale = 1.0 / sc->char_width;
835 /* We consider a font that consumes 80 columns to be "18 points".
837 If neither -size nor -columns was specified, default to 60 columns
838 (which is 24 points.)
840 If both were specified, -columns has priority.
846 if (target_columns <= 0 && font_size <= 0)
849 if (target_columns > 0)
850 font_size = base_size * (base_col / (double) target_columns);
851 else if (font_size > 0)
852 target_columns = base_col * (base_size / (double) font_size);
855 sc->line_pixel_width = target_columns * sc->char_width;
857 sc->font_scale /= target_columns;
858 sc->line_height = font_height * sc->font_scale;
861 /* Buffer only two lines of text.
862 If the buffer is too big, there's a significant delay between
863 when the program launches and when the text appears, which can be
864 irritating for time-sensitive output (clock, current music, etc.)
866 sc->buf_size = target_columns * 2;
867 if (sc->buf_size < 80) sc->buf_size = 80;
868 sc->buf = (char *) calloc (1, sc->buf_size);
870 sc->subproc_relaunch_delay = 2 * 1000; /* 2 seconds */
871 sc->total_lines = max_lines-1;
874 star_spin = -star_spin;
876 if (!alignment_str || !*alignment_str ||
877 !strcasecmp(alignment_str, "left"))
879 else if (!strcasecmp(alignment_str, "center") ||
880 !strcasecmp(alignment_str, "middle"))
882 else if (!strcasecmp(alignment_str, "right"))
887 "%s: alignment must be left, center, or right, not \"%s\"\n",
888 progname, alignment_str);
892 launch_text_generator (sc);
894 /* one more reshape, after line_height has been computed */
895 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
900 draw_stars (ModeInfo *mi)
902 sws_configuration *sc = &scs[MI_SCREEN(mi)];
904 glMatrixMode (GL_PROJECTION);
909 glMatrixMode (GL_MODELVIEW);
913 glOrtho (-0.5 * MI_WIDTH(mi), 0.5 * MI_WIDTH(mi),
914 -0.5 * MI_HEIGHT(mi), 0.5 * MI_HEIGHT(mi),
916 glRotatef (sc->star_theta, 0.0, 0.0, 1.0);
917 if (textures_p) glDisable (GL_TEXTURE_2D);
918 glCallList (sc->star_list);
919 if (textures_p) glEnable (GL_TEXTURE_2D);
923 glMatrixMode (GL_PROJECTION);
928 draw_sws (ModeInfo *mi)
930 sws_configuration *sc = &scs[MI_SCREEN(mi)];
931 /* XtAppContext app = XtDisplayToApplicationContext (sc->dpy);*/
932 Display *dpy = MI_DISPLAY(mi);
933 Window window = MI_WINDOW(mi);
936 if (!sc->glx_context)
940 if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
941 XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
944 glDrawBuffer (GL_BACK);
945 glXMakeCurrent (dpy, window, *(sc->glx_context));
947 glClear (GL_COLOR_BUFFER_BIT);
951 glMatrixMode (GL_MODELVIEW);
958 if (textures_p) glDisable (GL_TEXTURE_2D);
960 glColor3f (0.4, 0.4, 0.4);
961 glTranslatef (0,-1, 0);
962 for (i = 0; i < 16; i++)
965 grid (1, 1, sc->char_width * sc->font_scale, sc->line_height, 0);
966 glTranslatef(0, 1, 0);
968 if (textures_p) glEnable (GL_TEXTURE_2D);
972 /* Scroll to current position */
973 glTranslatef (0.0, sc->intra_line_scroll, 0.0);
975 glColor3f (1.0, 1.0, 0.4);
976 glCallList (sc->text_list);
977 mi->polygon_count = sc->polygon_count;
979 sc->intra_line_scroll += sc->line_height / scroll_steps;
981 if (sc->intra_line_scroll >= sc->line_height)
983 sc->intra_line_scroll = 0;
985 /* Drop the oldest line off the end. */
989 /* Scroll the contents of the lines array toward 0. */
990 if (sc->total_lines > 0)
992 for (i = 1; i < sc->total_lines; i++)
993 sc->lines[i-1] = sc->lines[i];
994 sc->lines[--sc->total_lines] = 0;
997 /* Bring in new lines at the end. */
1000 if (sc->total_lines < max_lines)
1001 /* Oops, we ran out of text... well, insert some blank lines
1002 here so that new text still pulls in from the bottom of
1003 the screen, isntead of just appearing. */
1004 sc->total_lines = max_lines;
1006 glDeleteLists (sc->text_list, 1);
1007 sc->text_list = glGenLists (1);
1008 glNewList (sc->text_list, GL_COMPILE);
1009 sc->polygon_count = 0;
1011 glScalef (sc->font_scale, sc->font_scale, sc->font_scale);
1012 for (i = 0; i < sc->total_lines; i++)
1014 double fade = (fade_p ? 1.0 * i / sc->total_lines : 1.0);
1015 int offscreen_lines = 2;
1018 double y = ((sc->total_lines - (i + offscreen_lines) - 1)
1021 char *line = sc->lines[i];
1025 double xx = x * 1.4; /* a little more to the left */
1027 sprintf(n, "%d:", i);
1028 draw_string (sc, xx / sc->font_scale, y / sc->font_scale, n);
1031 if (!line || !*line)
1034 if (sc->line_thickness != 1 && !textures_p)
1036 int max_thick_lines = MAX_THICK_LINES;
1037 GLfloat thinnest_line = 1.0;
1038 GLfloat thickest_line = sc->line_thickness;
1039 GLfloat range = thickest_line - thinnest_line;
1042 int j = sc->total_lines - i - 1;
1044 if (j > max_thick_lines)
1045 thickness = thinnest_line;
1047 thickness = (thinnest_line +
1048 (range * ((max_thick_lines - j) /
1049 (GLfloat) max_thick_lines)));
1051 glLineWidth (thickness);
1056 int n = string_width (sc, line);
1057 xoff = 1.0 - (n * sc->font_scale);
1063 glColor3f (fade, fade, 0.5 * fade);
1064 draw_string (sc, (x + xoff) / sc->font_scale, y / sc->font_scale,
1067 sc->polygon_count += strlen (line);
1075 if (mi->fps_p) do_fps (mi);
1077 glXSwapBuffers(dpy, window);
1079 sc->star_theta += star_spin;
1083 release_sws (ModeInfo *mi)
1087 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
1088 sws_configuration *sc = &scs[screen];
1090 XtRemoveInput (sc->pipe_id);
1094 XtRemoveTimeOut (sc->pipe_timer);
1096 /* #### there's more to free here */
1105 XSCREENSAVER_MODULE_2 ("StarWars", starwars, sws)