summaryrefslogtreecommitdiffstats
path: root/hacks/glx/esper.c
diff options
context:
space:
mode:
Diffstat (limited to 'hacks/glx/esper.c')
-rw-r--r--hacks/glx/esper.c2481
1 files changed, 0 insertions, 2481 deletions
diff --git a/hacks/glx/esper.c b/hacks/glx/esper.c
deleted file mode 100644
index bbc0aa6..0000000
--- a/hacks/glx/esper.c
+++ /dev/null
@@ -1,2481 +0,0 @@
-/* esper, Copyright © 2017-2021 Jamie Zawinski <jwz@jwz.org>
- * Enhance 224 to 176. Pull out track right. Center in pull back.
- * Pull back. Wait a minute. Go right. Stop. Enhance 57 19. Track 45 left.
- * Gimme a hardcopy right there.
- *
- * Permission to use, copy, modify, distribute, and sell this software and its
- * documentation for any purpose is hereby granted without fee, provided that
- * the above copyright notice appear in all copies and that both that
- * copyright notice and this permission notice appear in supporting
- * documentation. No representations are made about the suitability of this
- * software for any purpose. It is provided "as is" without express or
- * implied warranty.
- */
-
-/*
- The Esper machine has a 4:3 display, about 12" diagonal.
- The display is overlayed with a 10x7 grid of blue lines.
- The scene goes approximately like this:
-
- "Enhance 224 To 176."
-
- ZM 0000 NS 0000 EW 0000
-
- The reticle is displayed centered.
- It moves in 8 steps with 3 frame blur to move to around to grid 1,4.
-
- ZM 0000 NS 0000 EW 0000
- ZM 0000 NS 0001 EW 0001
- ZM 0000 NS 0001 EW 0002
- ZM 0000 NS 0002 EW 0003
- ZM 0000 NS 0003 EW 0005
- ZM 0000 NS 0004 EW 0008
- ZM 0000 NS 0015 EW 0011
-
- These numbers appear to have little relation to what we are
- actually seeing on the screen. Also the same numbers are
- repeated later when looking at totally different parts of
- the photograph.
-
- ZM 0000 NS 0117 EW 0334
-
- The box appears: 8 steps, final box is 1.5x2.25 at -0.5,4.0.
-
- ZM 4086 NS 0117 EW 0334
-
- The box blinks yellow 5x.
- The image's zoom-and-pan takes 8 steps, with no text on the screen.
- The zoom is in discreet steps, with flashes.
- The grid stays the same size the whole time.
- The flashes look like solarization to blue.
- When the zoom is finished, there is still no text.
-
- "Enhance." Goes 4 more ticks down the same hole?
- "Stop." Moves up a little bit at the end.
-
- Then with no instructions, it goes 20 ticks by itself, off camera.
-
- "Move in." 10 ticks.
- "Stop." (We are looking at a fist in the picture.)
- "Pull out track right."
- "Stop." (We are looking at a newspaper.)
- "Center and pull back."
- "Stop." (We just passed the round mirror.)
- "Track 45 right."
- "Stop."
- "Center and stop."
-
- This time there was no grid until it stopped, then the grid showed up.
- There is video tearing at the bottom.
-
- "Enhance 34 to 36."
-
- ZM 0000 NS 0063 EW 0185
- ZM 0000 NS 0197 EW 0334
- ZM 3841 NS 0197 EW 0334
-
- It kind of zooms in to the center wobbly and willy-nilly.
- We are now looking at a glass.
-
- "Pan right and pull back." (There is no grid while moving again.)
- "Stop."
-
- Ok, at this point, we enter fantasy-land. From here on, the images
- shown are very high resolution with no noise. And suddenly the
- UI on the Esper is *way* higher resolution. My theory is that from
- this point on in the scene, we are not looking at the literal Esper
- machine, but instead the movie is presenting Decard's perception of
- it. We're seeing the room, not the photo of the room. The map has
- become the territory.
-
- "Enhance 34 to 46."
-
- ZM 0000 NS 0197 EW 0334
-
- This has the reticle and box only, no grid, ends with no grid.
-
- "Pull back."
- "Wait a minute. Go right."
- "Stop."
- Now it's going around the corner or something.
-
- "Enhance 57 19."
- This has a reticle then box, but the image started zooming early.
-
- "Track 45 left."
- zooms out and moves left
-
- "Stop." (O hai Zhora.)
- "Enhance 15 to 23."
-
- ZM 3852 NS 0197 EW 0334
-
- "Gimme a hardcopy right there."
-
- The printer polaroid is WAY lower resolution than the image we see on
- the "screen" -- in keeping with my theory that we were not seeing the
- screen.
-
-
- TODO:
-
- * There's a glitch at the top/bottom of the texfont textures.
- * "Pull back" isn't quite symmetric: zoom origin is slightly off.
- * Maybe display text like "Pull right" and "Stop".
-*/
-
-
-/* Use a small point size to keep it nice and grainy. */
-#define TITLE_FONT \
- "OCR A Std 10, Lucida Console 10, Monaco 10, Courier 10, monospace 10"
-
-#define DEFAULTS "*delay: 20000 \n" \
- "*wireframe: False \n" \
- "*showFPS: False \n" \
- "*fpsTop: True \n" \
- "*useSHM: True \n" \
- "*titleFont: " TITLE_FONT "\n" \
- "*desktopGrabber: xscreensaver-getimage -no-desktop %s\n" \
- "*grabDesktopImages: False \n" \
- "*chooseRandomImages: True \n" \
- "*gridColor: #4444FF\n" \
- "*reticleColor: #FFFF77\n" \
- "*textColor: #FFFFBB\n" \
-
-# define refresh_esper 0
-# define release_esper 0
-# include "xlockmore.h"
-
-#undef RANDSIGN
-#define RANDSIGN() ((random() & 1) ? 1 : -1)
-#undef BELLRAND
-#define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
-
-#ifdef USE_GL
-
-#undef SMOOTH
-#define USE_ASYNC_LOADER
-
-# define DEF_GRID_SIZE "11"
-# define DEF_GRID_THICKNESS "15"
-# define DEF_TITLES "True"
-# define DEF_SPEED "1.0"
-# define DEF_DEBUG "False"
-
-#include "grab-ximage.h"
-#include "texfont.h"
-
-#ifdef HAVE_XSHM_EXTENSION
-# include "xshm.h" /* to get <sys/shm.h> */
-#endif
-
-
-typedef struct {
- double x, y, w, h;
-} rect;
-
-typedef struct {
- ModeInfo *mi;
- unsigned long id; /* unique */
- char *title; /* the filename of this image */
- int w, h; /* size in pixels of the image */
- int tw, th; /* size in pixels of the texture */
- XRectangle geom; /* where in the image the bits are */
- Bool loaded_p; /* whether the image has finished loading */
-# ifdef USE_ASYNC_LOADER
- texture_loader_t *loader; /* asynchronous image loader */
-# endif
- Bool used_p; /* whether the image has yet appeared
- on screen */
- GLuint texid; /* which texture contains the image */
- int refcount; /* how many sprites refer to this image */
-} image;
-
-
-typedef enum {
- BLANK,
- GRID_ON,
- IMAGE_LOAD,
- IMAGE_UNLOAD,
- IMAGE_FORCE_UNLOAD,
- REPOSITION,
- RETICLE_ON,
- RETICLE_MOVE,
- BOX_MOVE,
- IMAGE_ZOOM,
- MANUAL_RETICLE_ON,
- MANUAL_RETICLE,
- MANUAL_BOX_ON,
- MANUAL_BOX
-} anim_state;
-
-typedef enum { NEW, IN, FULL, OUT, DEAD } sprite_state;
-typedef enum { IMAGE, RETICLE, BOX, GRID, FLASH, TEXT } sprite_type;
-
-typedef struct {
- unsigned long id; /* unique */
- sprite_type type;
- image *img; /* type = IMAGE */
- unsigned long text_id; /* type = TEXT */
- char *text;
- GLfloat opacity;
- GLfloat thickness_scale; /* line and image types */
- Bool throb_p;
- double start_time; /* when this animation began */
- double duration; /* lifetime of sprite in seconds; 0 = inf */
- double fade_duration; /* speed of fade in and fade out */
- double pause_duration; /* delay before fade-in starts */
- Bool remain_p; /* pause forever before fade-out */
- rect from, to, current; /* the journey this image is taking */
- sprite_state state; /* the state we're in right now */
- double state_time; /* time of last state change */
- int frame_count; /* frames since last state change */
- Bool fatbits_p; /* For image texture rendering */
- Bool back_p; /* If BOX, zooming out, not in */
-} sprite;
-
-
-typedef struct {
- GLXContext *glx_context;
- int nimages; /* how many images are loaded or loading now */
- image *images[10]; /* pointers to the images */
-
- int nsprites; /* how many sprites are animating right now */
- sprite *sprites[100]; /* pointers to the live sprites */
-
- double now; /* current time in seconds */
- double dawn_of_time; /* when the program launched */
- double image_load_time; /* time when we last loaded a new image */
-
- texture_font_data *font_data;
-
- int sprite_id, image_id; /* debugging id counters */
-
- GLfloat grid_color[4], reticle_color[4], text_color[4];
-
- anim_state anim_state; /* Counters for global animation state, */
- double anim_start, anim_duration;
-
- Bool button_down_p;
-
-} esper_state;
-
-static esper_state *sss = NULL;
-
-
-/* Command-line arguments
- */
-static int grid_size;
-static int grid_thickness;
-
-static Bool do_titles; /* Display image titles. */
-static GLfloat speed;
-static Bool debug_p; /* Be loud and do weird things. */
-
-
-static XrmOptionDescRec opts[] = {
- { "-speed", ".speed", XrmoptionSepArg, 0 },
- { "-titles", ".titles", XrmoptionNoArg, "True" },
- { "-no-titles", ".titles", XrmoptionNoArg, "False" },
- { "-debug", ".debug", XrmoptionNoArg, "True" },
-};
-
-static argtype vars[] = {
- { &grid_size, "gridSize", "GridSize", DEF_GRID_SIZE, t_Int},
- { &grid_thickness,"gridThickness","GridThickness",DEF_GRID_THICKNESS, t_Int},
- { &do_titles, "titles", "Titles", DEF_TITLES, t_Bool},
- { &speed, "speed", "Speed", DEF_SPEED, t_Float},
- { &debug_p, "debug", "Debug", DEF_DEBUG, t_Bool},
-};
-
-ENTRYPOINT ModeSpecOpt esper_opts = {countof(opts), opts, countof(vars), vars, NULL};
-
-
-/* Returns the current time in seconds as a double.
- */
-static double
-double_time (void)
-{
- struct timeval now;
-# ifdef GETTIMEOFDAY_TWO_ARGS
- struct timezone tzp;
- gettimeofday(&now, &tzp);
-# else
- gettimeofday(&now);
-# endif
-
- return (now.tv_sec + ((double) now.tv_usec * 0.000001));
-}
-
-static const char *
-state_name (anim_state s)
-{
- switch (s) {
- case BLANK: return "BLANK";
- case GRID_ON: return "GRID_ON";
- case IMAGE_LOAD: return "IMAGE_LOAD";
- case IMAGE_UNLOAD: return "IMAGE_UNLOAD";
- case IMAGE_FORCE_UNLOAD: return "IMAGE_FORCE_UNLOAD";
- case REPOSITION: return "REPOSITION";
- case RETICLE_ON: return "RETICLE_ON";
- case RETICLE_MOVE: return "RETICLE_MOVE";
- case BOX_MOVE: return "BOX_MOVE";
- case IMAGE_ZOOM: return "IMAGE_ZOOM";
- case MANUAL_BOX_ON: return "MANUAL_BOX_ON";
- case MANUAL_BOX: return "MANUAL_BOX";
- case MANUAL_RETICLE_ON: return "MANUAL_RETICLE_ON";
- case MANUAL_RETICLE: return "MANUAL_RETICLE";
- default: return "UNKNOWN";
- }
-}
-
-
-static void image_loaded_cb (const char *filename, XRectangle *geom,
- int image_width, int image_height,
- int texture_width, int texture_height,
- void *closure);
-
-
-/* Allocate an image structure and start a file loading in the background.
- */
-static image *
-alloc_image (ModeInfo *mi)
-{
- esper_state *ss = &sss[MI_SCREEN(mi)];
- int wire = MI_IS_WIREFRAME(mi);
- image *img = (image *) calloc (1, sizeof (*img));
-
- img->id = ++ss->image_id;
- img->loaded_p = False;
- img->used_p = False;
- img->mi = mi;
-
- glGenTextures (1, &img->texid);
- if (img->texid <= 0) abort();
-
- ss->image_load_time = ss->now;
-
- if (wire)
- image_loaded_cb (0, 0, 0, 0, 0, 0, img);
- else
- {
- /* If possible, load images at much higher resolution than the window,
- to facilitate deep zooms.
- */
- int max_max = 4096; /* ~12 megapixels */
- int max = 0;
-
-# if defined(HAVE_XSHM_EXTENSION) && \
- !defined(HAVE_MOBILE) && \
- !defined(HAVE_COCOA)
-
- /* Try not to ask for an image larger than the SHM segment size.
- If XSHM fails in a real-X11 world, it can take a staggeringly long
- time to transfer the image bits from the server over Xproto -- like,
- *18 seconds* for 4096 px and 8 seconds for 3072 px on MacOS XQuartz.
- What madness is this?
- */
- unsigned long shmmax = 0;
-
-# if defined(SHMMAX)
- /* Linux 2.6 defines this to be 0x2000000, but on CentOS 6.9,
- "sysctl kernel.shmmax" reports a luxurious 0x1000000000. */
- shmmax = SHMMAX;
-# elif defined(__APPLE__)
- /* MacOS 10.13 "sysctl kern.sysv.shmmax" is paltry: */
- shmmax = 0x400000;
-# elif defined(__linux__)
- {
- /* Raspbian 10.6 = 0xFEFFFFFF, CentOS 7.7 = 0xFFFFFFFFFEFFFFFF */
- FILE *fp = fopen ("/proc/sys/kernel/shmmax", "r");
- if (fp)
- {
- int result = fscanf (fp, "%lu", &shmmax);
- if (!result || result == EOF)
- shmmax = 0; /* Just go with max_max. */
- fclose (fp);
- }
- }
-# endif /* !SHMMAX */
-
- if (shmmax)
- {
- /* Roughly, bytes => NxN. b = (n/8)*4n = n*n*4, so n^2 = 2b, so: */
- unsigned long n = sqrt(shmmax)/2;
- if (n < max_max)
- max_max = n;
- }
-# endif /* HAVE_XSHM_EXTENSION and real X11 */
-
- glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max);
- if (max > max_max) max = max_max;
-
- /* Never ask for an image smaller than the window, even if that
- will make XSHM fall back to Xproto. */
- if (max < MI_WIDTH(mi) || max < MI_HEIGHT(mi))
- max = 0;
-
-# ifdef USE_ASYNC_LOADER
- img->loader = alloc_texture_loader (mi->xgwa.screen, mi->window,
- *ss->glx_context, 0, 0, False,
- img->texid);
-# else
- load_texture_async (mi->xgwa.screen, mi->window, *ss->glx_context,
- max, max, False, img->texid, image_loaded_cb, img);
-# endif
- }
-
- ss->images[ss->nimages++] = img;
- if (ss->nimages >= countof(ss->images)) abort();
-
- return img;
-}
-
-
-/* Callback that tells us that the texture has been loaded.
- */
-static void
-image_loaded_cb (const char *filename, XRectangle *geom,
- int image_width, int image_height,
- int texture_width, int texture_height,
- void *closure)
-{
- image *img = (image *) closure;
- ModeInfo *mi = img->mi;
- int ow, oh;
- /* esper_state *ss = &sss[MI_SCREEN(mi)]; */
-
- int wire = MI_IS_WIREFRAME(mi);
-
- if (wire)
- {
- img->w = MI_WIDTH (mi) * (0.5 + frand (1.0));
- img->h = MI_HEIGHT (mi);
- img->geom.width = img->w;
- img->geom.height = img->h;
- goto DONE;
- }
-
-# ifdef USE_ASYNC_LOADER
- if (img->loader)
- {
- texture_loader_t *loader = img->loader;
- img->loader = 0;
- free_texture_loader (loader);
- }
-# endif
-
- if (image_width == 0 || image_height == 0)
- exit (1);
-
- img->w = image_width;
- img->h = image_height;
- img->tw = texture_width;
- img->th = texture_height;
- img->geom = *geom;
- img->title = (filename ? strdup (filename) : 0);
-
- ow = img->geom.width;
- oh = img->geom.height;
-
- /* If the image's width doesn't come back as the width of the screen,
- then the image must have been scaled down (due to insufficient
- texture memory.) Scale up the coordinates to stretch the image
- to fill the window.
- */
- if (img->w != MI_WIDTH(mi))
- {
- double scale = (double) MI_WIDTH(mi) / img->w;
- img->w *= scale;
- img->h *= scale;
- img->tw *= scale;
- img->th *= scale;
- img->geom.x *= scale;
- img->geom.y *= scale;
- img->geom.width *= scale;
- img->geom.height *= scale;
- }
-
- /* xscreensaver-getimage returns paths relative to the image directory
- now, so leave the sub-directory part in. Unless it's an absolute path.
- */
- if (img->title && img->title[0] == '/')
- {
- /* strip filename to part between last "/" and last ".". */
- char *s = strrchr (img->title, '/');
- if (s) strcpy (img->title, s+1);
- s = strrchr (img->title, '.');
- if (s) *s = 0;
- }
-
-# if !(__APPLE__ && TARGET_IPHONE_SIMULATOR || !defined(__OPTIMIZE__))
- if (debug_p)
-# endif
- fprintf (stderr, "%s: loaded %lu \"%s\" %dx%d\n",
- progname, img->id, (img->title ? img->title : "(null)"),
- ow, oh);
- DONE:
-
- img->loaded_p = True;
-}
-
-
-
-/* Free the image and texture, after nobody is referencing it.
- */
-static void
-destroy_image (ModeInfo *mi, image *img)
-{
- esper_state *ss = &sss[MI_SCREEN(mi)];
- Bool freed_p = False;
- int i;
-
- if (!img) abort();
- if (!img->loaded_p) abort();
- if (!img->used_p) abort();
- if (img->texid <= 0) abort();
- if (img->refcount != 0) abort();
-
- for (i = 0; i < ss->nimages; i++) /* unlink it from the list */
- if (ss->images[i] == img)
- {
- int j;
- for (j = i; j < ss->nimages-1; j++) /* pull remainder forward */
- ss->images[j] = ss->images[j+1];
- ss->images[j] = 0;
- ss->nimages--;
- freed_p = True;
- break;
- }
-
- if (!freed_p) abort();
-
- if (debug_p)
- fprintf (stderr, "%s: unloaded img %2lu: \"%s\"\n",
- progname, img->id, (img->title ? img->title : "(null)"));
-
- if (img->title) free (img->title);
- glDeleteTextures (1, &img->texid);
- free (img);
-}
-
-
-/* Return an image to use for a sprite.
- If it's time for a new one, get a new one.
- Otherwise, use an old one.
- Might return 0 if the machine is really slow.
- */
-static image *
-get_image (ModeInfo *mi)
-{
- esper_state *ss = &sss[MI_SCREEN(mi)];
- image *img = 0;
- image *loading_img = 0;
- int i;
-
- for (i = 0; i < ss->nimages; i++)
- {
- image *img2 = ss->images[i];
- if (!img2) abort();
- if (!img2->loaded_p)
- loading_img = img2;
- else
- img = img2;
- }
-
- /* Make sure that there is always one unused image in the pipe.
- */
- if (!img && !loading_img)
- alloc_image (mi);
-
- return img;
-}
-
-
-/* Allocate a new sprite and start its animation going.
- */
-static sprite *
-new_sprite (ModeInfo *mi, sprite_type type)
-{
- esper_state *ss = &sss[MI_SCREEN(mi)];
- image *img = (type == IMAGE ? get_image (mi) : 0);
- sprite *sp;
-
- if (type == IMAGE && !img)
- {
- /* Oops, no images yet! The machine is probably hurting bad.
- Let's give it some time before thrashing again. */
- usleep (250000);
- return 0;
- }
-
- sp = (sprite *) calloc (1, sizeof (*sp));
- sp->id = ++ss->sprite_id;
- sp->type = type;
- sp->start_time = ss->now;
- sp->state_time = sp->start_time;
- sp->thickness_scale = 1;
- sp->throb_p = True;
- sp->to.x = 0.5;
- sp->to.y = 0.5;
- sp->to.w = 1.0;
- sp->to.h = 1.0;
-
- if (img)
- {
- sp->img = img;
- sp->img->refcount++;
- sp->img->used_p = True;
- sp->duration = 0; /* forever, until further notice */
- sp->fade_duration = 0.5;
-
- /* Scale the sprite so that the image bits fill the window. */
- {
- double w = MI_WIDTH(mi);
- double h = MI_HEIGHT(mi);
- double r;
- r = ((img->geom.height / (double) img->geom.width) * (w / h));
- if (r > 1)
- sp->to.h *= r;
- else
- sp->to.w /= r;
- }
-
- /* Pan to a random spot */
- if (sp->to.h > 1)
- sp->to.y += frand ((sp->to.h - 1) / 2) * RANDSIGN();
- if (sp->to.w > 1)
- sp->to.x += frand ((sp->to.w - 1) / 2) * RANDSIGN();
- }
-
- sp->from = sp->current = sp->to;
-
- ss->sprites[ss->nsprites++] = sp;
- if (ss->nsprites >= countof(ss->sprites)) abort();
-
- return sp;
-}
-
-
-static sprite *
-copy_sprite (ModeInfo *mi, sprite *old)
-{
- sprite *sp = new_sprite (mi, (sprite_type) ~0L);
- int id;
- double tt;
- if (!sp) abort();
- tt = sp->start_time;
- id = sp->id;
- memcpy (sp, old, sizeof(*sp));
- sp->id = id;
- sp->state = NEW;
- sp->state_time = sp->start_time = tt;
- if (sp->img)
- sp->img->refcount++;
- return sp;
-}
-
-
-/* Free the given sprite, and decrement the reference count on its image.
- */
-static void
-destroy_sprite (ModeInfo *mi, sprite *sp)
-{
- esper_state *ss = &sss[MI_SCREEN(mi)];
- Bool freed_p = False;
- image *img;
- int i;
-
- if (!sp) abort();
- if (sp->state != DEAD) abort();
- img = sp->img;
-
- if (sp->type != IMAGE)
- {
- if (img) abort();
- }
- else
- {
- if (!img) abort();
- if (!img->loaded_p) abort();
- if (!img->used_p) abort();
- if (img->refcount <= 0) abort();
- }
-
- for (i = 0; i < ss->nsprites; i++) /* unlink it from the list */
- if (ss->sprites[i] == sp)
- {
- int j;
- for (j = i; j < ss->nsprites-1; j++) /* pull remainder forward */
- ss->sprites[j] = ss->sprites[j+1];
- ss->sprites[j] = 0;
- ss->nsprites--;
- freed_p = True;
- break;
- }
-
- if (!freed_p) abort();
- if (sp->text) free (sp->text);
- free (sp);
- sp = 0;
-
- if (img)
- {
- img->refcount--;
- if (img->refcount < 0) abort();
- if (img->refcount == 0)
- destroy_image (mi, img);
- }
-}
-
-
-/* Updates the sprite for the current frame of the animation based on
- its creation time compared to the current wall clock.
- */
-static void
-tick_sprite (ModeInfo *mi, sprite *sp)
-{
- esper_state *ss = &sss[MI_SCREEN(mi)];
- image *img = sp->img;
- double now = ss->now;
- double secs;
- double ratio;
- GLfloat visible = sp->duration + sp->fade_duration * 2;
- GLfloat total = sp->pause_duration + visible;
-
- if (sp->type != IMAGE)
- {
- if (sp->img) abort();
- }
- else
- {
- if (! sp->img) abort();
- if (! img->loaded_p) abort();
- }
-
- /* pause fade duration fade
- |------------|------------|---------|-----------|
- ....----====##########====----....
- from current to
- */
-
- secs = now - sp->start_time;
- ratio = (visible <= 0 ? 1 : ((secs - sp->pause_duration) / visible));
- if (ratio < 0) ratio = 0;
- else if (ratio > 1) ratio = 1;
-
- sp->current.x = sp->from.x + ratio * (sp->to.x - sp->from.x);
- sp->current.y = sp->from.y + ratio * (sp->to.y - sp->from.y);
- sp->current.w = sp->from.w + ratio * (sp->to.w - sp->from.w);
- sp->current.h = sp->from.h + ratio * (sp->to.h - sp->from.h);
-
- sp->thickness_scale = 1;
-
- if (secs < sp->pause_duration)
- {
- sp->state = IN;
- sp->opacity = 0;
- }
- else if (secs < sp->pause_duration + sp->fade_duration)
- {
- sp->state = IN;
- sp->opacity = (secs - sp->pause_duration) / (GLfloat) sp->fade_duration;
- }
- else if (sp->duration == 0 || /* 0 means infinite lifetime */
- sp->remain_p ||
- secs < sp->pause_duration + sp->fade_duration + sp->duration)
- {
- sp->state = FULL;
- sp->opacity = 1;
-
- /* Just after reaching full opacity, pulse the width up and down. */
- if (sp->fade_duration > 0 &&
- secs < sp->pause_duration + sp->fade_duration * 2)
- {
- GLfloat f = ((secs - (sp->pause_duration + sp->fade_duration)) /
- sp->fade_duration);
- if (sp->throb_p)
- sp->thickness_scale = 1 + 3 * (f > 0.5 ? 1-f : f);
- }
- }
- else if (secs < total)
- {
- sp->state = OUT;
- sp->opacity = (total - secs) / sp->fade_duration;
- }
- else
- {
- sp->state = DEAD;
- sp->opacity = 0;
- }
-
- sp->frame_count++;
-}
-
-
-/* Draw the given sprite at the phase of its animation dictated by
- its creation time compared to the current wall clock.
- */
-static void
-draw_image_sprite (ModeInfo *mi, sprite *sp)
-{
- /* esper_state *ss = &sss[MI_SCREEN(mi)]; */
- int wire = MI_IS_WIREFRAME(mi);
- image *img = sp->img;
-
- if (! sp->img) abort();
- if (! img->loaded_p) abort();
-
- glPushMatrix();
- {
- GLfloat s = 1 + (sp->thickness_scale - 1) / 40.0;
- glTranslatef (0.5, 0.5, 0);
- glScalef (s, s, 1);
- glTranslatef (-0.5, -0.5, 0);
-
- glTranslatef (sp->current.x, sp->current.y, 0);
- glScalef (sp->current.w, sp->current.h, 1);
-
- glTranslatef (-0.5, -0.5, 0);
-
- if (wire) /* Draw a grid inside the box */
- {
- GLfloat dy = 0.1;
- GLfloat dx = dy * img->w / img->h;
- GLfloat x, y;
-
- if (sp->id & 1)
- glColor4f (sp->opacity, 0, 0, 1);
- else
- glColor4f (0, 0, sp->opacity, 1);
-
- glBegin(GL_LINES);
- glVertex3f (0, 0, 0); glVertex3f (1, 1, 0);
- glVertex3f (1, 0, 0); glVertex3f (0, 1, 0);
-
- for (y = 0; y < 1+dy; y += dy)
- {
- GLfloat yy = (y > 1 ? 1 : y);
- for (x = 0.5; x < 1+dx; x += dx)
- {
- GLfloat xx = (x > 1 ? 1 : x);
- glVertex3f (0, xx, 0); glVertex3f (1, xx, 0);
- glVertex3f (yy, 0, 0); glVertex3f (yy, 1, 0);
- }
- for (x = 0.5; x > -dx; x -= dx)
- {
- GLfloat xx = (x < 0 ? 0 : x);
- glVertex3f (0, xx, 0); glVertex3f (1, xx, 0);
- glVertex3f (yy, 0, 0); glVertex3f (yy, 1, 0);
- }
- }
- glEnd();
- }
- else /* Draw the texture quad */
- {
- GLfloat texw = img->geom.width / (GLfloat) img->tw;
- GLfloat texh = img->geom.height / (GLfloat) img->th;
- GLfloat texx1 = img->geom.x / (GLfloat) img->tw;
- GLfloat texy1 = img->geom.y / (GLfloat) img->th;
- GLfloat texx2 = texx1 + texw;
- GLfloat texy2 = texy1 + texh;
- GLfloat o = sp->opacity;
- GLint mag = (sp->fatbits_p ? GL_NEAREST : GL_LINEAR);
-
- glBindTexture (GL_TEXTURE_2D, img->texid);
-
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag);
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mag);
-
- /* o = 1 - sin ((1 - o*o*o) * M_PI/2); */
- glColor4f (1, 1, 1, o);
-
- glNormal3f (0, 0, 1);
- glBegin (GL_QUADS);
- glTexCoord2f (texx1, texy2); glVertex3f (0, 0, 0);
- glTexCoord2f (texx2, texy2); glVertex3f (1, 0, 0);
- glTexCoord2f (texx2, texy1); glVertex3f (1, 1, 0);
- glTexCoord2f (texx1, texy1); glVertex3f (0, 1, 0);
- glEnd();
-
- if (debug_p) /* Draw a border around the image */
- {
- if (!wire) glDisable (GL_TEXTURE_2D);
- glColor4f (sp->opacity, 0, 0, 1);
- glBegin (GL_LINE_LOOP);
- glVertex3f (0, 0, 0);
- glVertex3f (0, 1, 0);
- glVertex3f (1, 1, 0);
- glVertex3f (1, 0, 0);
- glEnd();
- if (!wire) glEnable (GL_TEXTURE_2D);
- }
- }
- }
- glPopMatrix();
-}
-
-
-static void
-draw_line_sprite (ModeInfo *mi, sprite *sp)
-{
- esper_state *ss = &sss[MI_SCREEN(mi)];
- int wire = MI_IS_WIREFRAME(mi);
- int w = MI_WIDTH(mi);
- int h = MI_HEIGHT(mi);
- int wh = (w > h ? w : h);
- int gs = (sp->type == RETICLE ? grid_size+1 : grid_size);
- int sx = wh / (gs + 1);
- int sy;
- int k;
- GLfloat t = grid_thickness * sp->thickness_scale;
- int fade;
- GLfloat color[4];
-
- GLfloat x = w * sp->current.x;
- GLfloat y = h * sp->current.y;
- GLfloat bw = w * sp->current.w;
- GLfloat bh = h * sp->current.h;
-
- if (MI_WIDTH(mi) > 2560) t *= 3; /* Retina displays */
-
- if (sx < 10) sx = 10;
- sy = sx;
-
- if (t > sx/3) t = sx/3;
- if (t < 1) t = 1;
- fade = t;
- if (fade < 1) fade = 1;
-
- if (t <= 0 || sp->opacity <= 0) return;
-
- glPushMatrix();
- glLoadIdentity();
-
- if (debug_p)
- {
- GLfloat s = 0.75;
- glScalef (s, s, s);
- }
-
- glOrtho (0, w, 0, h, -1, 1);
-
- switch (sp->type) {
- case GRID: memcpy (color, ss->grid_color, sizeof(color)); break;
- case RETICLE: memcpy (color, ss->reticle_color, sizeof(color)); break;
- case BOX: memcpy (color, ss->reticle_color, sizeof(color)); break;
- default: abort();
- }
-
- if (sp->type == GRID)
- {
- GLfloat s = 1 + (sp->thickness_scale - 1) / 120.0;
- glTranslatef (w/2, h/2, 0);
- glScalef (s, s, 1);
- glTranslatef (-w/2, -h/2, 0);
- }
-
- glColor4fv (color);
-
- if (!wire) glDisable (GL_TEXTURE_2D);
-
- for (k = 0; k < fade; k++)
- {
- GLfloat t2 = t * (1 - (k / (fade * 1.0)));
- if (t2 <= 0) break;
- color[3] = sp->opacity / fade;
- glColor4fv (color);
-
- glBegin (wire ? GL_LINES : GL_QUADS);
-
- switch (sp->type) {
- case GRID:
- {
- GLfloat xoff = (w - sx * (w / sx)) / 2.0;
- GLfloat yoff = (h - sy * (h / sy)) / 2.0;
- for (y = -sy/2+t2/2; y < h; y += sy)
- for (x = -sx/2-t2/2; x < w; x += sx)
- {
- glVertex3f (xoff+x+t2, yoff+y, 0);
- glVertex3f (xoff+x+t2, yoff+y+sy-t2, 0);
- glVertex3f (xoff+x, yoff+y+sy-t2, 0);
- glVertex3f (xoff+x, yoff+y, 0);
- mi->polygon_count++;
-
- glVertex3f (xoff+x, yoff+y-t2, 0);
- glVertex3f (xoff+x+sx, yoff+y-t2, 0);
- glVertex3f (xoff+x+sx, yoff+y, 0);
- glVertex3f (xoff+x, yoff+y, 0);
- mi->polygon_count++;
- }
- }
- break;
-
- case BOX:
- glVertex3f (x-bw/2-t2/2, y-bh/2-t2/2, 0);
- glVertex3f (x+bw/2+t2/2, y-bh/2-t2/2, 0);
- glVertex3f (x+bw/2+t2/2, y-bh/2+t2/2, 0);
- glVertex3f (x-bw/2-t2/2, y-bh/2+t2/2, 0);
- mi->polygon_count++;
-
- glVertex3f (x-bw/2-t2/2, y+bh/2-t2/2, 0);
- glVertex3f (x+bw/2+t2/2, y+bh/2-t2/2, 0);
- glVertex3f (x+bw/2+t2/2, y+bh/2+t2/2, 0);
- glVertex3f (x-bw/2-t2/2, y+bh/2+t2/2, 0);
- mi->polygon_count++;
-
- glVertex3f (x-bw/2+t2/2, y-bh/2+t2/2, 0);
- glVertex3f (x-bw/2+t2/2, y+bh/2-t2/2, 0);
- glVertex3f (x-bw/2-t2/2, y+bh/2-t2/2, 0);
- glVertex3f (x-bw/2-t2/2, y-bh/2+t2/2, 0);
- mi->polygon_count++;
-
- glVertex3f (x+bw/2+t2/2, y-bh/2+t2/2, 0);
- glVertex3f (x+bw/2+t2/2, y+bh/2-t2/2, 0);
- glVertex3f (x+bw/2-t2/2, y+bh/2-t2/2, 0);
- glVertex3f (x+bw/2-t2/2, y-bh/2+t2/2, 0);
- mi->polygon_count++;
- break;
-
- case RETICLE:
- glVertex3f (x+t2/2, y+sy/2-t2/2, 0);
- glVertex3f (x+t2/2, h, 0);
- glVertex3f (x-t2/2, h, 0);
- glVertex3f (x-t2/2, y+sy/2-t2/2, 0);
- mi->polygon_count++;
-
- glVertex3f (x-t2/2, y-sy/2+t2/2, 0);
- glVertex3f (x-t2/2, 0, 0);
- glVertex3f (x+t2/2, 0, 0);
- glVertex3f (x+t2/2, y-sy/2+t2/2, 0);
- mi->polygon_count++;
-
- glVertex3f (x-sx/2+t2/2, y+t2/2, 0);
- glVertex3f (0, y+t2/2, 0);
- glVertex3f (0, y-t2/2, 0);
- glVertex3f (x-sx/2+t2/2, y-t2/2, 0);
- mi->polygon_count++;
-
- glVertex3f (x+sx/2-t2/2, y-t2/2, 0);
- glVertex3f (w, y-t2/2, 0);
- glVertex3f (w, y+t2/2, 0);
- glVertex3f (x+sx/2-t2/2, y+t2/2, 0);
- mi->polygon_count++;
- break;
-
- default: abort();
- }
- glEnd();
- }
-
- glPopMatrix();
-
- if (!wire) glEnable (GL_TEXTURE_2D);
-}
-
-
-static sprite * find_newest_sprite (ModeInfo *, sprite_type);
-static void compute_image_rect (rect *, sprite *, Bool);
-
-static void
-draw_text_sprite (ModeInfo *mi, sprite *sp)
-{
- esper_state *ss = &sss[MI_SCREEN(mi)];
- int wire = MI_IS_WIREFRAME(mi);
- GLfloat w = MI_WIDTH(mi);
- GLfloat h = MI_HEIGHT(mi);
- GLfloat s;
- int x, y, z;
- XCharStruct e;
- sprite *target = 0;
- char text[255];
- GLfloat color[4];
- int i;
-
- if (sp->opacity <= 0)
- return;
-
- for (i = 0; i < ss->nsprites; i++)
- {
- sprite *sp2 = ss->sprites[i];
- if (sp2->id == sp->text_id && sp2->state != DEAD)
- {
- target = sp2;
- break;
- }
- }
-
- if (target)
- {
- rect r;
- sprite *img;
-
- if (target->opacity <= 0 &&
- (target->state == NEW || target->state == IN))
- return;
-
- r = target->current;
-
- img = find_newest_sprite (mi, IMAGE);
- if (img)
- compute_image_rect (&r, img, target->back_p);
-
- mi->recursion_depth = (img
- ? MIN (img->current.w, img->current.h)
- : 0);
-
- x = abs ((int) (r.x * 10000)) % 10000;
- y = abs ((int) (r.y * 10000)) % 10000;
- z = abs ((int) (r.w * 10000)) % 10000;
-
- sprintf (text, "ZM %04d NS %04d EW %04d", z, y, x);
-
- if ((x == 0 || x == 5000) && /* startup */
- (y == 0 || y == 5000) &&
- (z == 0 || z == 5000))
- *text = 0;
-
- if (do_titles &&
- target->type == IMAGE &&
- target->remain_p) /* The initial background image */
- {
- char *s = (target->img &&
- target->img->title && *target->img->title
- ? target->img->title
- : "Loading");
- int L = strlen (s);
- int i = (L > 23 ? L-23 : 0);
- sprintf (text, ">>%-23s", target->img->title + i);
- for (s = text; *s; s++)
- if (*s >= 'a' && *s <= 'z') *s += ('A'-'a');
- else if (*s == '/' || *s == '-' || *s == '.') *s = '_';
- }
-
- if (!*text) return;
-
- if (sp->text) free (sp->text);
- sp->text = strdup (text);
- }
- else if (sp->text && *sp->text)
- /* The target sprite might be dead, but we saved our last text. */
- strcpy (text, sp->text);
- else
- /* No target, no saved text. */
- return;
-
- texture_string_metrics (ss->font_data, text, &e, 0, 0);
-
- glPushMatrix();
- glLoadIdentity();
- glOrtho (0, 1, 0, 1, -1, 1);
-
- /* Scale the text to fit N characters horizontally. */
- {
-# ifdef HAVE_MOBILE
- GLfloat c = 25;
-# else /* desktop */
- GLfloat c = (MI_WIDTH(mi) <= 640 ? 25 :
- MI_WIDTH(mi) <= 1280 ? 32 : 64);
-# endif
- s = w / (e.ascent * c);
- }
- w /= s;
- h /= s;
- x = (w - e.width) / 2;
- y = e.ascent + e.descent * 2;
-
- glScalef (1.0/w, 1.0/h, 1);
- glTranslatef (x, y, 0);
-
- memcpy (color, ss->text_color, sizeof(color));
- color[3] = sp->opacity;
- glColor4fv (color);
-
- if (wire)
- glEnable (GL_TEXTURE_2D);
-
-#ifndef HAVE_ANDROID /* Doesn't work -- prevents image loading? */
- print_texture_string (ss->font_data, text);
-# endif
- mi->polygon_count++;
-
- if (wire)
- glDisable (GL_TEXTURE_2D);
- glPopMatrix();
-}
-
-
-static void
-draw_flash_sprite (ModeInfo *mi, sprite *sp)
-{
- /* esper_state *ss = &sss[MI_SCREEN(mi)]; */
- int wire = MI_IS_WIREFRAME(mi);
- GLfloat o = sp->opacity;
-
- if (o <= 0) return;
- o = 0.7; /* Too fast to see, so keep it consistent */
-
- glPushMatrix();
- if (!wire)
- glDisable (GL_TEXTURE_2D);
- glColor4f (0, 0, 1, o);
- glColorMask (0, 0, 1, 1); /* write only into blue and alpha channels */
- glBegin (GL_QUADS);
- glVertex3f (0, 0, 0);
- glVertex3f (1, 0, 0);
- glVertex3f (1, 1, 0);
- glVertex3f (0, 1, 0);
- glEnd();
- glColorMask (1, 1, 1, 1);
- if (!wire)
- glEnable (GL_TEXTURE_2D);
- glPopMatrix();
-}
-
-
-static void
-draw_sprite (ModeInfo *mi, sprite *sp)
-{
- glEnable (GL_BLEND);
- switch (sp->type) {
- case IMAGE:
- draw_image_sprite (mi, sp);
- break;
- case RETICLE:
- case BOX:
- case GRID:
- draw_line_sprite (mi, sp);
- break;
- case TEXT:
- draw_text_sprite (mi, sp);
- break;
- case FLASH:
- draw_flash_sprite (mi, sp);
- break;
- default:
- abort();
- }
-}
-
-
-static void
-tick_sprites (ModeInfo *mi)
-{
- esper_state *ss = &sss[MI_SCREEN(mi)];
- int i;
-
-# ifdef USE_ASYNC_LOADER
- double end_by_time = ss->now + ((double) mi->pause) / 2000000;
- for (i = 0; i < ss->nimages; i++)
- {
- image *img = ss->images[i];
- if (img->loader)
- {
- if (texture_loader_failed (img->loader))
- abort();
- step_texture_loader (img->loader, end_by_time - double_time(),
- image_loaded_cb, img);
- }
- }
-# endif
-
- for (i = 0; i < ss->nsprites; i++)
- tick_sprite (mi, ss->sprites[i]);
-
- for (i = 0; i < ss->nsprites; i++)
- {
- sprite *sp = ss->sprites[i];
- if (sp->state == DEAD)
- {
- destroy_sprite (mi, sp);
- i--;
- }
- }
-}
-
-
-static void
-draw_sprites (ModeInfo *mi)
-{
- esper_state *ss = &sss[MI_SCREEN(mi)];
- int i;
-
- glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
- glPushMatrix();
-
-/*
- {
- GLfloat rot = current_device_rotation();
- glTranslatef (0.5, 0.5, 0);
- glRotatef(rot, 0, 0, 1);
- if ((rot > 45 && rot < 135) ||
- (rot < -45 && rot > -135))
- {
- GLfloat s = MI_WIDTH(mi) / (GLfloat) MI_HEIGHT(mi);
- glScalef (s, 1/s, 1);
- }
- glTranslatef (-0.5, -0.5, 0);
- }
-*/
-
- /* Draw the images first, then the overlays. */
- for (i = 0; i < ss->nsprites; i++)
- if (ss->sprites[i]->type == IMAGE)
- draw_sprite (mi, ss->sprites[i]);
- for (i = 0; i < ss->nsprites; i++)
- if (ss->sprites[i]->type != IMAGE)
- draw_sprite (mi, ss->sprites[i]);
-
- glPopMatrix();
-
- if (debug_p) /* draw a white box (the "screen") */
- {
- int wire = MI_IS_WIREFRAME(mi);
-
- if (!wire) glDisable (GL_TEXTURE_2D);
-
- glColor4f (1, 1, 1, 1);
- glBegin (GL_LINE_LOOP);
- glVertex3f (0, 0, 0);
- glVertex3f (0, 1, 0);
- glVertex3f (1, 1, 0);
- glVertex3f (1, 0, 0);
- glEnd();
-
- if (!wire) glEnable (GL_TEXTURE_2D);
- }
-}
-
-
-static void
-fadeout_sprite (ModeInfo *mi, sprite *sp)
-{
- esper_state *ss = &sss[MI_SCREEN(mi)];
-
- /* If it hasn't faded in yet, don't fade out. */
- if (ss->now <= sp->start_time + sp->pause_duration)
- sp->fade_duration = 0;
-
- /* Pretend it's at the point where it should fade out. */
- sp->pause_duration = 0;
- sp->duration = 9999;
- sp->remain_p = False;
- sp->start_time = ss->now - sp->duration;
-}
-
-static void
-fadeout_sprites (ModeInfo *mi, sprite_type type)
-{
- esper_state *ss = &sss[MI_SCREEN(mi)];
- int i;
- for (i = 0; i < ss->nsprites; i++)
- {
- sprite *sp = ss->sprites[i];
- if (sp->type == type)
- fadeout_sprite (mi, sp);
- }
-}
-
-
-static sprite *
-find_newest_sprite (ModeInfo *mi, sprite_type type)
-{
- esper_state *ss = &sss[MI_SCREEN(mi)];
- sprite *sp = 0;
- int i;
- for (i = 0; i < ss->nsprites; i++)
- {
- sprite *sp2 = ss->sprites[i];
- if (sp2->type == type &&
- (!sp ||
- (sp->start_time < sp2->start_time &&
- ss->now >= sp2->start_time + sp2->pause_duration)))
- sp = sp2;
- }
- return sp;
-}
-
-
-/* Enqueue a text sprite describing the given sprite that runs at the
- same time.
- */
-static sprite *
-push_text_sprite (ModeInfo *mi, sprite *sp)
-{
- /* esper_state *ss = &sss[MI_SCREEN(mi)]; */
- sprite *sp2 = new_sprite (mi, TEXT);
- if (!sp2) abort();
- sp2->text_id = sp->id;
- sp2->fade_duration = sp->fade_duration;
- sp2->duration = sp->duration;
- sp2->pause_duration = sp->pause_duration;
- return sp2;
-}
-
-
-/* Enqueue a flash sprite that fires at the same time.
- */
-#ifndef SMOOTH
-static sprite *
-push_flash_sprite (ModeInfo *mi, sprite *sp)
-{
- /* esper_state *ss = &sss[MI_SCREEN(mi)]; */
- sprite *sp2 = new_sprite (mi, FLASH);
- if (!sp2) abort();
- if (sp->type != IMAGE) abort();
- sp2->text_id = sp->id;
- sp2->duration = MAX (0.07 / speed, 0.07);
- sp2->fade_duration = 0; /* Fading these is too fast to see */
- sp2->pause_duration = sp->pause_duration + (sp->fade_duration * 0.3);
- return sp2;
-}
-#endif /* !SMOOTH */
-
-
-/* Set the sprite's duration based on distance travelled.
- */
-static void
-compute_sprite_duration (ModeInfo *mi, sprite *sp, Bool blink_p)
-{
- /* Compute max distance traveled by any point (corners or center). */
- /* (cpp is the devil) */
-# define L(F) (sp->F.x - sp->F.w/2) /* delta of left edge, from/to */
-# define R(F) (1-(sp->F.x + sp->F.w/2)) /* right */
-# define B(F) (sp->F.y - sp->F.h/2) /* top */
-# define T(F) (1-(sp->F.y + sp->F.h/2)) /* bottom */
-# define D(F,G) sqrt(F(from)*F(from) + G(to)*G(to)) /* corner traveled */
- double BL = D(B,L);
- double BR = D(B,R);
- double TL = D(T,L);
- double TR = D(T,R);
- double cx = sp->to.x - sp->from.x;
- double cy = sp->to.y - sp->from.y;
- double C = sqrt(cx*cx + cy*cy);
- double dist = MAX (BL, MAX (BR, MAX (TL, MAX (TR, C))));
-# undef L
-# undef R
-# undef B
-# undef T
-# undef D
-
- int steps = 1 + dist * 28;
- if (steps > 10) steps = 10;
-
- sp->duration = steps * 0.2 / speed;
-
-# ifndef SMOOTH
- sp->duration += 1.5 / speed; /* For linger added by animate_sprite_path() */
- if (blink_p) sp->duration += 0.6 / speed;
-# endif
-}
-
-
-/* Convert the sprite to a jerky transition.
- Instead of smoothly animating, move in discrete steps,
- using multiple staggered sprites.
- */
-static void
-animate_sprite_path (ModeInfo *mi, sprite *sp, Bool blink_p)
-{
-# ifndef SMOOTH
- /* esper_state *ss = &sss[MI_SCREEN(mi)]; */
- double dx = sp->to.x - sp->from.x;
- double dy = sp->to.y - sp->from.y;
- double dw = sp->to.w - sp->from.w;
- double dh = sp->to.h - sp->from.h;
- double linger = 1.5 / speed;
- double blinger = 0.6 / speed;
- double dur = sp->duration - linger - (blink_p ? blinger : 0);
- int steps = dur / 0.3 * speed; /* step duration in seconds */
- int i;
-
- if (sp->type == IMAGE)
- steps *= 0.8;
-
- if (steps < 2) steps = 2;
- if (steps > 10) steps = 10;
-
- /* if (dur <= 0.01) abort(); */
- if (dur < 0.01)
- linger = blinger = 0;
-
- for (i = 0; i <= steps; i++)
- {
- sprite *sp2 = copy_sprite (mi, sp);
- if (!sp2) abort();
-
- sp2->to.x = (sp->current.x + i * dx / steps);
- sp2->to.y = (sp->current.y + i * dy / steps);
- sp2->to.w = (sp->current.w + i * dw / steps);
- sp2->to.h = (sp->current.h + i * dh / steps);
- sp2->current = sp2->from = sp2->to;
- sp2->duration = dur / steps;
- sp2->pause_duration += i * sp2->duration;
- sp2->remain_p = False;
- sp2->fatbits_p = True;
-
- if (i == steps)
- sp2->duration += linger; /* last one lingers for a bit */
-
- if (i == steps && !blink_p)
- {
- sp2->remain_p = sp->remain_p;
- sp2->fatbits_p = False;
- }
-
- if (sp2->type == IMAGE && i > 0)
- push_flash_sprite (mi, sp2);
-
- if (sp2->type == RETICLE || sp2->type == BOX)
- {
- sp2 = push_text_sprite (mi, sp2);
- if (i == steps)
- sp2->duration += linger * 2;
- }
- }
-
- if (blink_p && blinger) /* last one blinks before vanishing */
- {
- int blinkers = 3;
- for (i = 1; i <= blinkers; i++)
- {
- sprite *sp2 = copy_sprite (mi, sp);
- if (!sp2) abort();
-
- sp2->current = sp2->from = sp->to;
- sp2->duration = blinger / blinkers;
- sp2->pause_duration += dur + linger + i * sp2->duration;
- sp2->remain_p = False;
- if (i == blinkers)
- {
- sp2->remain_p = sp->remain_p;
- sp2->fatbits_p = False;
- }
- }
- }
-
- /* Fade out the template sprite. It might not have even appeared yet. */
- fadeout_sprite (mi, sp);
-# endif
-}
-
-
-/* Input rect is of a reticle or box.
- Output rect is what the image's rect should be so that the only part
- visible is the part indicated by the input rect.
- */
-static void
-compute_image_rect (rect *r, sprite *img, Bool inverse_p)
-{
- double scale = (inverse_p ? 1/r->w : r->w);
- double dx = r->x - 0.5;
- double dy = r->y - 0.5;
-
- /* Adjust size and center by zoom factor */
- r->w = img->current.w / scale;
- r->h = img->current.h / scale;
- r->x = 0.5 + (img->current.x - 0.5) / scale;
- r->y = 0.5 + (img->current.y - 0.5) / scale;
-
- /* Move center */
-
- if (inverse_p)
- {
- dx = -dx; /* #### Close but not quite right */
- dy = -dy;
- }
-
- r->x -= dx / scale;
- r->y -= dy / scale;
-}
-
-
-/* Sets 'to' such that the image zooms out so that the only part visible
- is the part indicated by the box.
- */
-static void
-track_box_with_image (ModeInfo *mi, sprite *sp, sprite *img)
-{
- rect r = sp->current;
- compute_image_rect (&r, img, sp->back_p);
- img->to = r;
-
- /* Never zoom out too far. */
- if (img->to.w < 1 && img->to.h < 1)
- {
- if (img->to.w > img->to.h)
- {
- img->to.w = img->to.w / img->to.h;
- img->to.h = 1;
- }
- else
- {
- img->to.h = img->to.h / img->to.w;
- img->to.w = 1;
- }
- }
-
- /* Never pan beyond the bounds of the image. */
- if (img->to.x < -img->to.w/2+1) img->to.x = -img->to.w/2+1;
- if (img->to.x > img->to.w/2) img->to.x = img->to.w/2;
- if (img->to.y < -img->to.h/2+1) img->to.y = -img->to.h/2+1;
- if (img->to.y > img->to.h/2) img->to.y = img->to.h/2;
-}
-
-
-static void
-tick_animation (ModeInfo *mi)
-{
- esper_state *ss = &sss[MI_SCREEN(mi)];
- anim_state prev_state = ss->anim_state;
- sprite *sp = 0;
- int i;
-
- switch (ss->anim_state) {
- case BLANK:
- ss->anim_state = GRID_ON;
- break;
- case GRID_ON:
- ss->anim_state = IMAGE_LOAD;
- break;
- case IMAGE_LOAD:
- /* Only advance once an image has loaded. */
- if (find_newest_sprite (mi, IMAGE))
- ss->anim_state = RETICLE_ON;
- else {
- ss->anim_state = IMAGE_LOAD;
- }
- break;
- case RETICLE_ON:
- ss->anim_state = RETICLE_MOVE;
- break;
- case RETICLE_MOVE:
- if (random() % 6)
- ss->anim_state = BOX_MOVE;
- else
- ss->anim_state = IMAGE_ZOOM;
- break;
- case BOX_MOVE:
- ss->anim_state = IMAGE_ZOOM;
- break;
- case IMAGE_ZOOM:
- {
- sprite *sp = find_newest_sprite (mi, IMAGE);
- double depth = (sp
- ? MIN (sp->current.w, sp->current.h)
- : 0);
- if (depth > 20)
- ss->anim_state = IMAGE_UNLOAD;
- else
- ss->anim_state = RETICLE_ON;
- }
- break;
- case IMAGE_FORCE_UNLOAD:
- ss->anim_state = IMAGE_UNLOAD;
- break;
- case IMAGE_UNLOAD:
- ss->anim_state = IMAGE_LOAD;
- break;
- case MANUAL_BOX_ON:
- ss->anim_state = MANUAL_BOX;
- break;
- case MANUAL_BOX:
- break;
- case MANUAL_RETICLE_ON:
- ss->anim_state = MANUAL_RETICLE;
- break;
- case MANUAL_RETICLE:
- break;
- default:
- abort();
- break;
- }
-
- ss->anim_start = ss->now;
- ss->anim_duration = 0;
-
- if (debug_p)
- fprintf (stderr, "%s: entering %s\n",
- progname, state_name (ss->anim_state));
-
- switch (ss->anim_state) {
-
- case GRID_ON: /* Start the grid fading in. */
- if (! find_newest_sprite (mi, GRID))
- {
- sp = new_sprite (mi, GRID);
- if (!sp) abort();
- sp->fade_duration = 1.0 / speed;
- sp->duration = 2.0 / speed;
- sp->remain_p = True;
- ss->anim_duration = (sp->pause_duration + sp->fade_duration * 2 +
- sp->duration);
- }
- break;
-
- case IMAGE_LOAD:
- fadeout_sprites (mi, IMAGE);
- sp = new_sprite (mi, IMAGE);
- if (! sp)
- {
- if (debug_p) fprintf (stderr, "%s: image load failed\n", progname);
- break;
- }
-
- sp->fade_duration = 0.5 / speed;
- sp->duration = sp->fade_duration * 3;
- sp->remain_p = True;
- /* If we zoom in, we lose the pulse at the end. */
- /* sp->from.w = sp->from.h = 0.0001; */
- sp->current = sp->from;
-
- ss->anim_duration = (sp->pause_duration + sp->fade_duration * 2 +
- sp->duration);
-
- sp = push_text_sprite (mi, sp);
- sp->fade_duration = 0.2 / speed;
- sp->pause_duration = 0;
- sp->duration = 2.5 / speed;
- break;
-
- case IMAGE_FORCE_UNLOAD:
- break;
-
- case IMAGE_UNLOAD:
- sp = find_newest_sprite (mi, IMAGE);
- if (sp)
- sp->fade_duration = ((prev_state == IMAGE_FORCE_UNLOAD ? 0.2 : 3.0)
- / speed);
- fadeout_sprites (mi, IMAGE);
- fadeout_sprites (mi, RETICLE);
- fadeout_sprites (mi, BOX);
- fadeout_sprites (mi, TEXT);
- ss->anim_duration = (sp ? sp->fade_duration : 0) + 3.5 / speed;
- break;
-
- case RETICLE_ON: /* Display reticle at center. */
- fadeout_sprites (mi, TEXT);
- sp = new_sprite (mi, RETICLE);
- if (!sp) abort();
- sp->fade_duration = 0.2 / speed;
- sp->pause_duration = 1.0 / speed;
- sp->duration = 1.5 / speed;
- ss->anim_duration = (sp->pause_duration + sp->fade_duration * 2 +
- sp->duration);
- ss->anim_duration -= sp->fade_duration * 2;
- break;
-
- case RETICLE_MOVE:
- /* Reticle has faded in. Now move it to somewhere else.
- Create N new reticle sprites, wih staggered pause_durations.
- */
- {
- GLfloat ox = 0.5;
- GLfloat oy = 0.5;
- GLfloat nx, ny, dist;
-
- do { /* pick a new position not too near the old */
- nx = 0.3 + BELLRAND(0.4);
- ny = 0.3 + BELLRAND(0.4);
- dist = sqrt ((nx-ox)*(nx-ox) + (ny-oy)*(ny-oy));
- } while (dist < 0.1);
-
- sp = new_sprite (mi, RETICLE);
- if (!sp) abort();
-
- sp->from.x = ox;
- sp->from.y = oy;
- sp->current = sp->to = sp->from;
- sp->to.x = nx;
- sp->to.y = ny;
- sp->fade_duration = 0.2 / speed;
- sp->pause_duration = 0;
- compute_sprite_duration (mi, sp, False);
-
- ss->anim_duration = (sp->pause_duration + sp->fade_duration * 2 +
- sp->duration - 0.1);
- animate_sprite_path (mi, sp, False);
- }
- break;
-
- case BOX_MOVE:
- /* Reticle has moved, and faded out.
- Start the box zooming into place.
- */
- {
- GLfloat ox = 0.5;
- GLfloat oy = 0.5;
- GLfloat nx, ny;
- GLfloat z;
-
- /* Find the last-added reticle, for our destination position. */
- sp = 0;
- for (i = 0; i < ss->nsprites; i++)
- {
- sprite *sp2 = ss->sprites[i];
- if (sp2->type == RETICLE &&
- (!sp || sp->start_time < sp2->start_time))
- sp = sp2;
- }
- if (sp)
- {
- nx = sp->to.x;
- ny = sp->to.y;
- }
- else
- {
- nx = ny = 0.5;
- if (debug_p)
- fprintf (stderr, "%s: no reticle before box?\n", progname);
- }
-
- z = 0.3 + frand(0.5);
-
- /* Ensure that the selected box is contained within the screen */
- {
- double margin = 0.005;
- double maxw = 2 * MIN (1 - margin - nx, nx - margin);
- double maxh = 2 * MIN (1 - margin - ny, ny - margin);
- double max = MIN (maxw, maxh);
- if (z > max) z = max;
- }
-
- sp = new_sprite (mi, BOX);
- if (!sp) abort();
- sp->from.x = ox;
- sp->from.y = oy;
- sp->from.w = 1.0;
- sp->from.h = 1.0;
- sp->current = sp->from;
- sp->to.x = nx;
- sp->to.y = ny;
- sp->to.w = z;
- sp->to.h = z;
-
- /* Maybe zoom out instead of in.
- */
- {
- sprite *img = find_newest_sprite (mi, IMAGE);
- double depth = MIN (img->current.w, img->current.h);
- if (depth > 1 && /* if zoomed in */
- (depth < 6 ? !(random() % 5) : /* 20% */
- depth < 12 ? !(random() % 2) : /* 50% */
- (random() % 3))) /* 66% */
- {
- sp->back_p = True;
- if (depth < 1.5 && z < 0.8)
- {
- z = 0.8; /* don't zoom out much past 100% */
- sp->to.w = z;
- sp->to.h = z;
- }
- }
- }
-
- sp->fade_duration = 0.2 / speed;
- sp->pause_duration = 2.0 / speed;
- compute_sprite_duration (mi, sp, True);
- ss->anim_duration = (sp->pause_duration + sp->fade_duration * 2 +
- sp->duration - 0.1);
- animate_sprite_path (mi, sp, True);
- }
- break;
-
- case IMAGE_ZOOM:
-
- /* Box has moved, and faded out.
- Or, if no box, then just a reticle.
- Zoom the underlying image to track the box's position. */
- {
- sprite *img, *img2;
-
- /* Find latest box or reticle, for our destination position. */
- sp = find_newest_sprite (mi, BOX);
- if (! sp)
- sp = find_newest_sprite (mi, RETICLE);
- if (! sp)
- {
- if (debug_p)
- fprintf (stderr, "%s: no box or reticle before image\n",
- progname);
- break;
- }
-
- img = find_newest_sprite (mi, IMAGE);
- if (!img)
- {
- if (debug_p)
- fprintf (stderr, "%s: no image?\n", progname);
- break;
- }
-
- img2 = copy_sprite (mi, img);
- if (!img2) abort();
-
- img2->from = img->current;
-
- fadeout_sprite (mi, img);
-
- track_box_with_image (mi, sp, img2);
-
- img2->fade_duration = 0.2 / speed;
- img2->pause_duration = 0.5 / speed;
- img2->remain_p = True;
- img2->throb_p = False;
- compute_sprite_duration (mi, img2, False);
-
- img->start_time += img2->pause_duration;
-
- ss->anim_duration = (img2->pause_duration + img2->fade_duration * 2 +
- img2->duration);
- animate_sprite_path (mi, img2, False);
- fadeout_sprites (mi, TEXT);
- }
- break;
-
- case MANUAL_BOX_ON:
- case MANUAL_RETICLE_ON:
- break;
-
- case MANUAL_BOX:
- case MANUAL_RETICLE:
- {
- sprite_type tt = (ss->anim_state == MANUAL_BOX ? BOX : RETICLE);
- sprite *osp = find_newest_sprite (mi, tt);
- fadeout_sprites (mi, RETICLE);
- fadeout_sprites (mi, BOX);
-
- sp = new_sprite (mi, tt);
- if (!sp) abort();
- if (osp)
- sp->from = osp->current;
- else
- {
- sp->from.x = 0.5;
- sp->from.y = 0.5;
- sp->from.w = 0.5;
- sp->from.h = 0.5;
- }
- sp->to = sp->current = sp->from;
- sp->fade_duration = 0.2 / speed;
- sp->duration = 0.2 / speed;
- sp->remain_p = True;
- sp->throb_p = False;
- ss->anim_duration = 9999;
- }
- break;
-
- default:
- fprintf (stderr,"%s: unknown state %d\n",
- progname, (int) ss->anim_state);
- abort();
- }
-}
-
-
-/* Copied from gltrackball.c, sigh.
- */
-static void
-adjust_for_device_rotation (double *x, double *y, double *w, double *h)
-{
- int rot = (int) current_device_rotation();
- int swap;
-
- while (rot <= -180) rot += 360;
- while (rot > 180) rot -= 360;
-
- if (rot > 135 || rot < -135) /* 180 */
- {
- *x = *w - *x;
- *y = *h - *y;
- }
- else if (rot > 45) /* 90 */
- {
- swap = *x; *x = *y; *y = swap;
- swap = *w; *w = *h; *h = swap;
- *x = *w - *x;
- }
- else if (rot < -45) /* 270 */
- {
- swap = *x; *x = *y; *y = swap;
- swap = *w; *w = *h; *h = swap;
- *y = *h - *y;
- }
-}
-
-
-ENTRYPOINT Bool
-esper_handle_event (ModeInfo *mi, XEvent *event)
-{
- esper_state *ss = &sss[MI_SCREEN(mi)];
-
- if (event->xany.type == Expose ||
- event->xany.type == GraphicsExpose ||
- event->xany.type == VisibilityNotify)
- {
- return False;
- }
- else if (event->xany.type == KeyPress)
- {
- KeySym keysym;
- char c = 0;
- sprite *sp = 0;
- double delta = 0.025;
- double margin = 0.005;
- Bool ok = False;
-
- XLookupString (&event->xkey, &c, 1, &keysym, 0);
-
- if (c == '\t')
- {
- ss->anim_state = IMAGE_FORCE_UNLOAD;
- return True;
- }
-
- if (! find_newest_sprite (mi, IMAGE))
- return False; /* Too early */
-
- ss->now = double_time();
-
-# define BONK() do { \
- if (ss->anim_state != MANUAL_BOX_ON && \
- ss->anim_state != MANUAL_BOX) { \
- ss->anim_state = MANUAL_BOX_ON; \
- tick_animation (mi); \
- } \
- sp = find_newest_sprite (mi, BOX); \
- if (!sp) abort() ; \
- sp->from = sp->current; \
- sp->to = sp->from; \
- sp->start_time = ss->now - sp->fade_duration; \
- ok = True; \
- } while(0)
-
- if (keysym == XK_Left || c == ',' || c == '<')
- {
- BONK();
- sp->to.x -= delta;
- }
- else if (keysym == XK_Right || c == '.' || c == '>')
- {
- BONK();
- sp->to.x += delta;
- }
- else if (keysym == XK_Down || c == '-')
- {
- BONK();
- sp->to.y -= delta;
- }
- else if (keysym == XK_Up || c == '=')
- {
- BONK();
- sp->to.y += delta, ok = True;
- }
- else if (keysym == XK_Prior || c == '+')
- {
- BONK();
- sp->to.w += delta;
- sp->to.h = sp->to.w * sp->from.w / sp->from.h;
- }
- else if (keysym == XK_Next || c == '_')
- {
- BONK();
- sp->to.w -= delta;
- sp->to.h = sp->to.w * sp->from.w / sp->from.h;
- }
- else if ((c == ' ' || c == '\t') && debug_p &&
- ss->anim_state == MANUAL_BOX)
- {
- BONK(); /* Null motion: just flash the current image. */
- }
- else if ((keysym == XK_Home || c == ' ' || c == '\t') &&
- ss->anim_state == MANUAL_BOX)
- {
- BONK();
- sp->to.x = 0.5;
- sp->to.y = 0.5;
- sp->to.w = 0.5;
- sp->to.h = 0.5;
- }
- else if ((c == '\r' || c == '\n' || c == 033) &&
- ss->anim_state == MANUAL_BOX)
- {
- BONK();
- ss->anim_state = BOX_MOVE;
- ss->anim_duration = 9999;
- ss->anim_start = ss->now - ss->anim_duration;
- fadeout_sprite (mi, sp);
- return True;
- }
- else
- return False;
-
- if (! ok)
- return False;
-
- /* Keep it on screen */
- if (sp->to.w > 1 - margin)
- {
- GLfloat r = sp->to.h / sp->to.w;
- sp->to.w = 1-margin;
- sp->to.h = (1-margin) * r;
- }
- if (sp->to.h > 1)
- {
- GLfloat r = sp->to.h / sp->to.w;
- sp->to.w = (1-margin) / r;
- sp->to.h = 1-margin;
- }
-
- if (sp->to.x - sp->to.w/2 < margin)
- sp->to.x = sp->to.w/2 + margin;
- if (sp->to.y - sp->to.h/2 < margin)
- sp->to.y = sp->to.h/2 + margin;
-
- if (sp->to.x + sp->to.w/2 >= 1 + margin)
- sp->to.x = 1 - (sp->to.w/2 + margin);
- if (sp->to.y + sp->to.h/2 >= 1 + margin)
- sp->to.y = 1 - (sp->to.h/2 + margin);
-
- /* Now let's give a momentary glimpse of what the image would do. */
- if (debug_p)
- {
- sprite *img = 0;
- int i;
-
- /* Find the lingering image */
- /* img = find__sprite (mi, IMAGE); */
- for (i = 0; i < ss->nsprites; i++)
- {
- sprite *sp2 = ss->sprites[i];
- if (sp2->type == IMAGE &&
- sp2->remain_p &&
- (!img ||
- (img->start_time < sp2->start_time &&
- ss->now >= sp2->start_time + sp2->pause_duration)))
- img = sp2;
- }
-
- if (!img) abort();
- img = copy_sprite (mi, img);
- img->pause_duration = 0;
- img->fade_duration = 0.1 / speed;
- img->duration = 0.5 / speed;
- img->start_time = ss->now;
- img->remain_p = False;
- track_box_with_image (mi, sp, img);
- img->from = img->current = img->to;
- }
-
- return True;
- }
- else if (event->xany.type == ButtonPress &&
- event->xbutton.button == Button1)
- {
- ss->button_down_p = 1;
- return True;
- }
- else if (event->xany.type == ButtonRelease &&
- event->xbutton.button == Button1)
- {
- ss->button_down_p = 0;
-
- if (ss->anim_state == MANUAL_BOX)
- {
- sprite *sp = find_newest_sprite (mi, BOX);
- if (sp) fadeout_sprite (mi, sp);
- ss->anim_state = BOX_MOVE;
- ss->anim_duration = 9999;
- ss->anim_start = ss->now - ss->anim_duration;
- }
- else if (ss->anim_state == MANUAL_RETICLE)
- {
- sprite *sp = find_newest_sprite (mi, RETICLE);
- if (sp) fadeout_sprite (mi, sp);
- ss->anim_state = RETICLE_MOVE;
- ss->anim_duration = 9999;
- ss->anim_start = ss->now - ss->anim_duration;
- }
- return True;
- }
- else if (event->xany.type == MotionNotify &&
- ss->button_down_p &&
- (ss->anim_state == MANUAL_RETICLE ||
- ss->anim_state == RETICLE_MOVE))
- {
- sprite *sp = 0;
- double x = event->xmotion.x;
- double y = event->xmotion.y;
- double w = MI_WIDTH(mi);
- double h = MI_HEIGHT(mi);
-
- adjust_for_device_rotation (&x, &y, &w, &h);
- x = x/w;
- y = 1-y/h;
-
- if (ss->anim_state != MANUAL_RETICLE_ON &&
- ss->anim_state != MANUAL_RETICLE)
- {
- ss->anim_state = MANUAL_RETICLE_ON;
- tick_animation (mi);
- }
- sp = find_newest_sprite (mi, RETICLE);
- if (!sp) abort();
- sp->from = sp->current;
- sp->to = sp->from;
- sp->start_time = ss->now - sp->fade_duration;
- sp->remain_p = True;
-
- sp->current.x = MIN (0.95, MAX (0.05, x));
- sp->current.y = MIN (0.95, MAX (0.05, y));
- sp->from = sp->to = sp->current;
-
- /* Don't update the text sprite more often than once a second. */
- {
- sprite *sp2 = find_newest_sprite (mi, TEXT);
- if (!sp2 || sp2->start_time < ss->now-1)
- {
- fadeout_sprites (mi, TEXT);
- sp = push_text_sprite (mi, sp);
- sp->remain_p = True;
- }
- }
-
- return True;
- }
- else if (event->xany.type == MotionNotify &&
- ss->button_down_p &&
- (ss->anim_state == MANUAL_BOX ||
- ss->anim_state == BOX_MOVE))
- {
- sprite *sp = 0;
- double x = event->xmotion.x;
- double y = event->xmotion.y;
- double w = MI_WIDTH(mi);
- double h = MI_HEIGHT(mi);
- double max;
- Bool ok = True;
-
- adjust_for_device_rotation (&x, &y, &w, &h);
- x = x/w;
- y = 1-y/h;
-
- BONK();
- max = (2 * (0.5 - MAX (fabs (sp->current.x - 0.5),
- fabs (sp->current.y - 0.5)))
- * 0.95);
-
- x = fabs (x - sp->current.x);
- y = fabs (y - sp->current.y);
-
- if (x > y)
- sp->current.w = sp->current.h = MIN (max, MAX (0.05, 2*x));
- else
- sp->current.w = sp->current.h = MIN (max, MAX (0.05, 2*y));
- sp->from = sp->to = sp->current;
-
- /* Don't update the text sprite more often than once a second. */
- {
- sprite *sp2 = find_newest_sprite (mi, TEXT);
- if (!sp2 || sp2->start_time < ss->now-1)
- {
- fadeout_sprites (mi, TEXT);
- sp = push_text_sprite (mi, sp);
- sp->remain_p = True;
- }
- }
-
- return ok;
- }
- else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
- {
- ss->anim_state = IMAGE_FORCE_UNLOAD;
- return True;
- }
-# undef BONK
-
- return False;
-}
-
-
-ENTRYPOINT void
-reshape_esper (ModeInfo *mi, int width, int height)
-{
- GLfloat s;
-
- glViewport (0, 0, width, height);
- glMatrixMode (GL_PROJECTION);
- glLoadIdentity();
- glRotatef (current_device_rotation(), 0, 0, 1);
- glMatrixMode (GL_MODELVIEW);
- glLoadIdentity();
-
- s = 2;
-
- if (debug_p)
- {
- s *= 0.75;
- if (s < 0.1) s = 0.1;
- }
-
- glScalef (s, s, s);
- glTranslatef (-0.5, -0.5, 0);
-
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
- /* Stretch each existing image to match new window aspect. */
- {
- esper_state *ss = &sss[MI_SCREEN(mi)];
- int i;
- for (i = 0; i < ss->nsprites; i++)
- {
- sprite *sp = ss->sprites[i];
- if (sp && sp->type == IMAGE && sp->img && sp->img->loaded_p)
- {
- GLfloat sp_asp = sp->current.h / sp->current.w;
- GLfloat img_asp = (sp->img->geom.height /
- (GLfloat) sp->img->geom.width);
- GLfloat new_win = (MI_WIDTH(mi) / (double) MI_HEIGHT(mi));
- GLfloat old_win = sp_asp / img_asp;
- GLfloat r = old_win / new_win;
- if (img_asp > 1)
- {
- sp->from.h /= r;
- sp->current.h /= r;
- sp->to.h /= r;
- }
- else
- {
- sp->from.w *= r;
- sp->current.w *= r;
- sp->to.w *= r;
- }
- }
- }
- }
-}
-
-
-static void
-parse_color (ModeInfo *mi, char *key, GLfloat color[4])
-{
- XColor xcolor;
- char *string = get_string_resource (mi->dpy, key, "EsperColor");
- if (!XParseColor (mi->dpy, mi->xgwa.colormap, string, &xcolor))
- {
- fprintf (stderr, "%s: unparsable color in %s: %s\n", progname,
- key, string);
- exit (1);
- }
- free (string);
-
- color[0] = xcolor.red / 65536.0;
- color[1] = xcolor.green / 65536.0;
- color[2] = xcolor.blue / 65536.0;
- color[3] = 1;
-}
-
-
-ENTRYPOINT void
-init_esper (ModeInfo *mi)
-{
- int screen = MI_SCREEN(mi);
- esper_state *ss;
-
- MI_INIT (mi, sss);
- ss = &sss[screen];
-
- if ((ss->glx_context = init_GL(mi)) != NULL) {
- reshape_esper (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
- } else {
- MI_CLEARWINDOW(mi);
- }
-
- parse_color (mi, "gridColor", ss->grid_color);
- parse_color (mi, "reticleColor", ss->reticle_color);
- parse_color (mi, "textColor", ss->text_color);
-
- ss->font_data = load_texture_font (mi->dpy, "titleFont");
-
- ss->now = double_time();
- ss->dawn_of_time = ss->now;
-
- alloc_image (mi);
-
- ss->anim_state = BLANK;
- ss->anim_start = 0;
- ss->anim_duration = 0;
-}
-
-
-ENTRYPOINT void
-draw_esper (ModeInfo *mi)
-{
- esper_state *ss = &sss[MI_SCREEN(mi)];
- int wire = MI_IS_WIREFRAME(mi);
-
- if (!ss->glx_context)
- return;
-
- glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *ss->glx_context);
-
- mi->polygon_count = 0;
-
- ss->now = double_time();
-
- glDisable (GL_LIGHTING);
- glDisable (GL_DEPTH_TEST);
- glDepthMask (GL_FALSE);
- glEnable (GL_CULL_FACE);
- glCullFace (GL_BACK);
-
- if (! wire)
- {
- glEnable (GL_TEXTURE_2D);
- glShadeModel (GL_SMOOTH);
- glEnable (GL_BLEND);
- glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- }
-
- tick_sprites (mi);
- draw_sprites (mi);
- if (ss->now >= ss->anim_start + ss->anim_duration)
- tick_animation (mi);
-
- if (mi->fps_p) do_fps (mi);
-
- glFinish();
- glXSwapBuffers (MI_DISPLAY (mi), MI_WINDOW(mi));
-}
-
-
-ENTRYPOINT void
-free_esper (ModeInfo *mi)
-{
- esper_state *ss = &sss[MI_SCREEN(mi)];
- int i;
- if (!ss->glx_context) return;
- glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *ss->glx_context);
-
- if (ss->font_data) free_texture_font (ss->font_data);
- for (i = 0; i < ss->nimages; i++) {
- if (ss->images[i]) {
- if (ss->images[i]->title) free (ss->images[i]->title);
- if (ss->images[i]->texid) glDeleteTextures (1, &ss->images[i]->texid);
- free (ss->images[i]);
- }
- }
- for (i = 0; i < countof(ss->sprites); i++) {
- if (ss->sprites[i]) {
- if (ss->sprites[i]->text) free (ss->sprites[i]->text);
- if (ss->sprites[i]) free (ss->sprites[i]);
- }
- }
-}
-
-XSCREENSAVER_MODULE ("Esper", esper)
-
-#endif /* USE_GL */