-/* glslideshow, Copyright (c) 2003-2005 Jamie Zawinski <jwz@jwz.org>
+/* glslideshow, Copyright (c) 2003-2014 Jamie Zawinski <jwz@jwz.org>
* Loads a sequence of images and smoothly pans around them; crossfades
* when loading new images.
*
* thread support at a lower level?
*/
-#include <X11/Intrinsic.h>
-
-# define PROGCLASS "GLSlideshow"
-# define HACK_INIT init_slideshow
-# define HACK_DRAW draw_slideshow
-# define HACK_RESHAPE reshape_slideshow
-# define HACK_HANDLE_EVENT glslideshow_handle_event
-# define EVENT_MASK (ExposureMask|VisibilityChangeMask)
-# define slideshow_opts xlockmore_opts
-
-# define DEF_FADE_DURATION "2"
-# define DEF_PAN_DURATION "6"
-# define DEF_IMAGE_DURATION "30"
-# define DEF_ZOOM "75"
-# define DEF_FPS_CUTOFF "5"
-# define DEF_TITLES "False"
-# define DEF_LETTERBOX "True"
-# define DEF_DEBUG "False"
-# define DEF_MIPMAP "True"
-
#define DEFAULTS "*delay: 20000 \n" \
"*wireframe: False \n" \
"*showFPS: False \n" \
"*fpsSolid: True \n" \
"*useSHM: True \n" \
- "*titleFont: -*-times-bold-r-normal-*-180-*\n" \
- "*desktopGrabber: xscreensaver-getimage -no-desktop %s\n"
+ "*titleFont: -*-helvetica-medium-r-normal-*-*-180-*-*-*-*-*-*\n" \
+ "*desktopGrabber: xscreensaver-getimage -no-desktop %s\n" \
+ "*grabDesktopImages: False \n" \
+ "*chooseRandomImages: True \n"
+# define refresh_slideshow 0
+# define release_slideshow 0
# include "xlockmore.h"
#undef countof
#ifdef USE_GL
-#include <GL/glu.h>
-#include <math.h>
-#include <sys/time.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include "grab-ximage.h"
-#include "glxfonts.h"
-extern XtAppContext app;
+# define DEF_FADE_DURATION "2"
+# define DEF_PAN_DURATION "6"
+# define DEF_IMAGE_DURATION "30"
+# define DEF_ZOOM "75"
+# define DEF_FPS_CUTOFF "5"
+# define DEF_TITLES "False"
+# define DEF_LETTERBOX "True"
+# define DEF_DEBUG "False"
+# define DEF_MIPMAP "True"
+
+#include "grab-ximage.h"
+#include "texfont.h"
typedef struct {
double x, y, w, h;
double image_load_time; /* time when we last loaded a new image */
double prev_frame_time; /* time when we last drew a frame */
+ Bool awaiting_first_image_p; /* Early in startup: nothing to display yet */
Bool redisplay_needed_p; /* Sometimes we can get away with not
re-painting. Tick this if a redisplay
is required. */
Bool checked_fps_p; /* Whether we have checked for a low
frame rate. */
- XFontStruct *xfont; /* for printing image file names */
- GLuint font_dlist;
+ texture_font_data *font_data; /* for printing image file names */
int sprite_id, image_id; /* debugging id counters */
+ double time_elapsed;
+ int frames_elapsed;
+
} slideshow_state;
static slideshow_state *sss = NULL;
{"-cutoff", ".FPScutoff", XrmoptionSepArg, 0 },
{"-titles", ".titles", XrmoptionNoArg, "True" },
{"-letterbox", ".letterbox", XrmoptionNoArg, "True" },
- {"-clip", ".letterbox", XrmoptionNoArg, "False" },
+ {"-no-letterbox", ".letterbox", XrmoptionNoArg, "False" },
+ {"-clip", ".letterbox", XrmoptionNoArg, "False" },
{"-mipmaps", ".mipmap", XrmoptionNoArg, "True" },
{"-no-mipmaps", ".mipmap", XrmoptionNoArg, "False" },
{"-debug", ".debug", XrmoptionNoArg, "True" },
{ &do_titles, "titles", "Titles", DEF_TITLES, t_Bool},
};
-ModeSpecOpt slideshow_opts = {countof(opts), opts, countof(vars), vars, NULL};
+ENTRYPOINT ModeSpecOpt slideshow_opts = {countof(opts), opts, countof(vars), vars, NULL};
static const char *
blurb (void)
{
+# ifdef HAVE_JWXYZ
+ return "GLSlideshow";
+# else
static char buf[255];
time_t now = time ((time_t *) 0);
char *ct = (char *) ctime (&now);
strncpy(buf+n, ct+11, 8);
strcpy(buf+n+9, ": ");
return buf;
+# endif
}
slideshow_state *ss = &sss[MI_SCREEN(mi)];
int wire = MI_IS_WIREFRAME(mi);
image *img = (image *) calloc (1, sizeof (*img));
- Bool async_p = True;
img->id = ++ss->image_id;
img->loaded_p = False;
if (wire)
image_loaded_cb (0, 0, 0, 0, 0, 0, img);
- else if (async_p)
- screen_to_texture_async (mi->xgwa.screen, mi->window, 0, 0, mipmap_p,
- img->texid, image_loaded_cb, img);
else
- {
- char *filename = 0;
- XRectangle geom;
- int iw=0, ih=0, tw=0, th=0;
- glBindTexture (GL_TEXTURE_2D, img->texid);
-
- if (! screen_to_texture (mi->xgwa.screen, mi->window, 0, 0, mipmap_p,
- &filename, &geom, &iw, &ih, &tw, &th))
- exit(1);
- image_loaded_cb (filename, &geom, iw, ih, tw, th, img);
- if (filename) free (filename);
- }
+ load_texture_async (mi->xgwa.screen, mi->window, *ss->glx_context,
+ 0, 0, mipmap_p, img->texid, image_loaded_cb, img);
ss->images[ss->nimages++] = img;
if (ss->nimages >= countof(ss->images)) abort();
}
-/* Block until the first image is completely loaded.
- We normally load images in the background, but we have nothing to draw
- until we get that first image...
- */
-static void
-await_first_image (ModeInfo *mi)
-{
- slideshow_state *ss = &sss[MI_SCREEN(mi)];
- image *img;
- int i = 0;
- if (ss->nimages != 0) abort();
- img = alloc_image (mi);
-
- while (! img->loaded_p)
- {
- usleep (100000); /* check every 1/10th sec */
- if (i++ > 600) abort(); /* if a minute has passed, we're broken */
-
- while (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
- XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
- }
-
- if (debug_p)
- fprintf (stderr, "\n");
-}
-
-
/* Callback that tells us that the texture has been loaded.
*/
static void
img->geom.height *= scale;
}
- if (img->title) /* strip filename to part after last /. */
+ /* 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 (debug_p)
int vp_h = MI_HEIGHT(mi);
int img_w = sp->img->geom.width;
int img_h = sp->img->geom.height;
- int min_w, min_h, max_w, max_h;
+ int min_w, max_w;
double ratio = (double) img_h / img_w;
if (letterbox_p)
{
min_w = img_w;
- min_h = img_h;
}
else
{
if (img_w < vp_w)
- {
- min_w = vp_w;
- min_h = img_h * (float) vp_w / img_w;
- }
+ min_w = vp_w;
else
- {
- min_w = img_w * (float) vp_h / img_h;
- min_h = vp_h;
- }
+ min_w = img_w * (float) vp_h / img_h;
}
max_w = min_w * 100 / zoom;
- max_h = min_h * 100 / zoom;
sp->from.w = min_w + frand ((max_w - min_w) * 0.4);
sp->to.w = max_w - frand ((max_w - min_w) * 0.4);
sp->from = swap;
}
- /* Make sure the aspect ratios are within 0.0001 of each other.
+ /* Make sure the aspect ratios are within 0.001 of each other.
*/
- if ((int) (0.5 + (sp->from.w * 1000 / sp->from.h)) !=
- (int) (0.5 + (sp->to.w * 1000 / sp->to.h)))
- abort();
+ {
+ int r1 = 0.5 + (sp->from.w * 1000 / sp->from.h);
+ int r2 = 0.5 + (sp->to.w * 1000 / sp->to.h);
+ if (r1 < r2-1 || r1 > r2+1)
+ {
+ fprintf (stderr,
+ "%s: botched aspect: %f x %f (%d) vs %f x %f (%d): %s\n",
+ progname,
+ sp->from.w, sp->from.h, r1,
+ sp->to.w, sp->to.h, r2,
+ (sp->img->title ? sp->img->title : "[null]"));
+ abort();
+ }
+ }
sp->from.x /= vp_w;
sp->from.y /= vp_h;
if (do_titles &&
- img->title && *img->title)
+ img->title && *img->title &&
+ (sp->state == IN || sp->state == FULL))
{
- int x = 10;
- int y = mi->xgwa.height - 10;
- glColor4f (0, 0, 0, sp->opacity); /* cheap-assed dropshadow */
- print_gl_string (mi->dpy, ss->xfont, ss->font_dlist,
- mi->xgwa.width, mi->xgwa.height, x, y,
- img->title);
- x++; y++;
glColor4f (1, 1, 1, sp->opacity);
- print_gl_string (mi->dpy, ss->xfont, ss->font_dlist,
- mi->xgwa.width, mi->xgwa.height, x, y,
- img->title);
+ print_texture_label (mi->dpy, ss->font_data,
+ mi->xgwa.width, mi->xgwa.height,
+ 1, img->title);
}
}
glPopMatrix();
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);
+ }
+*/
+
for (i = 0; i < ss->nsprites; i++)
draw_sprite (mi, ss->sprites[i]);
glPopMatrix();
}
-void
+ENTRYPOINT void
reshape_slideshow (ModeInfo *mi, int width, int height)
{
slideshow_state *ss = &sss[MI_SCREEN(mi)];
glViewport (0, 0, width, height);
glMatrixMode (GL_PROJECTION);
glLoadIdentity();
+ glRotatef (current_device_rotation(), 0, 0, 1);
glMatrixMode (GL_MODELVIEW);
glLoadIdentity();
}
-Bool
-glslideshow_handle_event (ModeInfo *mi, XEvent *event)
+ENTRYPOINT Bool
+slideshow_handle_event (ModeInfo *mi, XEvent *event)
{
slideshow_state *ss = &sss[MI_SCREEN(mi)];
- if (event->xany.type == ButtonPress &&
- event->xbutton.button == Button1)
- {
- ss->change_now_p = True;
- return True;
- }
- else if (event->xany.type == KeyPress)
- {
- KeySym keysym;
- char c = 0;
- XLookupString (&event->xkey, &c, 1, &keysym, 0);
- if (c == ' ' || c == '\r' || c == '\n' || c == '\t')
- {
- ss->change_now_p = True;
- return True;
- }
- }
- else if (event->xany.type == Expose ||
+ if (event->xany.type == Expose ||
event->xany.type == GraphicsExpose ||
event->xany.type == VisibilityNotify)
{
fprintf (stderr, "%s: exposure\n", blurb());
return False;
}
+ else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
+ {
+ ss->change_now_p = True;
+ return True;
+ }
return False;
}
static void
check_fps (ModeInfo *mi)
{
+#ifndef HAVE_JWXYZ /* always assume Cocoa and mobile are fast enough */
+
slideshow_state *ss = &sss[MI_SCREEN(mi)];
- static double time_elapsed = 0;
- static int frames_elapsed = 0;
double start_time, end_time, wall_elapsed, frame_duration, fps;
int i;
start_time = ss->now;
end_time = double_time();
frame_duration = end_time - start_time; /* time spent drawing this frame */
- time_elapsed += frame_duration; /* time spent drawing all frames */
- frames_elapsed++;
+ ss->time_elapsed += frame_duration; /* time spent drawing all frames */
+ ss->frames_elapsed++;
wall_elapsed = end_time - ss->dawn_of_time;
- fps = frames_elapsed / time_elapsed;
+ fps = ss->frames_elapsed / ss->time_elapsed;
ss->theoretical_fps = fps;
if (ss->checked_fps_p) return;
if (debug_p)
fprintf (stderr,
"%s: %.1f fps is fast enough (with %d frames in %.1f secs)\n",
- blurb(), fps, frames_elapsed, wall_elapsed);
+ blurb(), fps, ss->frames_elapsed, wall_elapsed);
return;
}
/* Need this in case zoom changed. */
reshape_slideshow (mi, mi->xgwa.width, mi->xgwa.height);
+#endif /* HAVE_JWXYZ */
}
}
-void
+ENTRYPOINT void
init_slideshow (ModeInfo *mi)
{
int screen = MI_SCREEN(mi);
if (debug_p) glLineWidth (3);
- load_font (mi->dpy, "titleFont", &ss->xfont, &ss->font_dlist);
+ ss->font_data = load_texture_font (mi->dpy, "titleFont");
if (debug_p)
hack_resources();
ss->dawn_of_time = ss->now;
ss->prev_frame_time = ss->now;
- await_first_image (mi); /* wait for first image to fully load */
-
- ss->now = double_time();
- ss->dawn_of_time = ss->now;
-
- new_sprite (mi); /* start first sprite fading in */
+ ss->awaiting_first_image_p = True;
+ alloc_image (mi);
}
-void
+ENTRYPOINT void
draw_slideshow (ModeInfo *mi)
{
slideshow_state *ss = &sss[MI_SCREEN(mi)];
if (!ss->glx_context)
return;
+ glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(ss->glx_context));
+
+ if (ss->awaiting_first_image_p)
+ {
+ image *img = ss->images[0];
+ if (!img) abort();
+ if (!img->loaded_p)
+ return;
+
+ ss->awaiting_first_image_p = False;
+ ss->dawn_of_time = double_time();
+
+ /* start the very first sprite fading in */
+ new_sprite (mi);
+ }
+
ss->now = double_time();
/* Each sprite has three states: fading in, full, fading out.
draw_sprites (mi);
- ss->fps = fps_1 (mi);
- if (mi->fps_p) fps_2 (mi);
+ if (mi->fps_p) do_fps (mi);
glFinish();
glXSwapBuffers (MI_DISPLAY (mi), MI_WINDOW(mi));
check_fps (mi);
}
+XSCREENSAVER_MODULE_2 ("GLSlideshow", glslideshow, slideshow)
+
#endif /* USE_GL */