2 * starwars, Copyright (c) 1998-2005 Jamie Zawinski <jwz@jwz.org> and
3 * Claudio Matsuoka <claudio@helllabs.org>
5 * Permission to use, copy, modify, distribute, and sell this software and its
6 * documentation for any purpose is hereby granted without fee, provided that
7 * the above copyright notice appear in all copies and that both that
8 * copyright notice and this permission notice appear in supporting
9 * documentation. No representations are made about the suitability of this
10 * software for any purpose. It is provided "as is" without express or
13 * Star Wars -- Phosphor meets a well-known scroller from a galaxy far,
16 * Feb 2000 Claudio Matsuoka First version.
17 * Jan 2001 Jamie Zawinski Rewrote large sections to add the ability to
18 * run a subprocess, customization of the font
19 * size and other parameters, etc.
20 * Feb 2001 jepler@inetnebr.com Added anti-aliased lines, and fade-to-black.
21 * Feb 2005 Jamie Zawinski Added texture fonts.
26 * starwars -program 'cat starwars.txt' -columns 25 -no-wrap -texture
29 #include <X11/Intrinsic.h>
31 extern XtAppContext app;
33 #define PROGCLASS "StarWars"
34 #define HACK_INIT init_sws
35 #define HACK_DRAW draw_sws
36 #define HACK_RESHAPE reshape_sws
37 #define sws_opts xlockmore_opts
39 #define DEF_PROGRAM "(default)"
40 #define DEF_LINES "125"
41 #define DEF_STEPS "35"
42 #define DEF_SPIN "0.03"
43 #define DEF_FONT_SIZE "-1"
44 #define DEF_COLUMNS "-1"
45 #define DEF_WRAP "True"
46 #define DEF_ALIGN "Center"
47 #define DEF_SMOOTH "True"
48 #define DEF_THICK "True"
49 #define DEF_FADE "True"
50 #define DEF_TEXTURES "True"
51 #define DEF_DEBUG "False"
53 /* Utopia 480 needs a 2048x2048 texture.
54 Utopia 400 needs a 1024x1024 texture.
55 Utopia 180 needs a 512x512 texture.
56 Times 240 needs a 512x512 texture.
58 #define DEF_FONT "-*-utopia-bold-r-normal-*-*-400-*-*-*-*-iso8859-1"
62 #define MAX_THICK_LINES 25
63 #define FONT_WEIGHT 14
66 #define DEFAULTS "*delay: 40000 \n" \
67 "*showFPS: False \n" \
69 "*program: " DEF_PROGRAM "\n" \
70 "*lines: " DEF_LINES "\n" \
71 "*spin: " DEF_SPIN "\n" \
72 "*steps: " DEF_STEPS "\n" \
73 "*smooth: " DEF_SMOOTH "\n" \
74 "*thick: " DEF_THICK "\n" \
75 "*fade: " DEF_FADE "\n" \
76 "*textures: " DEF_TEXTURES "\n" \
77 "*fontSize: " DEF_FONT_SIZE "\n" \
78 "*columns: " DEF_COLUMNS "\n" \
79 "*lineWrap: " DEF_WRAP "\n" \
80 "*alignment: " DEF_ALIGN "\n" \
81 "*font: " DEF_FONT "\n" \
82 "*debug: " DEF_DEBUG "\n" \
85 #define countof(x) (sizeof((x))/sizeof((*x)))
87 #include "xlockmore.h"
89 #ifdef USE_GL /* whole file */
95 #include "glutstroke.h"
96 #include "glut_roman.h"
97 #define GLUT_FONT (&glutStrokeRoman)
100 # include <sys/utsname.h>
101 #endif /* HAVE_UNAME */
105 GLXContext *glx_context;
107 GLuint text_list, star_list;
108 texture_font_data *texfont;
113 Time subproc_relaunch_delay;
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", ".fontSize", 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 {"-left", ".alignment", XrmoptionNoArg, "Left" },
170 {"-right", ".alignment", XrmoptionNoArg, "Right" },
171 {"-center", ".alignment", XrmoptionNoArg, "Center" },
172 {"-debug", ".debug", XrmoptionNoArg, "True" },
175 static argtype vars[] = {
176 {&program, "program", "Program", DEF_PROGRAM, t_String},
177 {&max_lines, "lines", "Integer", DEF_LINES, t_Int},
178 {&scroll_steps, "steps", "Integer", DEF_STEPS, t_Int},
179 {&star_spin, "spin", "Float", DEF_SPIN, t_Float},
180 {&font_size, "fontSize", "Float", DEF_FONT_SIZE, t_Float},
181 {&target_columns, "columns", "Integer", DEF_COLUMNS, t_Int},
182 {&wrap_p, "lineWrap", "Boolean", DEF_WRAP, t_Bool},
183 {&alignment_str, "alignment", "Alignment", DEF_ALIGN, t_String},
184 {&smooth_p, "smooth", "Boolean", DEF_SMOOTH, t_Bool},
185 {&thick_p, "thick", "Boolean", DEF_THICK, t_Bool},
186 {&fade_p, "fade", "Boolean", DEF_FADE, t_Bool},
187 {&textures_p, "textures", "Boolean", DEF_TEXTURES, t_Bool},
188 {&debug_p, "debug", "Boolean", DEF_DEBUG, t_Bool},
191 ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
195 /* Tabs are bad, mmmkay? */
198 untabify (const char *string)
200 const char *ostring = string;
201 char *result = (char *) malloc ((strlen(string) * 8) + 1);
211 } while (col % TAB_WIDTH);
214 else if (*string == '\r' || *string == '\n')
219 else if (*string == '\010') /* backspace */
221 if (string > ostring)
236 strip (char *s, Bool leading, Bool trailing)
240 while (L > 0 && (s[L-1] == ' ' || s[L-1] == '\t'))
245 while (*s2 == ' ' || *s2 == '\t')
256 /* The GLUT font only has ASCII characters in them, so do what we can to
257 convert Latin1 characters to the nearest ASCII equivalent...
260 latin1_to_ascii (char *s)
262 unsigned char *us = (unsigned char *) s;
263 const unsigned char ascii[95] = {
264 '!', 'C', '#', '#', 'Y', '|', 'S', '_', 'C', '?', '<', '=', '-', 'R', '_',
265 '?', '?', '2', '3', '\'','u', 'P', '.', ',', '1', 'o', '>', '?', '?', '?',
266 '?', 'A', 'A', 'A', 'A', 'A', 'A', 'E', 'C', 'E', 'E', 'E', 'E', 'I', 'I',
267 'I', 'I', 'D', 'N', 'O', 'O', 'O', 'O', 'O', 'x', '0', 'U', 'U', 'U', 'U',
268 'Y', 'p', 'S', 'a', 'a', 'a', 'a', 'a', 'a', 'e', 'c', 'e', 'e', 'e', 'e',
269 'i', 'i', 'i', 'i', 'o', 'n', 'o', 'o', 'o', 'o', 'o', '/', 'o', 'u', 'u',
270 'u', 'u', 'y', 'p', 'y' };
274 *us = ascii[*us - 161];
283 (This bit mostly cribbed from phosphor.c)
286 static void drain_input (sws_configuration *sc);
289 subproc_cb (XtPointer closure, int *source, XtInputId *id)
291 sws_configuration *sc = (sws_configuration *) closure;
297 launch_text_generator (sws_configuration *sc)
299 char *oprogram = get_string_resource ("program", "Program");
302 if (!strcasecmp(oprogram, "(default)"))
304 oprogram = FORTUNE_PROGRAM;
306 #if defined(__linux__) && defined(HAVE_UNAME)
308 static int done_once = 0;
314 if (uname (&uts) == 0)
316 static char cmd[200];
318 /* strip version at the first non-digit-dash-dot, to
319 lose any "SMP" crap at the end. */
320 for (s = uts.release; *s; s++)
321 if (!isdigit(*s) && *s != '.' && *s != '-')
323 sprintf (cmd, "cat /usr/src/linux-%s/README", uts.release);
324 if (!stat (cmd+4, &st))
328 /* kernel source not installed? try X... */
329 strcpy (cmd, "cat /usr/X11R6/lib/X11/doc/README");
330 if (!stat (cmd+4, &st))
336 #endif /* __linux__ && HAVE_UNAME */
338 #ifdef __APPLE__ /* MacOS X + XDarwin */
340 static int done_once = 0;
344 static char *cmd = "cat /usr/X11R6/README";
345 if (!stat (cmd+4, &st))
349 #endif /* __APPLE__ */
352 program = (char *) malloc (strlen (oprogram) + 10);
353 strcpy (program, "( ");
354 strcat (program, oprogram);
355 strcat (program, " ) 2>&1");
357 if ((sc->pipe = popen (program, "r")))
360 XtAppAddInput (app, fileno (sc->pipe),
361 (XtPointer) (XtInputReadMask | XtInputExceptMask),
362 subproc_cb, (XtPointer) sc);
372 relaunch_generator_timer (XtPointer closure, XtIntervalId *id)
374 sws_configuration *sc = (sws_configuration *) closure;
375 launch_text_generator (sc);
379 /* When the subprocess has generated some output, this reads as much as it
380 can into sc->buf at sc->buf_tail.
383 drain_input (sws_configuration *sc)
385 if (sc->buf_tail < sizeof(sc->buf) - 2)
387 int target = sizeof(sc->buf) - sc->buf_tail - 2;
388 int n = read (fileno (sc->pipe),
389 (void *) (sc->buf + sc->buf_tail),
394 sc->buf[sc->buf_tail] = 0;
398 XtRemoveInput (sc->pipe_id);
403 /* If the process didn't print a terminating newline, add one. */
404 if (sc->buf_tail > 1 &&
405 sc->buf[sc->buf_tail-1] != '\n')
407 sc->buf[sc->buf_tail++] = '\n';
408 sc->buf[sc->buf_tail] = 0;
411 /* Then add one more, just for giggles. */
412 sc->buf[sc->buf_tail++] = '\n';
413 sc->buf[sc->buf_tail] = 0;
415 /* Set up a timer to re-launch the subproc in a bit. */
416 XtAppAddTimeOut (app, sc->subproc_relaunch_delay,
417 relaunch_generator_timer,
425 string_width (sws_configuration *sc, const char *s)
428 return texture_string_width (sc->texfont, s, 0);
430 return glutStrokeLength (GLUT_FONT, (unsigned char *) s);
434 char_width (sws_configuration *sc, char c)
439 return string_width (sc, s);
443 /* Populates the sc->lines list with as many lines as are currently in
444 sc->buf (which was filled by drain_input().
447 get_more_lines (sws_configuration *sc)
449 /* wrap anyway, if it's absurdly long. */
450 int wrap_pix = (wrap_p ? sc->line_pixel_width : 10000);
456 while (sc->total_lines < max_lines)
460 if (s >= sc->buf + sc->buf_tail)
462 /* Reached end of buffer before end of line. Bail. */
466 cw = char_width (sc, *s);
468 if (*s == '\r' || *s == '\n' ||
469 col_pix + cw >= wrap_pix)
473 if (*s == '\r' || *s == '\n')
475 if (*s == '\r' && s[1] == '\n') /* swallow CRLF too */
482 /* We wrapped -- try to back up to the previous word boundary. */
485 while (s2 > sc->buf && *s2 != ' ' && *s2 != '\t')
495 sc->lines[sc->total_lines] = (char *) malloc (L+1);
496 memcpy (sc->lines[sc->total_lines], sc->buf, L);
497 sc->lines[sc->total_lines][L] = 0;
500 latin1_to_ascii (sc->lines[sc->total_lines]);
503 char *t = sc->lines[sc->total_lines];
504 char *ut = untabify (t);
505 strip (ut, (alignment == 0), 1); /* if centering, strip
506 leading whitespace too */
507 sc->lines[sc->total_lines] = ut;
513 if (sc->buf_tail > (s - sc->buf))
515 int i = sc->buf_tail - (s - sc->buf);
516 memmove (sc->buf, s, i);
518 sc->buf[sc->buf_tail] = 0;
525 sc->buf[sc->buf_tail] = 0;
536 int tab_pix = TAB_WIDTH * sc->char_width;
537 col = TAB_WIDTH * ((col / TAB_WIDTH) + 1);
538 col_pix = tab_pix * ((col / tab_pix) + 1);
547 draw_string (sws_configuration *sc, GLfloat x, GLfloat y, const char *s)
550 if (!s || !*s) return;
552 glTranslatef (x, y, 0);
555 print_texture_string (sc->texfont, s);
558 glutStrokeCharacter (GLUT_FONT, *s++);
564 GLfloat h = sc->line_height / sc->font_scale;
568 if (textures_p) glDisable (GL_TEXTURE_2D);
570 glColor3f (0.4, 0.4, 0.4);
572 glTranslatef (x, y, 0);
576 w = string_width (sc, c);
577 glBegin (GL_LINE_LOOP);
578 glVertex3f (0, 0, 0);
579 glVertex3f (w, 0, 0);
580 glVertex3f (w, h, 0);
581 glVertex3f (0, h, 0);
583 glTranslatef (w, 0, 0);
586 if (textures_p) glEnable (GL_TEXTURE_2D);
592 grid (double width, double height, double xspacing, double yspacing, double z)
595 for (y = 0; y <= height/2; y += yspacing)
598 glVertex3f(-width/2, y, z);
599 glVertex3f( width/2, y, z);
600 glVertex3f(-width/2, -y, z);
601 glVertex3f( width/2, -y, z);
604 for (x = 0; x <= width/2; x += xspacing)
607 glVertex3f( x, -height/2, z);
608 glVertex3f( x, height/2, z);
609 glVertex3f(-x, -height/2, z);
610 glVertex3f(-x, height/2, z);
615 glVertex3f(-width, 0, z);
616 glVertex3f( width, 0, z);
617 glVertex3f(0, -height, z);
618 glVertex3f(0, height, z);
623 box (double width, double height, double depth)
625 glBegin(GL_LINE_LOOP);
626 glVertex3f(-width/2, -height/2, -depth/2);
627 glVertex3f(-width/2, height/2, -depth/2);
628 glVertex3f( width/2, height/2, -depth/2);
629 glVertex3f( width/2, -height/2, -depth/2);
631 glBegin(GL_LINE_LOOP);
632 glVertex3f(-width/2, -height/2, depth/2);
633 glVertex3f(-width/2, height/2, depth/2);
634 glVertex3f( width/2, height/2, depth/2);
635 glVertex3f( width/2, -height/2, depth/2);
637 glBegin(GL_LINE_LOOP);
638 glVertex3f(-width/2, -height/2, -depth/2);
639 glVertex3f(-width/2, -height/2, depth/2);
640 glVertex3f(-width/2, height/2, depth/2);
641 glVertex3f(-width/2, height/2, -depth/2);
643 glBegin(GL_LINE_LOOP);
644 glVertex3f( width/2, -height/2, -depth/2);
645 glVertex3f( width/2, -height/2, depth/2);
646 glVertex3f( width/2, height/2, depth/2);
647 glVertex3f( width/2, height/2, -depth/2);
652 glVertex3f(-width/2, height/2, depth/2);
653 glVertex3f(-width/2, -height/2, -depth/2);
655 glVertex3f( width/2, height/2, depth/2);
656 glVertex3f( width/2, -height/2, -depth/2);
658 glVertex3f(-width/2, -height/2, depth/2);
659 glVertex3f(-width/2, height/2, -depth/2);
661 glVertex3f( width/2, -height/2, depth/2);
662 glVertex3f( width/2, height/2, -depth/2);
667 /* Construct stars (number of stars is dependent on size of screen) */
669 init_stars (ModeInfo *mi, int width, int height)
671 sws_configuration *sc = &scs[MI_SCREEN(mi)];
673 int size = (width > height ? width : height);
674 int nstars = size * size / 320;
677 int steps = max_size / inc;
679 glDeleteLists (sc->star_list, 1);
680 sc->star_list = glGenLists (1);
681 glNewList (sc->star_list, GL_COMPILE);
683 glEnable(GL_POINT_SMOOTH);
685 for (j = 1; j <= steps; j++)
687 glPointSize(inc * j);
689 for (i = 0; i < nstars / steps; i++)
691 glColor3f (0.6 + frand(0.3),
694 glVertex2f (2 * size * (0.5 - frand(1.0)),
695 2 * size * (0.5 - frand(1.0)));
703 /* Window management, etc
706 reshape_sws (ModeInfo *mi, int width, int height)
708 sws_configuration *sc = &scs[MI_SCREEN(mi)];
710 /* Set up matrices for perspective text display
713 GLfloat desired_aspect = (GLfloat) 3/4;
714 int w = mi->xgwa.width;
715 int h = mi->xgwa.height;
720 int h2 = w * desired_aspect;
721 yoff = (h - h2) / 2; /* Wide window: letterbox at top and bottom. */
722 if (yoff < 0) yoff = 0; /* Tall window: clip off the top. */
727 glMatrixMode (GL_PROJECTION);
728 glViewport (0, yoff, w, h);
730 glMatrixMode (GL_MODELVIEW);
732 gluPerspective (80.0, 1/desired_aspect, 1000, 55000);
733 gluLookAt (0.0, 0.0, 4600.0,
736 glRotatef (-60.0, 1.0, 0.0, 0.0);
739 glRotatef (60.0, 1.0, 0.0, 0.0);
740 glTranslatef (260, 3200, 0);
741 glScalef (1.85, 1.85, 1);
744 /* The above gives us an arena where the bottom edge of the screen is
745 represented by the line (-2100,-3140,0) - ( 2100,-3140,0). */
747 /* Now let's move the origin to the front of the screen. */
748 glTranslatef (0.0, -3140, 0.0);
750 /* And then let's scale so that the bottom of the screen is 1.0 wide. */
751 glScalef (4200, 4200, 4200);
755 /* Compute the height in pixels of the line at the bottom of the screen. */
757 GLdouble mm[17], pm[17];
759 GLfloat x = 0.5, y1 = 0, z = 0;
760 GLfloat y2 = sc->line_height;
761 GLdouble wx=-1, wy1=-1, wy2=-1, wz=-1;
763 glGetDoublev (GL_MODELVIEW_MATRIX, mm);
764 glGetDoublev (GL_PROJECTION_MATRIX, pm);
765 glGetIntegerv (GL_VIEWPORT, vp);
766 gluProject (x, y1, z, mm, pm, vp, &wx, &wy1, &wz);
767 gluProject (x, y2, z, mm, pm, vp, &wx, &wy2, &wz);
768 sc->line_pixel_height = (wy2 - wy1);
772 /* Compute the best looking line thickness for the bottom line.
775 sc->line_thickness = 1.0;
777 sc->line_thickness = (GLfloat) sc->line_pixel_height / FONT_WEIGHT;
779 if (sc->line_thickness < 1.2)
780 sc->line_thickness = 1.0;
785 gl_init (ModeInfo *mi)
787 sws_configuration *sc = &scs[MI_SCREEN(mi)];
789 program = get_string_resource ("program", "Program");
791 glDisable (GL_LIGHTING);
792 glDisable (GL_DEPTH_TEST);
796 glEnable (GL_LINE_SMOOTH);
797 glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
798 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
802 sc->text_list = glGenLists (1);
803 glNewList (sc->text_list, GL_COMPILE);
806 sc->star_list = glGenLists (1);
807 glNewList (sc->star_list, GL_COMPILE);
810 sc->line_thickness = 1.0;
815 init_sws (ModeInfo *mi)
819 sws_configuration *sc;
822 scs = (sws_configuration *)
823 calloc (MI_NUM_SCREENS(mi), sizeof (sws_configuration));
825 fprintf(stderr, "%s: out of memory\n", progname);
829 sc = &scs[MI_SCREEN(mi)];
830 sc->lines = (char **) calloc (max_lines+1, sizeof(char *));
833 sc = &scs[MI_SCREEN(mi)];
835 if ((sc->glx_context = init_GL(mi)) != NULL) {
837 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
838 init_stars (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
844 sc->texfont = load_texture_font (MI_DISPLAY(mi), "font");
845 cw = texture_string_width (sc->texfont, "n", &lh);
848 glEnable(GL_ALPHA_TEST);
849 glEnable (GL_TEXTURE_2D);
853 font_height = GLUT_FONT->top - GLUT_FONT->bottom;
854 sc->char_width = glutStrokeWidth (GLUT_FONT, 'z'); /* 'n' seems wide */
857 sc->font_scale = 1.0 / sc->char_width;
860 /* We consider a font that consumes 80 columns to be "18 points".
862 If neither -size nor -columns was specified, default to 60 columns
863 (which is 24 points.)
865 If both were specified, -columns has priority.
871 if (target_columns <= 0 && font_size <= 0)
874 if (target_columns > 0)
875 font_size = base_size * (base_col / (double) target_columns);
876 else if (font_size > 0)
877 target_columns = base_col * (base_size / (double) font_size);
880 sc->line_pixel_width = target_columns * sc->char_width;
882 sc->font_scale /= target_columns;
883 sc->line_height = font_height * sc->font_scale;
886 sc->subproc_relaunch_delay = 2 * 1000;
887 sc->total_lines = max_lines-1;
890 star_spin = -star_spin;
892 if (!alignment_str || !*alignment_str ||
893 !strcasecmp(alignment_str, "left"))
895 else if (!strcasecmp(alignment_str, "center") ||
896 !strcasecmp(alignment_str, "middle"))
898 else if (!strcasecmp(alignment_str, "right"))
903 "%s: alignment must be left, center, or right, not \"%s\"\n",
904 progname, alignment_str);
908 launch_text_generator (sc);
910 /* one more reshape, after line_height has been computed */
911 reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
916 draw_stars (ModeInfo *mi)
918 sws_configuration *sc = &scs[MI_SCREEN(mi)];
920 glMatrixMode (GL_PROJECTION);
925 glMatrixMode (GL_MODELVIEW);
929 glOrtho (-0.5 * MI_WIDTH(mi), 0.5 * MI_WIDTH(mi),
930 -0.5 * MI_HEIGHT(mi), 0.5 * MI_HEIGHT(mi),
932 glRotatef (sc->star_theta, 0.0, 0.0, 1.0);
933 if (textures_p) glDisable (GL_TEXTURE_2D);
934 glCallList (sc->star_list);
935 if (textures_p) glEnable (GL_TEXTURE_2D);
939 glMatrixMode (GL_PROJECTION);
944 draw_sws (ModeInfo *mi)
946 sws_configuration *sc = &scs[MI_SCREEN(mi)];
947 Display *dpy = MI_DISPLAY(mi);
948 Window window = MI_WINDOW(mi);
951 if (!sc->glx_context)
954 if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
955 XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
957 glDrawBuffer (GL_BACK);
958 glXMakeCurrent (dpy, window, *(sc->glx_context));
960 glClear (GL_COLOR_BUFFER_BIT);
964 glMatrixMode (GL_MODELVIEW);
971 if (textures_p) glDisable (GL_TEXTURE_2D);
973 glColor3f (0.4, 0.4, 0.4);
974 glTranslatef (0,-1, 0);
975 for (i = 0; i < 16; i++)
978 grid (1, 1, sc->char_width * sc->font_scale, sc->line_height, 0);
979 glTranslatef(0, 1, 0);
981 if (textures_p) glEnable (GL_TEXTURE_2D);
985 /* Scroll to current position */
986 glTranslatef (0.0, sc->intra_line_scroll, 0.0);
988 glColor3f (1.0, 1.0, 0.4);
989 glCallList (sc->text_list);
990 mi->polygon_count = sc->polygon_count;
992 sc->intra_line_scroll += sc->line_height / scroll_steps;
994 if (sc->intra_line_scroll >= sc->line_height)
996 sc->intra_line_scroll = 0;
998 /* Drop the oldest line off the end. */
1000 free (sc->lines[0]);
1002 /* Scroll the contents of the lines array toward 0. */
1003 if (sc->total_lines > 0)
1005 for (i = 1; i < sc->total_lines; i++)
1006 sc->lines[i-1] = sc->lines[i];
1007 sc->lines[--sc->total_lines] = 0;
1010 /* Bring in new lines at the end. */
1011 get_more_lines (sc);
1013 if (sc->total_lines < max_lines)
1014 /* Oops, we ran out of text... well, insert some blank lines
1015 here so that new text still pulls in from the bottom of
1016 the screen, isntead of just appearing. */
1017 sc->total_lines = max_lines;
1019 glDeleteLists (sc->text_list, 1);
1020 sc->text_list = glGenLists (1);
1021 glNewList (sc->text_list, GL_COMPILE);
1022 sc->polygon_count = 0;
1024 glScalef (sc->font_scale, sc->font_scale, sc->font_scale);
1025 for (i = 0; i < sc->total_lines; i++)
1027 double fade = (fade_p ? 1.0 * i / sc->total_lines : 1.0);
1028 int offscreen_lines = 3;
1031 double y = ((sc->total_lines - (i + offscreen_lines) - 1)
1034 char *line = sc->lines[i];
1038 double xx = x * 1.4; /* a little more to the left */
1040 sprintf(n, "%d:", i);
1041 draw_string (sc, xx / sc->font_scale, y / sc->font_scale, n);
1044 if (!line || !*line)
1047 if (sc->line_thickness != 1 && !textures_p)
1049 int max_thick_lines = MAX_THICK_LINES;
1050 GLfloat thinnest_line = 1.0;
1051 GLfloat thickest_line = sc->line_thickness;
1052 GLfloat range = thickest_line - thinnest_line;
1055 int j = sc->total_lines - i - 1;
1057 if (j > max_thick_lines)
1058 thickness = thinnest_line;
1060 thickness = (thinnest_line +
1061 (range * ((max_thick_lines - j) /
1062 (GLfloat) max_thick_lines)));
1064 glLineWidth (thickness);
1069 int n = string_width (sc, line);
1070 xoff = 1.0 - (n * sc->font_scale);
1076 glColor3f (fade, fade, 0.5 * fade);
1077 draw_string (sc, (x + xoff) / sc->font_scale, y / sc->font_scale,
1080 sc->polygon_count += strlen (line);
1088 if (mi->fps_p) do_fps (mi);
1090 glXSwapBuffers(dpy, window);
1092 sc->star_theta += star_spin;