2 * fliptext, Copyright (c) 2005-2007 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 /* 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 */
73 #define DEF_FONT_SIZE "20"
74 #define DEF_COLUMNS "80"
75 #define DEF_ALIGN "random"
76 #define DEF_SPEED "1.0"
79 #define FONT_WEIGHT 14
82 typedef enum { NEW, HESITATE, IN, LINGER, OUT, DEAD } line_state;
83 typedef enum { SCROLL_BOTTOM, SCROLL_TOP, SPIN } line_anim_type;
85 typedef struct { GLfloat x, y, z; } XYZ;
89 GLfloat width, height; /* size */
90 XYZ from, to, current; /* start, end, and current position */
91 GLfloat fth, tth, cth; /* rotation around Z */
93 int cluster_size; /* how many lines in this cluster */
94 int cluster_pos; /* position of this line in the cluster */
96 line_state state; /* current motion model */
97 int step, steps; /* progress along this path */
105 GLXContext *glx_context;
107 texture_font_data *texfont;
111 XtIntervalId pipe_timer;
112 Time subproc_relaunch_delay;
118 int char_width; /* in font units */
119 int line_height; /* in font units */
120 double font_scale; /* convert font units to display units */
122 int font_wrap_pixels; /* in font units (for wrapping text) */
124 int top_margin, bottom_margin;
125 int left_margin, right_margin;
131 line_anim_type anim_type;
136 } fliptext_configuration;
139 static fliptext_configuration *scs = NULL;
141 static char *program;
142 static int max_lines, min_lines;
143 static float font_size;
144 static int target_columns;
145 static char *alignment_str;
146 static int alignment, alignment_random_p;
147 static GLfloat speed;
149 static XrmOptionDescRec opts[] = {
150 {"-program", ".program", XrmoptionSepArg, 0 },
151 {"-lines", ".lines", XrmoptionSepArg, 0 },
152 {"-size", ".fontSize", XrmoptionSepArg, 0 },
153 {"-columns", ".columns", XrmoptionSepArg, 0 },
154 {"-speed", ".speed", XrmoptionSepArg, 0 },
155 /*{"-font", ".font", XrmoptionSepArg, 0 },*/
156 {"-alignment", ".alignment", XrmoptionSepArg, 0 },
157 {"-left", ".alignment", XrmoptionNoArg, "Left" },
158 {"-right", ".alignment", XrmoptionNoArg, "Right" },
159 {"-center", ".alignment", XrmoptionNoArg, "Center" },
162 static argtype vars[] = {
163 {&program, "program", "Program", DEF_PROGRAM, t_String},
164 {&max_lines, "lines", "Integer", DEF_LINES, t_Int},
165 {&font_size, "fontSize", "Float", DEF_FONT_SIZE, t_Float},
166 {&target_columns, "columns", "Integer", DEF_COLUMNS, t_Int},
167 {&alignment_str, "alignment", "Alignment", DEF_ALIGN, t_String},
168 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
171 ENTRYPOINT ModeSpecOpt fliptext_opts = {countof(opts), opts, countof(vars), vars, NULL};
175 /* Tabs are bad, mmmkay? */
178 untabify (const char *string)
180 const char *ostring = string;
181 char *result = (char *) malloc ((strlen(string) * 8) + 1);
191 } while (col % TAB_WIDTH);
194 else if (*string == '\r' || *string == '\n')
199 else if (*string == '\010') /* backspace */
201 if (string > ostring)
216 strip (char *s, Bool leading, Bool trailing)
220 while (L > 0 && (s[L-1] == ' ' || s[L-1] == '\t'))
225 while (*s2 == ' ' || *s2 == '\t')
238 (This bit mostly cribbed from phosphor.c)
241 static void drain_input (fliptext_configuration *sc);
244 subproc_cb (XtPointer closure, int *source, XtInputId *id)
246 fliptext_configuration *sc = (fliptext_configuration *) closure;
252 launch_text_generator (fliptext_configuration *sc)
254 XtAppContext app = XtDisplayToApplicationContext (sc->dpy);
255 char *oprogram = get_string_resource (sc->dpy, "program", "Program");
256 char *program = (char *) malloc (strlen (oprogram) + 10);
257 strcpy (program, "( ");
258 strcat (program, oprogram);
259 strcat (program, " ) 2>&1");
261 if ((sc->pipe = popen (program, "r")))
264 XtAppAddInput (app, fileno (sc->pipe),
265 (XtPointer) (XtInputReadMask | XtInputExceptMask),
266 subproc_cb, (XtPointer) sc);
276 relaunch_generator_timer (XtPointer closure, XtIntervalId *id)
278 fliptext_configuration *sc = (fliptext_configuration *) closure;
279 if (!sc->pipe_timer) abort();
281 launch_text_generator (sc);
285 /* When the subprocess has generated some output, this reads as much as it
286 can into sc->buf at sc->buf_tail.
289 drain_input (fliptext_configuration *sc)
291 XtAppContext app = XtDisplayToApplicationContext (sc->dpy);
292 if (sc->buf_tail < sc->buf_size - 2)
294 int target = sc->buf_size - sc->buf_tail - 2;
296 ? read (fileno (sc->pipe),
297 (void *) (sc->buf + sc->buf_tail),
303 sc->buf[sc->buf_tail] = 0;
309 XtRemoveInput (sc->pipe_id);
315 /* If the process didn't print a terminating newline, add one. */
316 if (sc->buf_tail > 1 &&
317 sc->buf[sc->buf_tail-1] != '\n')
319 sc->buf[sc->buf_tail++] = '\n';
320 sc->buf[sc->buf_tail] = 0;
323 /* Then add one more, just for giggles. */
324 sc->buf[sc->buf_tail++] = '\n';
325 sc->buf[sc->buf_tail] = 0;
327 /* Set up a timer to re-launch the subproc in a bit. */
328 sc->pipe_timer = XtAppAddTimeOut (app, sc->subproc_relaunch_delay,
329 relaunch_generator_timer,
337 char_width (fliptext_configuration *sc, char c)
342 return texture_string_width (sc->texfont, s, 0);
346 /* Returns a single line of text from the output buffer of the subprocess,
347 taking into account wrapping, centering, etc. Returns 0 if no complete
348 line is currently available.
351 get_one_line (fliptext_configuration *sc)
354 int wrap_pix = sc->font_wrap_pixels;
363 if (s >= sc->buf + sc->buf_tail)
364 /* Reached end of buffer before end of line. Bail. */
367 cw = char_width (sc, *s);
369 if (*s == '\r' || *s == '\n' ||
370 col_pix + cw >= wrap_pix)
374 if (*s == '\r' || *s == '\n')
376 if (*s == '\r' && s[1] == '\n') /* swallow CRLF too */
383 /* We wrapped -- try to back up to the previous word boundary. */
386 while (s2 > sc->buf && *s2 != ' ' && *s2 != '\t')
397 result = (char *) malloc (L+1);
398 memcpy (result, sc->buf, L);
403 char *ut = untabify (t);
404 strip (ut, (alignment == 0), 1); /* if centering, strip
405 leading whitespace too */
410 if (sc->buf_tail > (s - sc->buf))
412 int i = sc->buf_tail - (s - sc->buf);
413 memmove (sc->buf, s, i);
415 sc->buf[sc->buf_tail] = 0;
422 sc->buf[sc->buf_tail] = 0;
433 int tab_pix = TAB_WIDTH * sc->char_width;
434 col = TAB_WIDTH * ((col / TAB_WIDTH) + 1);
435 col_pix = tab_pix * ((col / tab_pix) + 1);
446 blank_p (const char *s)
449 if (*s != ' ' && *s != '\t' && *s != '\r' && *s != '\n')
454 /* Reads some text from the subprocess, and creates and returns a `line'
455 object. Adds that object to the lines list. Returns 0 if no text
458 If skip_blanks_p, then keep trying for new lines of text until we
459 get one that is not empty.
462 make_line (fliptext_configuration *sc, Bool skip_blanks_p)
468 s = get_one_line (sc);
469 if (s && skip_blanks_p && blank_p (s))
477 ln = (line *) calloc (1, sizeof(*ln));
480 ln->width = sc->font_scale * texture_string_width (sc->texfont, s, 0);
481 ln->height = sc->font_scale * sc->line_height;
483 memcpy (ln->color, sc->color, sizeof(ln->color));
486 if (sc->lines_size <= sc->nlines)
488 sc->lines_size = (sc->lines_size * 1.2) + sc->nlines;
489 sc->lines = (line **)
490 realloc (sc->lines, sc->lines_size * sizeof(*sc->lines));
493 fprintf (stderr, "%s: out of memory (%d lines)\n",
494 progname, sc->lines_size);
499 sc->lines[sc->nlines-1] = ln;
504 /* frees the object and removes it from the list.
507 free_line (fliptext_configuration *sc, line *line)
510 for (i = 0; i < sc->nlines; i++)
511 if (sc->lines[i] == line)
513 if (i == sc->nlines) abort();
514 for (; i < sc->nlines-1; i++)
515 sc->lines[i] = sc->lines[i+1];
525 draw_line (ModeInfo *mi, line *line)
527 int wire = MI_IS_WIREFRAME(mi);
528 fliptext_configuration *sc = &scs[MI_SCREEN(mi)];
530 if (! line->text || !*line->text ||
531 line->state == NEW || line->state == HESITATE || line->state == DEAD)
535 glTranslatef (line->current.x, line->current.y, line->current.z);
537 glRotatef (line->cth, 0, 1, 0);
540 glTranslatef (-line->width, 0, 0);
541 else if (alignment == 0)
542 glTranslatef (-line->width/2, 0, 0);
544 glScalef (sc->font_scale, sc->font_scale, sc->font_scale);
546 glColor4f (line->color[0], line->color[1], line->color[2], line->color[3]);
549 print_texture_string (sc->texfont, line->text);
553 char *s = line->text;
556 glDisable (GL_TEXTURE_2D);
557 glColor3f (0.4, 0.4, 0.4);
561 w = texture_string_width (sc->texfont, c, &h);
562 glBegin (GL_LINE_LOOP);
563 glVertex3f (0, 0, 0);
564 glVertex3f (w, 0, 0);
565 glVertex3f (w, h, 0);
566 glVertex3f (0, h, 0);
568 glTranslatef (w, 0, 0);
573 glDisable (GL_TEXTURE_2D);
574 glColor3f (0.4, 0.4, 0.4);
575 glBegin (GL_LINE_LOOP);
576 glVertex3f (0, 0, 0);
577 glVertex3f (line->width/sc->font_scale, 0, 0);
578 glVertex3f (line->width/sc->font_scale, line->height/sc->font_scale, 0);
579 glVertex3f (0, line->height/sc->font_scale, 0);
581 if (!wire) glEnable (GL_TEXTURE_2D);
586 mi->polygon_count += strlen (line->text);
590 tick_line (fliptext_configuration *sc, line *line)
592 int stagger = 30; /* frames of delay between line spin-outs */
593 int slide = 600; /* frames in a slide in/out */
594 int linger = 0; /* frames to pause with no motion */
597 if (line->state >= DEAD) abort();
598 if (++line->step >= line->steps)
603 if (linger == 0 && line->state == LINGER)
606 if (sc->anim_type != SPIN)
611 case HESITATE: /* entering state HESITATE */
612 switch (sc->anim_type)
615 line->steps = (line->cluster_pos * stagger);
618 line->steps = stagger * (line->cluster_size - line->cluster_pos);
621 line->steps = stagger * line->cluster_pos;
630 switch (sc->anim_type)
632 case SCROLL_BOTTOM: /* entering state BOTTOM IN */
635 line->from.y = (sc->bottom_margin -
637 (line->cluster_pos + 1)));
638 line->to.y += (line->height *
639 ((line->cluster_size/2.0) - line->cluster_pos));
643 case SCROLL_TOP: /* entering state TOP IN */
646 line->from.y = (sc->top_margin +
648 (line->cluster_size - line->cluster_pos)));
649 line->to.y += (line->height *
650 ((line->cluster_size/2.0) - line->cluster_pos));
654 case SPIN: /* entering state SPIN IN */
657 line->to.y += (line->height *
658 ((line->cluster_size/2.0) - line->cluster_pos));
659 line->from.y += (line->height *
660 ((line->cluster_size/2.0) - line->cluster_pos));
673 switch (sc->anim_type)
675 case SCROLL_BOTTOM: /* entering state BOTTOM OUT */
676 line->from = line->to;
678 line->to.y = (sc->top_margin +
680 (line->cluster_size - line->cluster_pos)));
684 case SCROLL_TOP: /* entering state TOP OUT */
685 line->from = line->to;
687 line->to.y = (sc->bottom_margin -
689 (line->cluster_pos + 1)));
693 case SPIN: /* entering state SPIN OUT */
694 line->from = line->to;
696 line->to.y += (line->height *
697 ((line->cluster_size/2.0) - line->cluster_pos));
699 line->fth = line->tth;
710 line->from = line->to;
711 line->steps = linger;
718 line->steps /= speed;
725 i = (double) line->step / line->steps;
727 /* Move along the path exponentially, slow side towards the middle. */
728 if (line->state == OUT)
731 ii = 1 - ((1-i) * (1-i));
733 line->current.x = line->from.x + (ii * (line->to.x - line->from.x));
734 line->current.y = line->from.y + (ii * (line->to.y - line->from.y));
735 line->current.z = line->from.z + (ii * (line->to.z - line->from.z));
736 line->cth = line->fth + (ii * (line->tth - line->fth));
738 if (line->state == OUT) ii = 1-ii;
739 line->color[3] = sc->color[3] * ii;
752 /* Start a new cluster of lines going.
753 Pick their anim type, and in, mid, and out positions.
756 reset_lines (ModeInfo *mi)
758 fliptext_configuration *sc = &scs[MI_SCREEN(mi)];
761 GLfloat minx, maxx, miny, maxy, minz, maxz, maxw, maxh;
763 sc->rotation.x = 5 - BELLRAND(10);
764 sc->rotation.y = 5 - BELLRAND(10);
765 sc->rotation.z = 5 - BELLRAND(10);
767 switch (random() % 8)
769 case 0: sc->anim_type = SCROLL_TOP; break;
770 case 1: sc->anim_type = SCROLL_BOTTOM; break;
771 default: sc->anim_type = SPIN; break;
774 minx = sc->left_margin * 0.9;
775 maxx = sc->right_margin * 0.9;
777 miny = sc->bottom_margin * 0.9;
778 maxy = sc->top_margin * 0.9;
780 minz = sc->left_margin * 5;
781 maxz = sc->right_margin * 2;
783 maxw = sc->font_wrap_pixels * sc->font_scale;
784 maxh = max_lines * sc->line_height * sc->font_scale;
786 if (maxw > maxx - minx)
788 if (maxh > maxy - miny)
791 if (alignment_random_p)
792 alignment = (random() % 3) - 1;
794 if (alignment == -1) maxx -= maxw;
795 else if (alignment == 1) minx += maxw;
796 else minx += maxw/2, maxx -= maxw/2;
801 sc->mid.x = minx + frand (maxx - minx);
802 if (sc->anim_type == SPIN)
803 sc->mid.y = miny + BELLRAND (maxy - miny);
805 sc->mid.y = miny + frand (maxy - miny);
807 sc->in.x = BELLRAND(sc->right_margin * 2) - sc->right_margin;
808 sc->out.x = BELLRAND(sc->right_margin * 2) - sc->right_margin;
810 sc->in.y = miny + frand(maxy - miny);
811 sc->out.y = miny + frand(maxy - miny);
813 sc->in.z = minz + frand(maxz - minz);
814 sc->out.z = minz + frand(maxz - minz);
818 if (sc->anim_type == SPIN && sc->in.z > 0) sc->in.z /= 4;
819 if (sc->anim_type == SPIN && sc->out.z > 0) sc->out.z /= 4;
821 for (i = 0; i < max_lines; i++)
823 line *line = make_line (sc, (i == 0));
824 if (!line) break; /* no text available */
825 if (i >= min_lines &&
826 (!line->text || !*line->text)) /* blank after min */
830 for (i = 0; i < sc->nlines; i++)
832 line *line = sc->lines[i];
835 line->from.y = sc->bottom_margin;
840 line->from.y = prev->from.y - prev->height;
841 line->to.y = prev->to.y - prev->height;
843 line->cluster_pos = i;
844 line->cluster_size = sc->nlines;
851 parse_color (ModeInfo *mi, const char *name, const char *s, GLfloat *a)
854 if (! XParseColor (MI_DISPLAY(mi), MI_COLORMAP(mi), s, &c))
856 fprintf (stderr, "%s: can't parse %s color %s", progname, name, s);
859 a[0] = c.red / 65536.0;
860 a[1] = c.green / 65536.0;
861 a[2] = c.blue / 65536.0;
866 /* Window management, etc
869 reshape_fliptext (ModeInfo *mi, int width, int height)
871 fliptext_configuration *sc = &scs[MI_SCREEN(mi)];
872 GLfloat h = (GLfloat) height / (GLfloat) width;
874 glViewport (0, 0, (GLint) width, (GLint) height);
876 glMatrixMode(GL_PROJECTION);
878 gluPerspective (60.0, 1/h, 0.01, 100.0);
880 glMatrixMode(GL_MODELVIEW);
882 gluLookAt( 0.0, 0.0, 2.6,
886 glClear(GL_COLOR_BUFFER_BIT);
888 sc->right_margin = sc->top_margin / h;
889 sc->left_margin = -sc->right_margin;
894 init_fliptext (ModeInfo *mi)
896 int wire = MI_IS_WIREFRAME(mi);
898 fliptext_configuration *sc;
901 scs = (fliptext_configuration *)
902 calloc (MI_NUM_SCREENS(mi), sizeof (fliptext_configuration));
904 fprintf(stderr, "%s: out of memory\n", progname);
908 sc = &scs[MI_SCREEN(mi)];
909 sc->lines = (line **) calloc (max_lines+1, sizeof(char *));
912 sc = &scs[MI_SCREEN(mi)];
913 sc->dpy = MI_DISPLAY(mi);
915 if ((sc->glx_context = init_GL(mi)) != NULL) {
916 reshape_fliptext (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
919 program = get_string_resource (mi->dpy, "program", "Program");
923 sc->texfont = load_texture_font (MI_DISPLAY(mi), "font");
924 check_gl_error ("loading font");
925 cw = texture_string_width (sc->texfont, "n", &lh);
927 sc->line_height = lh;
932 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
934 glEnable (GL_ALPHA_TEST);
935 glEnable (GL_TEXTURE_2D);
937 /* "Anistropic filtering helps for quadrilateral-angled textures.
938 A sharper image is accomplished by interpolating and filtering
939 multiple samples from one or more mipmaps to better approximate
940 very distorted textures. This is the next level of filtering
941 after trilinear filtering." */
942 if (strstr ((char *) glGetString(GL_EXTENSIONS),
943 "GL_EXT_texture_filter_anisotropic"))
945 GLfloat anisotropic = 0.0;
946 glGetFloatv (GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropic);
947 if (anisotropic >= 1.0)
948 glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,
953 /* The default font is (by fiat) "18 points".
954 Interpret the user's font size request relative to that.
956 sc->font_scale = 3 * (font_size / 18.0);
958 if (target_columns <= 2) target_columns = 2;
960 /* Figure out what the wrap column should be, in font-coordinate pixels.
961 Compute it from the given -columns value, but don't let it be wider
965 GLfloat maxw = 110 * sc->line_height / sc->font_scale; /* magic... */
966 sc->font_wrap_pixels = target_columns * sc->char_width;
967 if (sc->font_wrap_pixels > maxw ||
968 sc->font_wrap_pixels <= 0)
969 sc->font_wrap_pixels = maxw;
972 sc->buf_size = target_columns * max_lines;
973 sc->buf = (char *) calloc (1, sc->buf_size);
975 sc->subproc_relaunch_delay = 2 * 1000; /* 2 seconds */
977 alignment_random_p = False;
978 if (!alignment_str || !*alignment_str ||
979 !strcasecmp(alignment_str, "left"))
981 else if (!strcasecmp(alignment_str, "center") ||
982 !strcasecmp(alignment_str, "middle"))
984 else if (!strcasecmp(alignment_str, "right"))
986 else if (!strcasecmp(alignment_str, "random"))
987 alignment = -1, alignment_random_p = True;
992 "%s: alignment must be left/center/right/random, not \"%s\"\n",
993 progname, alignment_str);
997 launch_text_generator (sc);
999 if (max_lines < 1) max_lines = 1;
1000 min_lines = max_lines * 0.66;
1001 if (min_lines > max_lines - 3) min_lines = max_lines - 4;
1002 if (min_lines < 1) min_lines = 1;
1004 parse_color (mi, "foreground",
1005 get_string_resource(mi->dpy, "foreground", "Foreground"),
1008 sc->top_margin = (sc->char_width * 100);
1009 sc->bottom_margin = -sc->top_margin;
1010 reshape_fliptext (mi, MI_WIDTH(mi), MI_HEIGHT(mi)); /* compute left/right */
1015 draw_fliptext (ModeInfo *mi)
1017 fliptext_configuration *sc = &scs[MI_SCREEN(mi)];
1018 /* XtAppContext app = XtDisplayToApplicationContext (sc->dpy);*/
1019 Display *dpy = MI_DISPLAY(mi);
1020 Window window = MI_WINDOW(mi);
1023 if (!sc->glx_context)
1026 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(sc->glx_context));
1029 if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
1030 XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
1033 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1035 mi->polygon_count = 0;
1039 GLfloat s = 3.0 / (sc->top_margin - sc->bottom_margin);
1043 glRotatef (sc->rotation.x, 1, 0, 0);
1044 glRotatef (sc->rotation.y, 0, 1, 0);
1045 glRotatef (sc->rotation.z, 0, 0, 1);
1048 glDisable (GL_TEXTURE_2D);
1050 glBegin (GL_LINE_LOOP);
1051 glVertex3f (sc->left_margin, sc->top_margin, 0);
1052 glVertex3f (sc->right_margin, sc->top_margin, 0);
1053 glVertex3f (sc->right_margin, sc->bottom_margin, 0);
1054 glVertex3f (sc->left_margin, sc->bottom_margin, 0);
1057 glVertex3f (sc->in.x, sc->top_margin, sc->in.z);
1058 glVertex3f (sc->in.x, sc->bottom_margin, sc->in.z);
1059 glVertex3f (sc->mid.x, sc->top_margin, sc->mid.z);
1060 glVertex3f (sc->mid.x, sc->bottom_margin, sc->mid.z);
1061 glVertex3f (sc->out.x, sc->top_margin, sc->out.z);
1062 glVertex3f (sc->out.x, sc->bottom_margin, sc->out.z);
1064 glEnable (GL_TEXTURE_2D);
1067 for (i = 0; i < sc->nlines; i++)
1069 line *line = sc->lines[i];
1070 draw_line (mi, line);
1071 tick_line (sc, line);
1074 for (i = sc->nlines-1; i >= 0; i--)
1076 line *line = sc->lines[i];
1077 if (line->state == DEAD)
1078 free_line (sc, line);
1081 if (sc->nlines == 0)
1086 if (mi->fps_p) do_fps (mi);
1088 glXSwapBuffers(dpy, window);
1092 release_fliptext (ModeInfo *mi)
1096 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
1097 fliptext_configuration *sc = &scs[screen];
1099 XtRemoveInput (sc->pipe_id);
1103 XtRemoveTimeOut (sc->pipe_timer);
1105 /* #### there's more to free here */
1113 XSCREENSAVER_MODULE ("FlipText", fliptext)