X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=hacks%2Fglx%2Fstarwars.c;h=9ee8cc3f05ed11e00cdb993ae1beeac83c0842d7;hp=afbefc8624912485fc0870c3d124ee61ff1b72ae;hb=4361b69d3178d7fc98d0388f9a223af6c2651aba;hpb=ebc241816cc8e3eec7270a594bb1a607df32bcd6 diff --git a/hacks/glx/starwars.c b/hacks/glx/starwars.c index afbefc86..9ee8cc3f 100644 --- a/hacks/glx/starwars.c +++ b/hacks/glx/starwars.c @@ -1,6 +1,5 @@ -/* - * starwars, Copyright (c) 1998-2001 Jamie Zawinski and - * Claudio Matauoka +/* starwars, Copyright (c) 1998-2015 Jamie Zawinski and + * Claudio Matsuoka * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -11,135 +10,179 @@ * implied warranty. * * Star Wars -- Phosphor meets a well-known scroller from a galaxy far, - * far away. Hacked by Claudio Matsuoka. Includes portions of - * mjk's GLUT library, Copyright (c) 1994, 1995, 1996 by Mark J. - * Kilgard. Roman simplex stroke font Copyright (c) 1989, 1990, - * 1991 by Sun Microsystems, Inc. and the X Consortium. + * far away. * - * Notes: - * - I tried texturized fonts but the roman simplex stroke font - * was the most readable for the 80-column text from fortune. - * - The proportional font is bad for text from ps(1) or w(1). - * - Apparently the RIVA TNT cards for PCs don't like the stars to - * be drawn in orthogonal perspective, causing unnecessary system - * load. - * - * History: - * 20000221 claudio First version - * 20010124 jwz Rewrote large sections to add the ability to + * Feb 2000 Claudio Matsuoka First version. + * Jan 2001 Jamie Zawinski Rewrote large sections to add the ability to * run a subprocess, customization of the font * size and other parameters, etc. + * Feb 2001 jepler@inetnebr.com Added anti-aliased lines, and fade-to-black. + * Feb 2005 Jamie Zawinski Added texture fonts. + * + * + * For the fanboys: + * + * starwars -program 'cat starwars.txt' -columns 25 -no-wrap */ -#include +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ -extern XtAppContext app; +#include +#include -#define PROGCLASS "StarWars" -#define HACK_INIT init_sws -#define HACK_DRAW draw_sws -#define sws_opts xlockmore_opts +#ifdef HAVE_UNISTD_H +# include +#endif -#define DEF_PROGRAM ZIPPY_PROGRAM -#define DEF_LINES "500" -#define DEF_STEPS "35" -#define DEF_SPIN "0.03" -#define DEF_FONT_SIZE "-1" -#define DEF_COLUMNS "-1" -#define DEF_WRAP "True" -#define DEF_ALIGN "Center" +#include "starwars.h" +#define DEFAULTS "*delay: 40000 \n" \ + "*showFPS: False \n" \ + "*fpsTop: True \n" \ + "*usePty: False \n" \ + "*texFontCacheSize: 300\n" \ + "*font: " DEF_FONT "\n" \ + "*textLiteral: " DEF_TEXT "\n" \ + "*program: xscreensaver-text --cols 0" /* don't wrap */ + +# define refresh_sws 0 +# define release_sws 0 +# define sws_handle_event 0 +#undef countof +#define countof(x) (sizeof((x))/sizeof((*x))) -#define TAB_WIDTH 8 +#include "xlockmore.h" +#include "textclient.h" +#include "utf8wc.h" -#define BASE_FONT_SIZE 18 /* magic */ -#define BASE_FONT_COLUMNS 80 /* magic */ +#ifdef USE_GL /* whole file */ +/* Should be in */ +# ifndef GL_TEXTURE_MAX_ANISOTROPY_EXT +# define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +# endif +# ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT +# define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF +# endif -#define DEFAULTS "*delay: 40000 \n" \ - "*program: " DEF_PROGRAM "\n" \ - "*lines: " DEF_LINES "\n" \ - "*spin: " DEF_SPIN "\n" \ - "*steps: " DEF_STEPS "\n" \ - "*starwars.fontSize: " DEF_FONT_SIZE "\n" \ - "*starwars.columns: " DEF_COLUMNS "\n" \ - "*starwars.lineWrap: " DEF_WRAP "\n" \ - "*starwars.alignment:" DEF_ALIGN "\n" -#undef countof -#define countof(x) (sizeof((x))/sizeof((*x))) +#define DEF_LINES "125" +#define DEF_STEPS "35" +#define DEF_SPIN "0.03" +#define DEF_SIZE "-1" +#define DEF_COLUMNS "-1" +#define DEF_LINE_WRAP "True" +#define DEF_ALIGNMENT "Center" +#define DEF_SMOOTH "True" +#define DEF_THICK "True" +#define DEF_FADE "True" +#define DEF_TEXTURES "True" +#define DEF_DEBUG "False" -#include "xlockmore.h" +#define DEF_FONT "-*-utopia-bold-r-normal-*-*-360-*-*-*-*-*-*" -#ifdef USE_GL /* whole file */ +#define TAB_WIDTH 8 -#include +#define MAX_THICK_LINES 25 +#define FONT_WEIGHT 14 + +#ifndef HAVE_MOBILE +# define KEEP_ASPECT /* Letterboxing looks dumb on iPhone. */ +#endif + +#include "texfont.h" #include "glutstroke.h" #include "glut_roman.h" #define GLUT_FONT (&glutStrokeRoman) - typedef struct { + Display *dpy; GLXContext *glx_context; GLuint text_list, star_list; + texture_font_data *texfont; + text_data *tc; - FILE *pipe; - XtInputId pipe_id; - Time subproc_relaunch_delay; - - char buf [1024]; + char *buf; + int buf_size; int buf_tail; + char **lines; + int *line_widths; int total_lines; - int columns; double star_theta; + double char_width; double line_height; + double descent; double font_scale; double intra_line_scroll; + int line_pixel_width; /* in font units (for wrapping text) */ + int line_pixel_height; /* in screen units (for computing line thickness) */ + GLfloat line_thickness; + } sws_configuration; static sws_configuration *scs = NULL; -static char *program; static int max_lines; static int scroll_steps; static float star_spin; static float font_size; static int target_columns; static int wrap_p; +static int smooth_p; +static int thick_p; +static int fade_p; +static int textures_p; +static int debug_p; static char *alignment_str; static int alignment; static XrmOptionDescRec opts[] = { - {"-program", ".starwars.program", XrmoptionSepArg, (caddr_t) 0 }, - {"-lines", ".starwars.lines", XrmoptionSepArg, (caddr_t) 0 }, - {"-steps", ".starwars.steps", XrmoptionSepArg, (caddr_t) 0 }, - {"-spin", ".starwars.spin", XrmoptionSepArg, (caddr_t) 0 }, - {"-size", ".starwars.fontSize", XrmoptionSepArg, (caddr_t) 0 }, - {"-columns", ".starwars.columns", XrmoptionSepArg, (caddr_t) 0 }, - {"-wrap", ".starwars.lineWrap", XrmoptionNoArg, (caddr_t) "True" }, - {"-no-wrap", ".starwars.lineWrap", XrmoptionNoArg, (caddr_t) "False" }, - {"-nowrap", ".starwars.lineWrap", XrmoptionNoArg, (caddr_t) "False" }, - {"-left", ".starwars.alignment",XrmoptionNoArg, (caddr_t) "Left" }, - {"-right", ".starwars.alignment",XrmoptionNoArg, (caddr_t) "Right" }, - {"-center", ".starwars.alignment",XrmoptionNoArg, (caddr_t) "Center" }, + {"-lines", ".lines", XrmoptionSepArg, 0 }, + {"-steps", ".steps", XrmoptionSepArg, 0 }, + {"-spin", ".spin", XrmoptionSepArg, 0 }, + {"-size", ".size", XrmoptionSepArg, 0 }, + {"-columns", ".columns", XrmoptionSepArg, 0 }, +/*{"-font", ".font", XrmoptionSepArg, 0 },*/ + {"-fade", ".fade", XrmoptionNoArg, "True" }, + {"-no-fade", ".fade", XrmoptionNoArg, "False" }, + {"-textures", ".textures", XrmoptionNoArg, "True" }, + {"-smooth", ".smooth", XrmoptionNoArg, "True" }, + {"-no-smooth", ".smooth", XrmoptionNoArg, "False" }, + {"-thick", ".thick", XrmoptionNoArg, "True" }, + {"-no-thick", ".thick", XrmoptionNoArg, "False" }, + {"-no-textures", ".textures", XrmoptionNoArg, "False" }, + {"-wrap", ".lineWrap", XrmoptionNoArg, "True" }, + {"-no-wrap", ".lineWrap", XrmoptionNoArg, "False" }, + {"-nowrap", ".lineWrap", XrmoptionNoArg, "False" }, + {"-alignment", ".alignment", XrmoptionSepArg, 0 }, + {"-left", ".alignment", XrmoptionNoArg, "Left" }, + {"-right", ".alignment", XrmoptionNoArg, "Right" }, + {"-center", ".alignment", XrmoptionNoArg, "Center" }, + {"-debug", ".debug", XrmoptionNoArg, "True" }, }; static argtype vars[] = { - {(caddr_t *) &program, "program", "Program", DEF_PROGRAM, t_String}, - {(caddr_t *) &max_lines, "lines", "Integer", DEF_LINES, t_Int}, - {(caddr_t *) &scroll_steps, "steps", "Integer", DEF_STEPS, t_Int}, - {(caddr_t *) &star_spin, "spin", "Float", DEF_SPIN, t_Float}, - {(caddr_t *) &font_size, "fontSize","Float", DEF_STEPS, t_Float}, - {(caddr_t *) &target_columns, "columns", "Integer", DEF_COLUMNS, t_Int}, - {(caddr_t *) &wrap_p, "lineWrap","Boolean", DEF_COLUMNS, t_Bool}, - {(caddr_t *) &alignment_str, "alignment","Alignment",DEF_ALIGN, t_String}, + {&max_lines, "lines", "Integer", DEF_LINES, t_Int}, + {&scroll_steps, "steps", "Integer", DEF_STEPS, t_Int}, + {&star_spin, "spin", "Float", DEF_SPIN, t_Float}, + {&font_size, "size", "Float", DEF_SIZE, t_Float}, + {&target_columns, "columns", "Integer", DEF_COLUMNS, t_Int}, + {&wrap_p, "lineWrap", "Boolean", DEF_LINE_WRAP, t_Bool}, + {&alignment_str, "alignment", "Alignment", DEF_ALIGNMENT, t_String}, + {&smooth_p, "smooth", "Boolean", DEF_SMOOTH, t_Bool}, + {&thick_p, "thick", "Boolean", DEF_THICK, t_Bool}, + {&fade_p, "fade", "Boolean", DEF_FADE, t_Bool}, + {&textures_p, "textures", "Boolean", DEF_TEXTURES, t_Bool}, + {&debug_p, "debug", "Boolean", DEF_DEBUG, t_Bool}, }; -ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL}; +ENTRYPOINT ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL}; @@ -148,6 +191,7 @@ ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL}; static char * untabify (const char *string) { + const char *ostring = string; char *result = (char *) malloc ((strlen(string) * 8) + 1); char *out = result; int col = 0; @@ -166,6 +210,11 @@ untabify (const char *string) *out++ = *string++; col = 0; } + else if (*string == '\010') /* backspace */ + { + if (string > ostring) + out--, string++; + } else { *out++ = *string++; @@ -173,224 +222,293 @@ untabify (const char *string) } } *out = 0; + return result; } - - -/* Subprocess. - (This bit mostly cribbed from phosphor.c) - */ - -static void drain_input (sws_configuration *sc); - static void -subproc_cb (XtPointer closure, int *source, XtInputId *id) +strip (char *s, Bool leading, Bool trailing) { - sws_configuration *sc = (sws_configuration *) closure; - drain_input (sc); + int L = strlen(s); + if (trailing) + while (L > 0 && (s[L-1] == ' ' || s[L-1] == '\t')) + s[L--] = 0; + if (leading) + { + char *s2 = s; + while (*s2 == ' ' || *s2 == '\t') + s2++; + if (s == s2) + return; + while (*s2) + *s++ = *s2++; + *s = 0; + } } -static void -launch_text_generator (sws_configuration *sc) +static int +sw_string_width (sws_configuration *sc, const char *s) { - char *oprogram = get_string_resource ("program", "Program"); - char *program = (char *) malloc (strlen (oprogram) + 10); - - strcpy (program, "( "); - strcat (program, oprogram); - strcat (program, " ) 2>&1"); - - if ((sc->pipe = popen (program, "r"))) + if (textures_p) { - sc->pipe_id = - XtAppAddInput (app, fileno (sc->pipe), - (XtPointer) (XtInputReadMask | XtInputExceptMask), - subproc_cb, (XtPointer) sc); + XCharStruct e; + texture_string_metrics (sc->texfont, s, &e, 0, 0); + return e.width; } else - { - perror (program); - } + return glutStrokeLength (GLUT_FONT, (unsigned char *) s); } -static void -relaunch_generator_timer (XtPointer closure, XtIntervalId *id) +static int +sw_string_width2 (sws_configuration *sc, const char *s, size_t size) { - sws_configuration *sc = (sws_configuration *) closure; - launch_text_generator (sc); -} + char *s2 = (char *) malloc (size + 1); + int result; + strncpy (s2, s, size); + s2[size] = 0; -/* When the subprocess has generated some output, this reads as much as it - can into sc->buf at sc->buf_tail. - */ -static void -drain_input (sws_configuration *sc) -{ - if (sc->buf_tail < sizeof(sc->buf) - 2) - { - int target = sizeof(sc->buf) - sc->buf_tail - 2; - int n = read (fileno (sc->pipe), - (void *) (sc->buf + sc->buf_tail), - target); - if (n > 0) - { - sc->buf_tail += n; - sc->buf[sc->buf_tail] = 0; - } - else - { - XtRemoveInput (sc->pipe_id); - sc->pipe_id = 0; - pclose (sc->pipe); - sc->pipe = 0; - - /* If the process didn't print a terminating newline, add one. */ - if (sc->buf_tail > 1 && - sc->buf[sc->buf_tail-1] != '\n') - { - sc->buf[sc->buf_tail++] = '\n'; - sc->buf[sc->buf_tail] = 0; - } - - /* Then add one more, just for giggles. */ - sc->buf[sc->buf_tail++] = '\n'; - sc->buf[sc->buf_tail] = 0; + result = sw_string_width (sc, s2); - /* Set up a timer to re-launch the subproc in a bit. */ - XtAppAddTimeOut (app, sc->subproc_relaunch_delay, - relaunch_generator_timer, - (XtPointer) sc); - } - } + free (s2); + return result; } -/* Populates the sc->lines list with as many lines as are currently in - sc->buf (which was filled by drain_input(). +/* Populates the sc->lines list with as many lines as possible. */ static void get_more_lines (sws_configuration *sc) { - int col = 0; + /* wrap anyway, if it's absurdly long. */ + int wrap_pix = (wrap_p ? sc->line_pixel_width : 10000); + char *s = sc->buf; + + int target = sc->buf_size - sc->buf_tail - 2; + + /* Fill as much as we can into sc->buf. + */ + while (target > 0) + { + int c = textclient_getc (sc->tc); + if (c <= 0) + break; + sc->buf[sc->buf_tail++] = (char) c; + sc->buf[sc->buf_tail] = 0; + target--; + } + while (sc->total_lines < max_lines) { - if (s >= sc->buf + sc->buf_tail) + char *next_s = s; + unsigned counter = 0; + + /* OS X is really slow to calcuate the bounds for a line of text, + so skip ahead a bit. + + Really though, the right thing to do is probably to wrap + CTLineCreateTruncatedLine, one way or another. */ + for (;;) { - /* Reached end of buffer before end of line. Bail. */ - return; + if (next_s >= sc->buf + sc->buf_tail) + break; + + if (!counter) + { + if (s > sc->buf && + sw_string_width2 (sc, sc->buf, next_s - sc->buf) >= wrap_pix) + break; + + counter = 12; /* <-- Adjust to taste. */ + s = next_s; + } + + if (*next_s == '\r' || *next_s == '\n') + break; + + --counter; + ++next_s; } - if (*s == '\n' || col > sc->columns) + for (;;) { - int L = s - sc->buf; + if (s >= sc->buf + sc->buf_tail) + /* Reached end of buffer before end of line. Bail. */ + return; - if (*s == '\n') - *s++ = 0; - else + /* When checking pixel width, always measure the line from the + beginning, or else multi-byte UTF-8 characters, particularly + combining diacriticals, aren't measured right. */ + + if (*s == '\r' || *s == '\n' || + (s > sc->buf && sw_string_width2 (sc, sc->buf, s - sc->buf) >= wrap_pix)) { - /* We wrapped -- try to back up to the previous word boundary. */ - char *s2 = s; - int n = 0; - while (s2 > sc->buf && *s2 != ' ' && *s2 != '\t') - s2--, n++; - if (s2 > sc->buf) + int L = s - sc->buf; + + if (*s == '\r' || *s == '\n') { - s = s2; + if (*s == '\r' && s[1] == '\n') /* swallow CRLF too */ + *s++ = 0; + *s++ = 0; - L = s - sc->buf; } - } + else + { + /* We wrapped -- try to back up to the previous word boundary. */ + char *s2 = s; + int n = 0; + while (s2 > sc->buf && *s2 != ' ' && *s2 != '\t') + s2--, n++; + if (s2 > sc->buf) + { + s = s2; + *s++ = 0; + L = s - sc->buf; + } + } - sc->lines[sc->total_lines] = (char *) malloc (L+1); - memcpy (sc->lines[sc->total_lines], sc->buf, L); - sc->lines[sc->total_lines][L] = 0; + sc->lines[sc->total_lines] = (char *) malloc (L+1); + memcpy (sc->lines[sc->total_lines], sc->buf, L); + sc->lines[sc->total_lines][L] = 0; - { - char *t = sc->lines[sc->total_lines]; - char *ut = untabify (t); - sc->lines[sc->total_lines] = ut; - free (t); - } + if (!textures_p) + { + /* The GLUT font only has ASCII characters. */ + char *s1 = utf8_to_latin1 (sc->lines[sc->total_lines], True); + free (sc->lines[sc->total_lines]); + sc->lines[sc->total_lines] = s1; + } - sc->total_lines++; + { + char *t = sc->lines[sc->total_lines]; + char *ut = untabify (t); + strip (ut, (alignment == 0), 1); /* if centering, strip + leading whitespace too */ + sc->lines[sc->total_lines] = ut; + free (t); + } + + sc->line_widths[sc->total_lines] = + sw_string_width(sc, sc->lines[sc->total_lines]); + + sc->total_lines++; + + if (sc->buf_tail > (s - sc->buf)) + { + int i = sc->buf_tail - (s - sc->buf); + memmove (sc->buf, s, i); + sc->buf_tail = i; + sc->buf[sc->buf_tail] = 0; + } + else + { + sc->buf_tail = 0; + } - if (sc->buf_tail > (s - sc->buf)) - { - int i = sc->buf_tail - (s - sc->buf); - memcpy (sc->buf, s, i); - sc->buf_tail = i; sc->buf[sc->buf_tail] = 0; + s = sc->buf; + + break; } else { - sc->buf_tail = 0; + s++; } - - sc->buf[sc->buf_tail] = 0; - s = sc->buf; - col = 0; - } - else - { - col++; - if (*s == '\t') - col = TAB_WIDTH * ((col / TAB_WIDTH) + 1); - s++; } } } static void -draw_string (int x, int y, const char *s) +draw_string (sws_configuration *sc, GLfloat x, GLfloat y, const char *s) { + const char *os = s; if (!s || !*s) return; glPushMatrix (); glTranslatef (x, y, 0); - while (*s) - glutStrokeCharacter (GLUT_FONT, *s++); + + if (textures_p) + print_texture_string (sc->texfont, s); + else + while (*s) + glutStrokeCharacter (GLUT_FONT, *s++); glPopMatrix (); + + if (debug_p) + { + GLfloat h = sc->line_height / sc->font_scale; + char **chars = utf8_split (os, 0); + int i, w = 0; + char *s2 = (char *) malloc (strlen(s) + 1); + *s2 = 0; + + if (textures_p) glDisable (GL_TEXTURE_2D); + glLineWidth (1); + + glColor3f (0.2, 0.2, 0.5); + + glBegin (GL_LINES); + + for (i = 0; chars[i]; i++) + { + glVertex3f (x + w, y - sc->descent, 0); /* char left */ + glVertex3f (x + w, y - sc->descent + h, 0); + strcat (s2, chars[i]); + w = sw_string_width (sc, s2); + free (chars[i]); + } + + glVertex3f (x + w, y - sc->descent, 0); /* char right */ + glVertex3f (x + w, y - sc->descent + h, 0); + + glVertex3f (x, y - sc->descent + h, 0); /* ascent */ + glVertex3f (x + w, y - sc->descent + h, 0); + + glVertex3f (x - sc->char_width, y, 0); /* baseline */ + glVertex3f (x + sc->char_width + w, y, 0); + + glVertex3f (x, y - sc->descent, 0); /* descent */ + glVertex3f (x + w, y - sc->descent, 0); + + glEnd(); + + free (s2); + free (chars); + + if (textures_p) glEnable (GL_TEXTURE_2D); + } } -#if 0 static void -grid (double width, double height, double spacing, double z) +grid (double width, double height, double xspacing, double yspacing, double z) { double x, y; - for (y = 0; y <= height/2; y += spacing) + for (y = 0; y <= height/2; y += yspacing) { - glBegin(GL_LINE_LOOP); + glBegin(GL_LINES); glVertex3f(-width/2, y, z); glVertex3f( width/2, y, z); - glEnd(); - glBegin(GL_LINE_LOOP); glVertex3f(-width/2, -y, z); glVertex3f( width/2, -y, z); glEnd(); } - for (x = 0; x <= width/2; x += spacing) + for (x = 0; x <= width/2; x += xspacing) { - glBegin(GL_LINE_LOOP); + glBegin(GL_LINES); glVertex3f( x, -height/2, z); glVertex3f( x, height/2, z); - glEnd(); - glBegin(GL_LINE_LOOP); glVertex3f(-x, -height/2, z); glVertex3f(-x, height/2, z); glEnd(); } - glBegin(GL_LINE_LOOP); + glBegin(GL_LINES); glVertex3f(-width, 0, z); glVertex3f( width, 0, z); - glEnd(); - glBegin(GL_LINE_LOOP); glVertex3f(0, -height, z); glVertex3f(0, height, z); glEnd(); @@ -424,70 +542,169 @@ box (double width, double height, double depth) glVertex3f( width/2, height/2, -depth/2); glEnd(); - glEnd(); - glBegin(GL_LINE_LOOP); + glBegin(GL_LINES); glVertex3f(-width/2, height/2, depth/2); glVertex3f(-width/2, -height/2, -depth/2); - glEnd(); - glBegin(GL_LINE_LOOP); + glVertex3f( width/2, height/2, depth/2); glVertex3f( width/2, -height/2, -depth/2); - glEnd(); - glBegin(GL_LINE_LOOP); + glVertex3f(-width/2, -height/2, depth/2); glVertex3f(-width/2, height/2, -depth/2); - glEnd(); - glBegin(GL_LINE_LOOP); + glVertex3f( width/2, -height/2, depth/2); glVertex3f( width/2, height/2, -depth/2); glEnd(); } -#endif /* 0 */ -/* Window management, etc - */ +/* Construct stars (number of stars is dependent on size of screen) */ static void -reshape (sws_configuration *sc, int width, int height) +init_stars (ModeInfo *mi, int width, int height) { - static Bool stars_done = False; + sws_configuration *sc = &scs[MI_SCREEN(mi)]; + int i, j; + int size = (width > height ? width : height); + int nstars = size * size / 320; + int max_size = 3; + GLfloat inc = 0.5; + int steps = max_size / inc; + + glDeleteLists (sc->star_list, 1); + sc->star_list = glGenLists (1); + glNewList (sc->star_list, GL_COMPILE); + + glEnable(GL_POINT_SMOOTH); - glViewport (0, 0, (GLint) width, (GLint) height); - if (!stars_done) + for (j = 1; j <= steps; j++) { - int i; - int nstars = width * height / 320; - glDeleteLists (sc->star_list, 1); - sc->star_list = glGenLists (1); - glNewList (sc->star_list, GL_COMPILE); + glPointSize(inc * j); glBegin (GL_POINTS); - for (i = 0; i < nstars; i++) + for (i = 0; i < nstars / steps; i++) { - GLfloat c = 0.6 + 0.3 * random() / RAND_MAX; - glColor3f (c, c, c); - glVertex3f (2 * width * (0.5 - 1.0 * random() / RAND_MAX), - 2 * height * (0.5 - 1.0 * random() / RAND_MAX), - 0.0); + glColor3f (0.6 + frand(0.3), + 0.6 + frand(0.3), + 0.6 + frand(0.3)); + glVertex2f (2 * size * (0.5 - frand(1.0)), + 2 * size * (0.5 - frand(1.0))); } glEnd (); - glEndList (); - stars_done = True; } + glEndList (); } -static void -gl_init (ModeInfo *mi) +/* Window management, etc + */ +ENTRYPOINT void +reshape_sws (ModeInfo *mi, int width, int height) { sws_configuration *sc = &scs[MI_SCREEN(mi)]; - program = get_string_resource ("program", "Program"); + /* Set up matrices for perspective text display + */ + { + GLfloat desired_aspect = (GLfloat) 3/4; + int w = mi->xgwa.width; + int h = mi->xgwa.height; + int yoff = 0; + GLfloat rot = current_device_rotation(); - glMatrixMode (GL_MODELVIEW); +#ifdef KEEP_ASPECT + { + int h2 = w * desired_aspect; + yoff = (h - h2) / 2; /* Wide window: letterbox at top and bottom. */ + if (yoff < 0) yoff = 0; /* Tall window: clip off the top. */ + h = h2; + } +#endif + + glViewport (0, yoff, w, h); + + glMatrixMode (GL_PROJECTION); + glLoadIdentity(); + + glMatrixMode (GL_MODELVIEW); + glLoadIdentity (); + gluPerspective (80.0, 1/desired_aspect, 1000, 55000); + gluLookAt (0.0, 0.0, 4600.0, + 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0); + + /* Horrible kludge to prevent the text from materializing already + on screen on iPhone in landscape mode. + */ + if ((rot > 45 && rot < 135) || + (rot < -45 && rot > -135)) + { + GLfloat s = 1.1; + glScalef (s, s, s); + } + + glRotatef (-60.0, 1.0, 0.0, 0.0); + +#if 0 + glRotatef (60.0, 1.0, 0.0, 0.0); + glTranslatef (260, 3200, 0); + glScalef (1.85, 1.85, 1); +#endif + + /* The above gives us an arena where the bottom edge of the screen is + represented by the line (-2100,-3140,0) - ( 2100,-3140,0). */ + + /* Now let's move the origin to the front of the screen. */ + glTranslatef (0.0, -3140, 0.0); + + /* And then let's scale so that the bottom of the screen is 1.0 wide. */ + glScalef (4200, 4200, 4200); + } + + + /* Compute the height in pixels of the line at the bottom of the screen. */ + { + GLdouble mm[17], pm[17]; + GLint vp[5]; + GLdouble x = 0.5, y1 = 0, z = 0; + GLdouble y2 = sc->line_height; + GLdouble wx=-1, wy1=-1, wy2=-1, wz=-1; + + glGetDoublev (GL_MODELVIEW_MATRIX, mm); + glGetDoublev (GL_PROJECTION_MATRIX, pm); + glGetIntegerv (GL_VIEWPORT, vp); + gluProject (x, y1, z, mm, pm, vp, &wx, &wy1, &wz); + gluProject (x, y2, z, mm, pm, vp, &wx, &wy2, &wz); + sc->line_pixel_height = (wy2 - wy1); + glLineWidth (1); + } + + /* Compute the best looking line thickness for the bottom line. + */ + if (!thick_p) + sc->line_thickness = 1.0; + else + sc->line_thickness = (GLfloat) sc->line_pixel_height / FONT_WEIGHT; + + if (sc->line_thickness < 1.2) + sc->line_thickness = 1.0; +} + + +static void +gl_init (ModeInfo *mi) +{ + sws_configuration *sc = &scs[MI_SCREEN(mi)]; glDisable (GL_LIGHTING); glDisable (GL_DEPTH_TEST); + if (smooth_p) + { + glEnable (GL_LINE_SMOOTH); + glHint (GL_LINE_SMOOTH_HINT, GL_NICEST); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable (GL_BLEND); + } + sc->text_list = glGenLists (1); glNewList (sc->text_list, GL_COMPILE); glEndList (); @@ -495,60 +712,119 @@ gl_init (ModeInfo *mi) sc->star_list = glGenLists (1); glNewList (sc->star_list, GL_COMPILE); glEndList (); + + sc->line_thickness = 1.0; } -void +static void free_sws (ModeInfo *mi); + + +ENTRYPOINT void init_sws (ModeInfo *mi) { double font_height; - sws_configuration *sc; + sws_configuration *sc = 0; - if (!scs) { - scs = (sws_configuration *) - calloc (MI_NUM_SCREENS(mi), sizeof (sws_configuration)); - if (!scs) { - fprintf(stderr, "%s: out of memory\n", progname); - exit(1); - } + MI_INIT (mi, scs, free_sws); - sc = &scs[MI_SCREEN(mi)]; - sc->lines = (char **) calloc (max_lines+1, sizeof(char *)); - } + sc = &scs[MI_SCREEN(mi)]; + sc->dpy = MI_DISPLAY(mi); sc = &scs[MI_SCREEN(mi)]; + /* Unchecked malloc. :( */ + sc->lines = (char **) calloc (max_lines+1, sizeof(char *)); + sc->line_widths = (int *) calloc (max_lines+1, sizeof(int)); if ((sc->glx_context = init_GL(mi)) != NULL) { gl_init(mi); - reshape(sc, MI_WIDTH(mi), MI_HEIGHT(mi)); - } - + reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi)); + clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */ + init_stars (mi, MI_WIDTH(mi), MI_HEIGHT(mi)); + } - font_height = GLUT_FONT->top - GLUT_FONT->bottom; - sc->font_scale = 1.0 / glutStrokeWidth (GLUT_FONT, 'z'); /* 'n' seems - too wide */ - if (target_columns > 0) + if (textures_p) { - sc->columns = target_columns; + XCharStruct e; + int cw, ascent, descent; + sc->texfont = load_texture_font (MI_DISPLAY(mi), "font"); + texture_string_metrics (sc->texfont, "n", &e, &ascent, &descent); + cw = e.width; + sc->char_width = cw; + font_height = ascent + descent; + sc->descent = descent; + glEnable(GL_ALPHA_TEST); + glEnable (GL_TEXTURE_2D); + + check_gl_error ("loading font"); + + /* "Anistropic filtering helps for quadrilateral-angled textures. + A sharper image is accomplished by interpolating and filtering + multiple samples from one or more mipmaps to better approximate + very distorted textures. This is the next level of filtering + after trilinear filtering." */ + if (smooth_p && + strstr ((char *) glGetString(GL_EXTENSIONS), + "GL_EXT_texture_filter_anisotropic")) + { + GLfloat anisotropic = 0.0; + glGetFloatv (GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropic); + if (anisotropic >= 1.0) + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, + anisotropic); + } } else { - if (font_size <= 0) - font_size = BASE_FONT_SIZE; - sc->columns = BASE_FONT_COLUMNS * ((double) BASE_FONT_SIZE / font_size); + font_height = GLUT_FONT->top - GLUT_FONT->bottom; + sc->char_width = glutStrokeWidth (GLUT_FONT, 'z'); /* 'n' seems wide */ + sc->descent = 0; } + + sc->font_scale = 1.0 / sc->char_width; + + + /* We consider a font that consumes 80 columns to be "18 points". - sc->font_scale /= sc->columns; + If neither -size nor -columns was specified, default to 60 columns + (which is 24 points.) + + If both were specified, -columns has priority. + */ + { + int base_col = 80; + int base_size = 18; + + if (target_columns <= 0 && font_size <= 0) + target_columns = 60; + + if (target_columns > 0) + font_size = base_size * (base_col / (double) target_columns); + else if (font_size > 0) + target_columns = base_col * (base_size / (double) font_size); + } + + sc->line_pixel_width = target_columns * sc->char_width; + + sc->font_scale /= target_columns; sc->line_height = font_height * sc->font_scale; + /* Buffer only a few lines of text. + If the buffer is too big, there's a significant delay between + when the program launches and when the text appears, which can be + irritating for time-sensitive output (clock, current music, etc.) - if (!wrap_p) sc->columns = 1000; /* wrap anyway, if it's absurdly long. */ + I'd like to buffer only 2 lines, but we need to assume that we + could get a whole line of N-byte Unicrud, and if we fill the buffer + before hitting the end of the line, we stall. + */ + sc->buf_size = target_columns * 2 * 4; + if (sc->buf_size < 80) sc->buf_size = 80; + sc->buf = (char *) calloc (1, sc->buf_size); - sc->subproc_relaunch_delay = 2 * 1000; sc->total_lines = max_lines-1; - launch_text_generator (sc); if (random() & 1) star_spin = -star_spin; @@ -568,13 +844,48 @@ init_sws (ModeInfo *mi) progname, alignment_str); exit (1); } + + sc->tc = textclient_open (sc->dpy); + + /* one more reshape, after line_height has been computed */ + reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi)); } -void +static void +draw_stars (ModeInfo *mi) +{ + sws_configuration *sc = &scs[MI_SCREEN(mi)]; + + glMatrixMode (GL_PROJECTION); + glPushMatrix (); + { + glLoadIdentity (); + + glMatrixMode (GL_MODELVIEW); + glPushMatrix (); + { + glLoadIdentity (); + glOrtho (-0.5 * MI_WIDTH(mi), 0.5 * MI_WIDTH(mi), + -0.5 * MI_HEIGHT(mi), 0.5 * MI_HEIGHT(mi), + -100.0, 100.0); + glRotatef (sc->star_theta, 0.0, 0.0, 1.0); + if (textures_p) glDisable (GL_TEXTURE_2D); + + glCallList (sc->star_list); + if (textures_p) glEnable (GL_TEXTURE_2D); + } + glPopMatrix (); + } + glMatrixMode (GL_PROJECTION); + glPopMatrix (); +} + +ENTRYPOINT void draw_sws (ModeInfo *mi) { sws_configuration *sc = &scs[MI_SCREEN(mi)]; +/* XtAppContext app = XtDisplayToApplicationContext (sc->dpy);*/ Display *dpy = MI_DISPLAY(mi); Window window = MI_WINDOW(mi); int i; @@ -582,61 +893,129 @@ draw_sws (ModeInfo *mi) if (!sc->glx_context) return; - if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput)) - XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput); - glDrawBuffer (GL_BACK); glXMakeCurrent (dpy, window, *(sc->glx_context)); glClear (GL_COLOR_BUFFER_BIT); - glPushMatrix (); + draw_stars (mi); - glMatrixMode (GL_PROJECTION); - glLoadIdentity (); - glOrtho (-0.5 * MI_WIDTH(mi), 0.5 * MI_WIDTH(mi), - -0.5 * MI_HEIGHT(mi), 0.5 * MI_HEIGHT(mi), - -100.0, 100.0); - glRotatef (sc->star_theta, 0.0, 0.0, 1.0); - glCallList (sc->star_list); - - glLoadIdentity (); - gluPerspective (80.0, 4.0/3.0, 10, 500000); glMatrixMode (GL_MODELVIEW); - gluLookAt (0.0, 0.0, 4600.0, - 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0); - - glRotatef (-60.0, 1.0, 0.0, 0.0); + glPushMatrix (); - /* The above gives us an arena where the bottom edge of the screen is - represented by the line (-2100,-3140,0) - ( 2100,-3140,0). */ +# ifdef HAVE_MOBILE + /* Need to do this every time to get device rotation right */ + reshape_sws (mi, MI_WIDTH(mi), MI_HEIGHT(mi)); +# endif - /* Now let's move the origin to the front of the screen. */ - glTranslatef (0.0, -3140, 0.0); + if (debug_p) + { + int i; + glPushMatrix (); + if (textures_p) glDisable (GL_TEXTURE_2D); + glLineWidth (1); + glTranslatef (0,-1, 0); + + glColor3f(1, 0, 0); /* Red line is where text appears */ + glPushMatrix(); + glTranslatef(0, -0.028, 0); + glLineWidth (4); + glBegin(GL_LINES); + glVertex3f(-0.5, 1, 0); + glVertex3f( 0.5, 1, 0); + glVertex3f(-0.5, -1, 0); + glVertex3f( 0.5, -1, 0); + glEnd(); + glLineWidth (1); + glPopMatrix(); - /* And then let's scale so that the bottom of the screen is 1.0 wide. */ - glScalef (4200, 4200, 4200); + glColor3f (0.2, 0.2, 0.2); + for (i = 0; i < 16; i++) + { + box (1, 1, 1); + grid (1, 1, sc->char_width * sc->font_scale, sc->line_height, 0); + glTranslatef(0, 1, 0); + } + if (textures_p) glEnable (GL_TEXTURE_2D); + glPopMatrix (); + check_gl_error ("debug render"); + } /* Scroll to current position */ glTranslatef (0.0, sc->intra_line_scroll, 0.0); glColor3f (1.0, 1.0, 0.4); - glCallList (sc->text_list); - sc->intra_line_scroll += sc->line_height / scroll_steps; + mi->polygon_count = 0; - if (sc->intra_line_scroll >= sc->line_height) + glPushMatrix (); + glScalef (sc->font_scale, sc->font_scale, sc->font_scale); + for (i = 0; i < sc->total_lines; i++) { - static time_t reshape_time = 0; - time_t now = time((time_t) 0); - if (reshape_time != now) /* only poll for reshape once a second */ + double fade = (fade_p ? 1.0 * i / sc->total_lines : 1.0); + int offscreen_lines = 2; + + double x = -0.5; + double y = ((sc->total_lines - (i + offscreen_lines) - 1) + * sc->line_height); + double xoff = 0; + char *line = sc->lines[i]; + + if (debug_p) { - reshape_time = now; - XGetWindowAttributes (dpy, window, &mi->xgwa); - reshape(sc, MI_WIDTH(mi), MI_HEIGHT(mi)); + double xx = x * 1.4; /* a little more to the left */ + char n[20]; + sprintf(n, "%d:", i); + glColor3f (1.0, 1.0, 1.0); + draw_string (sc, xx / sc->font_scale, y / sc->font_scale, n); } + if (!line || !*line) + continue; + + if (sc->line_thickness != 1 && !textures_p) + { + int max_thick_lines = MAX_THICK_LINES; + GLfloat thinnest_line = 1.0; + GLfloat thickest_line = sc->line_thickness; + GLfloat range = thickest_line - thinnest_line; + GLfloat thickness; + + int j = sc->total_lines - i - 1; + + if (j > max_thick_lines) + thickness = thinnest_line; + else + thickness = (thinnest_line + + (range * ((max_thick_lines - j) / + (GLfloat) max_thick_lines))); + + glLineWidth (thickness); + } + + if (alignment >= 0) + { + int n = sc->line_widths[i]; + xoff = 1.0 - (n * sc->font_scale); + } + + if (alignment == 0) + xoff /= 2; + + glColor3f (fade, fade, 0.5 * fade); + draw_string (sc, (x + xoff) / sc->font_scale, y / sc->font_scale, + line); + if (textures_p) + mi->polygon_count += strlen (line); + } + glPopMatrix (); + + + + sc->intra_line_scroll += sc->line_height / scroll_steps; + + if (sc->intra_line_scroll >= sc->line_height) + { sc->intra_line_scroll = 0; /* Drop the oldest line off the end. */ @@ -646,8 +1025,10 @@ draw_sws (ModeInfo *mi) /* Scroll the contents of the lines array toward 0. */ if (sc->total_lines > 0) { - for (i = 1; i < sc->total_lines; i++) + for (i = 1; i < sc->total_lines; i++) { sc->lines[i-1] = sc->lines[i]; + sc->line_widths[i-1] = sc->line_widths[i]; + } sc->lines[--sc->total_lines] = 0; } @@ -659,46 +1040,34 @@ draw_sws (ModeInfo *mi) here so that new text still pulls in from the bottom of the screen, isntead of just appearing. */ sc->total_lines = max_lines; - - glDeleteLists (sc->text_list, 1); - sc->text_list = glGenLists (1); - glNewList (sc->text_list, GL_COMPILE); - glPushMatrix (); - glScalef (sc->font_scale, sc->font_scale, sc->font_scale); - for (i = 0; i < sc->total_lines; i++) - { - int offscreen_lines = 3; - - double x = -0.5; - double y = ((sc->total_lines - (i + offscreen_lines) - 1) - * sc->line_height); - double xoff = 0; - char *line = sc->lines[i]; -#if 0 - char n[20]; - sprintf(n, "%d:", i); - draw_string (x / sc->font_scale, y / sc->font_scale, n); -#endif - if (!line || !*line) - continue; - - if (alignment >= 0) - xoff = 1.0 - (glutStrokeLength(GLUT_FONT, line) * sc->font_scale); - if (alignment == 0) - xoff /= 2; - - draw_string ((x + xoff) / sc->font_scale, y / sc->font_scale, line); - } - glPopMatrix (); - glEndList (); } glPopMatrix (); + if (mi->fps_p) do_fps (mi); glFinish(); glXSwapBuffers(dpy, window); sc->star_theta += star_spin; } +static void +free_sws (ModeInfo *mi) +{ + sws_configuration *sc = &scs[MI_SCREEN(mi)]; + if (sc->tc) + textclient_close (sc->tc); + + /* #### there's more to free here */ +} + + +#ifdef __GNUC__ + __extension__ /* don't warn about "string length is greater than the length + ISO C89 compilers are required to support" when including + "starwars.txt" in the defaults... */ +#endif + +XSCREENSAVER_MODULE_2 ("StarWars", starwars, sws) + #endif /* USE_GL */