2 * fliptext, Copyright (c) 2005-2006 Jamie Zawinski <jwz@jwz.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
15 #endif /* HAVE_CONFIG_H */
21 # include <X11/Intrinsic.h>
29 # include <sys/utsname.h>
30 #endif /* HAVE_UNAME */
33 /* Utopia 800 needs 64 512x512 textures (4096x4096 bitmap).
34 Utopia 720 needs 16 512x512 textures (2048x2048 bitmap).
35 Utopia 480 needs 16 512x512 textures (2048x2048 bitmap).
36 Utopia 400 needs 4 512x512 textures (1024x1024 bitmap).
37 Utopia 180 needs 1 512x512 texture.
38 Times 240 needs 1 512x512 texture.
40 #define DEF_FONT "-*-utopia-bold-r-normal-*-*-720-*-*-*-*-iso8859-1"
41 #define DEF_COLOR "#00CCFF"
43 #define DEFAULTS "*delay: 10000 \n" \
44 "*showFPS: False \n" \
45 "*wireframe: False \n" \
46 "*font: " DEF_FONT "\n" \
47 ".foreground: " DEF_COLOR "\n" \
49 # define refresh_fliptext 0
50 # define fliptext_handle_event 0
52 #define countof(x) (sizeof((x))/sizeof((*x)))
55 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
57 #include "xlockmore.h"
60 #ifdef USE_GL /* whole file */
62 #define DEF_PROGRAM "xscreensaver-text --cols 0" /* don't wrap */
64 #define DEF_FONT_SIZE "20"
65 #define DEF_COLUMNS "80"
66 #define DEF_ALIGN "random"
67 #define DEF_SPEED "1.0"
70 #define FONT_WEIGHT 14
73 typedef enum { NEW, HESITATE, IN, LINGER, OUT, DEAD } line_state;
74 typedef enum { SCROLL_BOTTOM, SCROLL_TOP, SPIN } line_anim_type;
76 typedef struct { GLfloat x, y, z; } XYZ;
80 GLfloat width, height; /* size */
81 XYZ from, to, current; /* start, end, and current position */
82 GLfloat fth, tth, cth; /* rotation around Z */
84 int cluster_size; /* how many lines in this cluster */
85 int cluster_pos; /* position of this line in the cluster */
87 line_state state; /* current motion model */
88 int step, steps; /* progress along this path */
96 GLXContext *glx_context;
98 texture_font_data *texfont;
102 XtIntervalId pipe_timer;
103 Time subproc_relaunch_delay;
109 int char_width; /* in font units */
110 int line_height; /* in font units */
111 double font_scale; /* convert font units to display units */
113 int font_wrap_pixels; /* in font units (for wrapping text) */
115 int top_margin, bottom_margin;
116 int left_margin, right_margin;
122 line_anim_type anim_type;
127 } fliptext_configuration;
130 static fliptext_configuration *scs = NULL;
132 static char *program;
133 static int max_lines, min_lines;
134 static float font_size;
135 static int target_columns;
136 static char *alignment_str;
137 static int alignment, alignment_random_p;
138 static GLfloat speed;
140 static XrmOptionDescRec opts[] = {
141 {"-program", ".program", XrmoptionSepArg, 0 },
142 {"-lines", ".lines", XrmoptionSepArg, 0 },
143 {"-size", ".fontSize", XrmoptionSepArg, 0 },
144 {"-columns", ".columns", XrmoptionSepArg, 0 },
145 {"-speed", ".speed", XrmoptionSepArg, 0 },
146 /*{"-font", ".font", XrmoptionSepArg, 0 },*/
147 {"-alignment", ".alignment", XrmoptionSepArg, 0 },
148 {"-left", ".alignment", XrmoptionNoArg, "Left" },
149 {"-right", ".alignment", XrmoptionNoArg, "Right" },
150 {"-center", ".alignment", XrmoptionNoArg, "Center" },
153 static argtype vars[] = {
154 {&program, "program", "Program", DEF_PROGRAM, t_String},
155 {&max_lines, "lines", "Integer", DEF_LINES, t_Int},
156 {&font_size, "fontSize", "Float", DEF_FONT_SIZE, t_Float},
157 {&target_columns, "columns", "Integer", DEF_COLUMNS, t_Int},
158 {&alignment_str, "alignment", "Alignment", DEF_ALIGN, t_String},
159 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
162 ENTRYPOINT ModeSpecOpt fliptext_opts = {countof(opts), opts, countof(vars), vars, NULL};
166 /* Tabs are bad, mmmkay? */
169 untabify (const char *string)
171 const char *ostring = string;
172 char *result = (char *) malloc ((strlen(string) * 8) + 1);
182 } while (col % TAB_WIDTH);
185 else if (*string == '\r' || *string == '\n')
190 else if (*string == '\010') /* backspace */
192 if (string > ostring)
207 strip (char *s, Bool leading, Bool trailing)
211 while (L > 0 && (s[L-1] == ' ' || s[L-1] == '\t'))
216 while (*s2 == ' ' || *s2 == '\t')
229 (This bit mostly cribbed from phosphor.c)
232 static void drain_input (fliptext_configuration *sc);
235 subproc_cb (XtPointer closure, int *source, XtInputId *id)
237 fliptext_configuration *sc = (fliptext_configuration *) closure;
243 launch_text_generator (fliptext_configuration *sc)
245 XtAppContext app = XtDisplayToApplicationContext (sc->dpy);
246 char *oprogram = get_string_resource (sc->dpy, "program", "Program");
247 char *program = (char *) malloc (strlen (oprogram) + 10);
248 strcpy (program, "( ");
249 strcat (program, oprogram);
250 strcat (program, " ) 2>&1");
252 if ((sc->pipe = popen (program, "r")))
255 XtAppAddInput (app, fileno (sc->pipe),
256 (XtPointer) (XtInputReadMask | XtInputExceptMask),
257 subproc_cb, (XtPointer) sc);
267 relaunch_generator_timer (XtPointer closure, XtIntervalId *id)
269 fliptext_configuration *sc = (fliptext_configuration *) closure;
270 if (!sc->pipe_timer) abort();
272 launch_text_generator (sc);
276 /* When the subprocess has generated some output, this reads as much as it
277 can into sc->buf at sc->buf_tail.
280 drain_input (fliptext_configuration *sc)
282 XtAppContext app = XtDisplayToApplicationContext (sc->dpy);
283 if (sc->buf_tail < sc->buf_size - 2)
285 int target = sc->buf_size - sc->buf_tail - 2;
287 ? read (fileno (sc->pipe),
288 (void *) (sc->buf + sc->buf_tail),
294 sc->buf[sc->buf_tail] = 0;
300 XtRemoveInput (sc->pipe_id);
306 /* If the process didn't print a terminating newline, add one. */
307 if (sc->buf_tail > 1 &&
308 sc->buf[sc->buf_tail-1] != '\n')
310 sc->buf[sc->buf_tail++] = '\n';
311 sc->buf[sc->buf_tail] = 0;
314 /* Then add one more, just for giggles. */
315 sc->buf[sc->buf_tail++] = '\n';
316 sc->buf[sc->buf_tail] = 0;
318 /* Set up a timer to re-launch the subproc in a bit. */
319 sc->pipe_timer = XtAppAddTimeOut (app, sc->subproc_relaunch_delay,
320 relaunch_generator_timer,
328 char_width (fliptext_configuration *sc, char c)
333 return texture_string_width (sc->texfont, s, 0);
337 /* Returns a single line of text from the output buffer of the subprocess,
338 taking into account wrapping, centering, etc. Returns 0 if no complete
339 line is currently available.
342 get_one_line (fliptext_configuration *sc)
345 int wrap_pix = sc->font_wrap_pixels;
354 if (s >= sc->buf + sc->buf_tail)
355 /* Reached end of buffer before end of line. Bail. */
358 cw = char_width (sc, *s);
360 if (*s == '\r' || *s == '\n' ||
361 col_pix + cw >= wrap_pix)
365 if (*s == '\r' || *s == '\n')
367 if (*s == '\r' && s[1] == '\n') /* swallow CRLF too */
374 /* We wrapped -- try to back up to the previous word boundary. */
377 while (s2 > sc->buf && *s2 != ' ' && *s2 != '\t')
388 result = (char *) malloc (L+1);
389 memcpy (result, sc->buf, L);
394 char *ut = untabify (t);
395 strip (ut, (alignment == 0), 1); /* if centering, strip
396 leading whitespace too */
401 if (sc->buf_tail > (s - sc->buf))
403 int i = sc->buf_tail - (s - sc->buf);
404 memmove (sc->buf, s, i);
406 sc->buf[sc->buf_tail] = 0;
413 sc->buf[sc->buf_tail] = 0;
424 int tab_pix = TAB_WIDTH * sc->char_width;
425 col = TAB_WIDTH * ((col / TAB_WIDTH) + 1);
426 col_pix = tab_pix * ((col / tab_pix) + 1);
437 blank_p (const char *s)
440 if (*s != ' ' && *s != '\t' && *s != '\r' && *s != '\n')
445 /* Reads some text from the subprocess, and creates and returns a `line'
446 object. Adds that object to the lines list. Returns 0 if no text
449 If skip_blanks_p, then keep trying for new lines of text until we
450 get one that is not empty.
453 make_line (fliptext_configuration *sc, Bool skip_blanks_p)
459 s = get_one_line (sc);
460 if (s && skip_blanks_p && blank_p (s))
468 ln = (line *) calloc (1, sizeof(*ln));
471 ln->width = sc->font_scale * texture_string_width (sc->texfont, s, 0);
472 ln->height = sc->font_scale * sc->line_height;
474 memcpy (ln->color, sc->color, sizeof(ln->color));
477 if (sc->lines_size <= sc->nlines)
479 sc->lines_size = (sc->lines_size * 1.2) + sc->nlines;
480 sc->lines = (line **)
481 realloc (sc->lines, sc->lines_size * sizeof(*sc->lines));
484 fprintf (stderr, "%s: out of memory (%d lines)\n",
485 progname, sc->lines_size);
490 sc->lines[sc->nlines-1] = ln;
495 /* frees the object and removes it from the list.
498 free_line (fliptext_configuration *sc, line *line)
501 for (i = 0; i < sc->nlines; i++)
502 if (sc->lines[i] == line)
504 if (i == sc->nlines) abort();
505 for (; i < sc->nlines-1; i++)
506 sc->lines[i] = sc->lines[i+1];
516 draw_line (ModeInfo *mi, line *line)
518 int wire = MI_IS_WIREFRAME(mi);
519 fliptext_configuration *sc = &scs[MI_SCREEN(mi)];
521 if (! line->text || !*line->text ||
522 line->state == NEW || line->state == HESITATE || line->state == DEAD)
526 glTranslatef (line->current.x, line->current.y, line->current.z);
528 glRotatef (line->cth, 0, 1, 0);
531 glTranslatef (-line->width, 0, 0);
532 else if (alignment == 0)
533 glTranslatef (-line->width/2, 0, 0);
535 glScalef (sc->font_scale, sc->font_scale, sc->font_scale);
537 glColor4f (line->color[0], line->color[1], line->color[2], line->color[3]);
540 print_texture_string (sc->texfont, line->text);
544 char *s = line->text;
547 glDisable (GL_TEXTURE_2D);
548 glColor3f (0.4, 0.4, 0.4);
552 w = texture_string_width (sc->texfont, c, &h);
553 glBegin (GL_LINE_LOOP);
554 glVertex3f (0, 0, 0);
555 glVertex3f (w, 0, 0);
556 glVertex3f (w, h, 0);
557 glVertex3f (0, h, 0);
559 glTranslatef (w, 0, 0);
564 glDisable (GL_TEXTURE_2D);
565 glColor3f (0.4, 0.4, 0.4);
566 glBegin (GL_LINE_LOOP);
567 glVertex3f (0, 0, 0);
568 glVertex3f (line->width/sc->font_scale, 0, 0);
569 glVertex3f (line->width/sc->font_scale, line->height/sc->font_scale, 0);
570 glVertex3f (0, line->height/sc->font_scale, 0);
572 if (!wire) glEnable (GL_TEXTURE_2D);
577 mi->polygon_count += strlen (line->text);
581 tick_line (fliptext_configuration *sc, line *line)
583 int stagger = 30; /* frames of delay between line spin-outs */
584 int slide = 600; /* frames in a slide in/out */
585 int linger = 0; /* frames to pause with no motion */
588 if (line->state >= DEAD) abort();
589 if (++line->step >= line->steps)
594 if (linger == 0 && line->state == LINGER)
597 if (sc->anim_type != SPIN)
602 case HESITATE: /* entering state HESITATE */
603 switch (sc->anim_type)
606 line->steps = (line->cluster_pos * stagger);
609 line->steps = stagger * (line->cluster_size - line->cluster_pos);
612 line->steps = stagger * line->cluster_pos;
621 switch (sc->anim_type)
623 case SCROLL_BOTTOM: /* entering state BOTTOM IN */
626 line->from.y = (sc->bottom_margin -
628 (line->cluster_pos + 1)));
629 line->to.y += (line->height *
630 ((line->cluster_size/2.0) - line->cluster_pos));
634 case SCROLL_TOP: /* entering state TOP IN */
637 line->from.y = (sc->top_margin +
639 (line->cluster_size - line->cluster_pos)));
640 line->to.y += (line->height *
641 ((line->cluster_size/2.0) - line->cluster_pos));
645 case SPIN: /* entering state SPIN IN */
648 line->to.y += (line->height *
649 ((line->cluster_size/2.0) - line->cluster_pos));
650 line->from.y += (line->height *
651 ((line->cluster_size/2.0) - line->cluster_pos));
664 switch (sc->anim_type)
666 case SCROLL_BOTTOM: /* entering state BOTTOM OUT */
667 line->from = line->to;
669 line->to.y = (sc->top_margin +
671 (line->cluster_size - line->cluster_pos)));
675 case SCROLL_TOP: /* entering state TOP OUT */
676 line->from = line->to;
678 line->to.y = (sc->bottom_margin -
680 (line->cluster_pos + 1)));
684 case SPIN: /* entering state SPIN OUT */
685 line->from = line->to;
687 line->to.y += (line->height *
688 ((line->cluster_size/2.0) - line->cluster_pos));
690 line->fth = line->tth;
701 line->from = line->to;
702 line->steps = linger;
709 line->steps /= speed;
716 i = (double) line->step / line->steps;
718 /* Move along the path exponentially, slow side towards the middle. */
719 if (line->state == OUT)
722 ii = 1 - ((1-i) * (1-i));
724 line->current.x = line->from.x + (ii * (line->to.x - line->from.x));
725 line->current.y = line->from.y + (ii * (line->to.y - line->from.y));
726 line->current.z = line->from.z + (ii * (line->to.z - line->from.z));
727 line->cth = line->fth + (ii * (line->tth - line->fth));
729 if (line->state == OUT) ii = 1-ii;
730 line->color[3] = sc->color[3] * ii;
743 /* Start a new cluster of lines going.
744 Pick their anim type, and in, mid, and out positions.
747 reset_lines (ModeInfo *mi)
749 fliptext_configuration *sc = &scs[MI_SCREEN(mi)];
752 GLfloat minx, maxx, miny, maxy, minz, maxz, maxw, maxh;
754 sc->rotation.x = 5 - BELLRAND(10);
755 sc->rotation.y = 5 - BELLRAND(10);
756 sc->rotation.z = 5 - BELLRAND(10);
758 switch (random() % 8)
760 case 0: sc->anim_type = SCROLL_TOP; break;
761 case 1: sc->anim_type = SCROLL_BOTTOM; break;
762 default: sc->anim_type = SPIN; break;
765 minx = sc->left_margin * 0.9;
766 maxx = sc->right_margin * 0.9;
768 miny = sc->bottom_margin * 0.9;
769 maxy = sc->top_margin * 0.9;
771 minz = sc->left_margin * 5;
772 maxz = sc->right_margin * 2;
774 maxw = sc->font_wrap_pixels * sc->font_scale;
775 maxh = max_lines * sc->line_height * sc->font_scale;
777 if (maxw > maxx - minx)
779 if (maxh > maxy - miny)
782 if (alignment_random_p)
783 alignment = (random() % 3) - 1;
785 if (alignment == -1) maxx -= maxw;
786 else if (alignment == 1) minx += maxw;
787 else minx += maxw/2, maxx -= maxw/2;
792 sc->mid.x = minx + frand (maxx - minx);
793 if (sc->anim_type == SPIN)
794 sc->mid.y = miny + BELLRAND (maxy - miny);
796 sc->mid.y = miny + frand (maxy - miny);
798 sc->in.x = BELLRAND(sc->right_margin * 2) - sc->right_margin;
799 sc->out.x = BELLRAND(sc->right_margin * 2) - sc->right_margin;
801 sc->in.y = miny + frand(maxy - miny);
802 sc->out.y = miny + frand(maxy - miny);
804 sc->in.z = minz + frand(maxz - minz);
805 sc->out.z = minz + frand(maxz - minz);
809 if (sc->anim_type == SPIN && sc->in.z > 0) sc->in.z /= 4;
810 if (sc->anim_type == SPIN && sc->out.z > 0) sc->out.z /= 4;
812 for (i = 0; i < max_lines; i++)
814 line *line = make_line (sc, (i == 0));
815 if (!line) break; /* no text available */
816 if (i >= min_lines &&
817 (!line->text || !*line->text)) /* blank after min */
821 for (i = 0; i < sc->nlines; i++)
823 line *line = sc->lines[i];
826 line->from.y = sc->bottom_margin;
831 line->from.y = prev->from.y - prev->height;
832 line->to.y = prev->to.y - prev->height;
834 line->cluster_pos = i;
835 line->cluster_size = sc->nlines;
842 parse_color (ModeInfo *mi, const char *name, const char *s, GLfloat *a)
845 if (! XParseColor (MI_DISPLAY(mi), MI_COLORMAP(mi), s, &c))
847 fprintf (stderr, "%s: can't parse %s color %s", progname, name, s);
850 a[0] = c.red / 65536.0;
851 a[1] = c.green / 65536.0;
852 a[2] = c.blue / 65536.0;
857 /* Window management, etc
860 reshape_fliptext (ModeInfo *mi, int width, int height)
862 fliptext_configuration *sc = &scs[MI_SCREEN(mi)];
863 GLfloat h = (GLfloat) height / (GLfloat) width;
865 glViewport (0, 0, (GLint) width, (GLint) height);
867 glMatrixMode(GL_PROJECTION);
869 gluPerspective (60.0, 1/h, 0.01, 100.0);
871 glMatrixMode(GL_MODELVIEW);
873 gluLookAt( 0.0, 0.0, 2.6,
877 glClear(GL_COLOR_BUFFER_BIT);
879 sc->right_margin = sc->top_margin / h;
880 sc->left_margin = -sc->right_margin;
885 init_fliptext (ModeInfo *mi)
887 int wire = MI_IS_WIREFRAME(mi);
889 fliptext_configuration *sc;
892 scs = (fliptext_configuration *)
893 calloc (MI_NUM_SCREENS(mi), sizeof (fliptext_configuration));
895 fprintf(stderr, "%s: out of memory\n", progname);
899 sc = &scs[MI_SCREEN(mi)];
900 sc->lines = (line **) calloc (max_lines+1, sizeof(char *));
903 sc = &scs[MI_SCREEN(mi)];
904 sc->dpy = MI_DISPLAY(mi);
906 if ((sc->glx_context = init_GL(mi)) != NULL) {
907 reshape_fliptext (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
910 program = get_string_resource (mi->dpy, "program", "Program");
914 sc->texfont = load_texture_font (MI_DISPLAY(mi), "font");
915 check_gl_error ("loading font");
916 cw = texture_string_width (sc->texfont, "n", &lh);
918 sc->line_height = lh;
923 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
925 glEnable (GL_ALPHA_TEST);
926 glEnable (GL_TEXTURE_2D);
928 # ifdef GL_TEXTURE_MAX_ANISOTROPY_EXT
929 /* "Anistropic filtering helps for quadrilateral-angled textures.
930 A sharper image is accomplished by interpolating and filtering
931 multiple samples from one or more mipmaps to better approximate
932 very distorted textures. This is the next level of filtering
933 after trilinear filtering." */
934 glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16);
939 /* The default font is (by fiat) "18 points".
940 Interpret the user's font size request relative to that.
942 sc->font_scale = 3 * (font_size / 18.0);
944 if (target_columns <= 2) target_columns = 2;
946 /* Figure out what the wrap column should be, in font-coordinate pixels.
947 Compute it from the given -columns value, but don't let it be wider
951 GLfloat maxw = 110 * sc->line_height / sc->font_scale; /* magic... */
952 sc->font_wrap_pixels = target_columns * sc->char_width;
953 if (sc->font_wrap_pixels > maxw ||
954 sc->font_wrap_pixels <= 0)
955 sc->font_wrap_pixels = maxw;
958 sc->buf_size = target_columns * max_lines;
959 sc->buf = (char *) calloc (1, sc->buf_size);
961 sc->subproc_relaunch_delay = 2 * 1000; /* 2 seconds */
963 alignment_random_p = False;
964 if (!alignment_str || !*alignment_str ||
965 !strcasecmp(alignment_str, "left"))
967 else if (!strcasecmp(alignment_str, "center") ||
968 !strcasecmp(alignment_str, "middle"))
970 else if (!strcasecmp(alignment_str, "right"))
972 else if (!strcasecmp(alignment_str, "random"))
973 alignment = -1, alignment_random_p = True;
978 "%s: alignment must be left/center/right/random, not \"%s\"\n",
979 progname, alignment_str);
983 launch_text_generator (sc);
985 if (max_lines < 1) max_lines = 1;
986 min_lines = max_lines * 0.66;
987 if (min_lines > max_lines - 3) min_lines = max_lines - 4;
988 if (min_lines < 1) min_lines = 1;
990 parse_color (mi, "foreground",
991 get_string_resource(mi->dpy, "foreground", "Foreground"),
994 sc->top_margin = (sc->char_width * 100);
995 sc->bottom_margin = -sc->top_margin;
996 reshape_fliptext (mi, MI_WIDTH(mi), MI_HEIGHT(mi)); /* compute left/right */
1001 draw_fliptext (ModeInfo *mi)
1003 fliptext_configuration *sc = &scs[MI_SCREEN(mi)];
1004 /* XtAppContext app = XtDisplayToApplicationContext (sc->dpy);*/
1005 Display *dpy = MI_DISPLAY(mi);
1006 Window window = MI_WINDOW(mi);
1009 if (!sc->glx_context)
1012 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(sc->glx_context));
1015 if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
1016 XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
1019 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1021 mi->polygon_count = 0;
1025 GLfloat s = 3.0 / (sc->top_margin - sc->bottom_margin);
1029 glRotatef (sc->rotation.x, 1, 0, 0);
1030 glRotatef (sc->rotation.y, 0, 1, 0);
1031 glRotatef (sc->rotation.z, 0, 0, 1);
1034 glDisable (GL_TEXTURE_2D);
1036 glBegin (GL_LINE_LOOP);
1037 glVertex3f (sc->left_margin, sc->top_margin, 0);
1038 glVertex3f (sc->right_margin, sc->top_margin, 0);
1039 glVertex3f (sc->right_margin, sc->bottom_margin, 0);
1040 glVertex3f (sc->left_margin, sc->bottom_margin, 0);
1043 glVertex3f (sc->in.x, sc->top_margin, sc->in.z);
1044 glVertex3f (sc->in.x, sc->bottom_margin, sc->in.z);
1045 glVertex3f (sc->mid.x, sc->top_margin, sc->mid.z);
1046 glVertex3f (sc->mid.x, sc->bottom_margin, sc->mid.z);
1047 glVertex3f (sc->out.x, sc->top_margin, sc->out.z);
1048 glVertex3f (sc->out.x, sc->bottom_margin, sc->out.z);
1050 glEnable (GL_TEXTURE_2D);
1053 for (i = 0; i < sc->nlines; i++)
1055 line *line = sc->lines[i];
1056 draw_line (mi, line);
1057 tick_line (sc, line);
1060 for (i = sc->nlines-1; i >= 0; i--)
1062 line *line = sc->lines[i];
1063 if (line->state == DEAD)
1064 free_line (sc, line);
1067 if (sc->nlines == 0)
1072 if (mi->fps_p) do_fps (mi);
1074 glXSwapBuffers(dpy, window);
1078 release_fliptext (ModeInfo *mi)
1082 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
1083 fliptext_configuration *sc = &scs[screen];
1085 XtRemoveInput (sc->pipe_id);
1089 XtRemoveTimeOut (sc->pipe_timer);
1091 /* #### there's more to free here */
1099 XSCREENSAVER_MODULE ("FlipText", fliptext)