Skip to content

AFNI/NIfTI Server

Sections
Personal tools
You are here: Home » AFNI » Documentation

Doxygen Source Code Documentation


Main Page   Alphabetical List   Data Structures   File List   Data Fields   Globals   Search  

gifview.c

Go to the documentation of this file.
00001 /* gifview.c - gifview's main loop.
00002    Copyright (C) 1997-2001 Eddie Kohler, eddietwo@lcs.mit.edu
00003    This file is part of gifview, in the gifsicle package.
00004 
00005    Gifview is free software. It is distributed under the GNU Public License,
00006    version 2 or later; you can copy, distribute, or alter it at will, as long
00007    as this notice is kept intact and this source code is made available. There
00008    is no warranty, express or implied. */
00009 
00010 #include "config.h"
00011 #ifdef X_DISPLAY_MISSING
00012 #error "You can't compile gifview without X."
00013 #endif
00014 
00015 #include "gifx.h"
00016 #include "clp.h"
00017 #include <X11/Xlib.h>
00018 #include <X11/Xutil.h>
00019 #include <X11/Xos.h>
00020 #include <X11/keysym.h>
00021 #include <X11/cursorfont.h>
00022 #ifdef HAVE_SYS_SELECT_H
00023 # include <sys/select.h>
00024 #endif
00025 #include <string.h>
00026 #include <stdio.h>
00027 #include <stdlib.h>
00028 #include <stdarg.h>
00029 #include <ctype.h>
00030 #include <errno.h>
00031 #include <assert.h>
00032 
00033 #ifdef __cplusplus
00034 #define EXTERN extern "C"
00035 #else
00036 #define EXTERN extern
00037 #endif
00038 
00039 #define SAVE_FRAMES 10
00040 
00041 /*****
00042  * TIME STUFF (from xwrits)
00043  **/
00044 
00045 #define MICRO_PER_SEC 1000000
00046 
00047 #define xwADDTIME(result, a, b) do { \
00048         (result).tv_sec = (a).tv_sec + (b).tv_sec; \
00049         if (((result).tv_usec = (a).tv_usec + (b).tv_usec) >= MICRO_PER_SEC) { \
00050                 (result).tv_sec++; \
00051                 (result).tv_usec -= MICRO_PER_SEC; \
00052         } } while (0)
00053 
00054 #define xwSUBTIME(result, a, b) do { \
00055         (result).tv_sec = (a).tv_sec - (b).tv_sec; \
00056         if (((result).tv_usec = (a).tv_usec - (b).tv_usec) < 0) { \
00057                 (result).tv_sec--; \
00058                 (result).tv_usec += MICRO_PER_SEC; \
00059         } } while (0)
00060 
00061 #define xwSETMINTIME(a, b) do { \
00062         if ((b).tv_sec < (a).tv_sec || \
00063             ((b).tv_sec == (a).tv_sec && (b).tv_usec < (a).tv_usec)) \
00064                 (a) = (b); \
00065         } while (0)
00066 
00067 #define xwTIMEGEQ(a, b) ((a).tv_sec > (b).tv_sec || \
00068         ((a).tv_sec == (b).tv_sec && (a).tv_usec >= (b).tv_usec))
00069 
00070 #define xwTIMEGT(a, b) ((a).tv_sec > (b).tv_sec || \
00071         ((a).tv_sec == (b).tv_sec && (a).tv_usec > (b).tv_usec))
00072 
00073 #define xwTIMELEQ0(a) ((a).tv_sec < 0 || ((a).tv_sec == 0 && (a).tv_usec <= 0))
00074 
00075 #ifdef X_GETTIMEOFDAY
00076 # define xwGETTIMEOFDAY(a) X_GETTIMEOFDAY(a)
00077 #elif GETTIMEOFDAY_PROTO == 0
00078 EXTERN int gettimeofday(struct timeval *, struct timezone *);
00079 # define xwGETTIMEOFDAY(a) gettimeofday((a), 0)
00080 #elif GETTIMEOFDAY_PROTO == 1
00081 # define xwGETTIMEOFDAY(a) gettimeofday((a))
00082 #else
00083 # define xwGETTIMEOFDAY(a) gettimeofday((a), 0)
00084 #endif
00085 
00086 #define xwGETTIME(a) do { xwGETTIMEOFDAY(&(a)); xwSUBTIME((a), (a), genesis_time); } while (0)
00087 struct timeval genesis_time;
00088 
00089 
00090 /*****
00091  * THE VIEWER STRUCTURE
00092  **/
00093 
00094 typedef struct Gt_Viewer {
00095   
00096   Display *display;
00097   int screen_number;
00098   Visual *visual;
00099   int depth;
00100   Colormap colormap;
00101   Gif_XContext *gfx;
00102   Cursor arrow_cursor;
00103   Cursor wait_cursor;
00104   
00105   Window parent;
00106   int top_level;
00107   
00108   Window window;
00109   int use_window;
00110   int width;
00111   int height;
00112   int resizable;
00113   int being_deleted;
00114   
00115   Gif_Stream *gfs;
00116   char *name;
00117   
00118   Gif_Image **im;
00119   int nim;
00120   
00121   Pixmap pixmap;
00122   int im_pos;
00123   int was_unoptimized;
00124   int free_pixmaps;
00125   
00126   Pixmap *unoptimized_pixmaps;
00127   
00128   struct Gt_Viewer *next;
00129   
00130   int can_animate;
00131   int animating;
00132   int unoptimizing;
00133   int scheduled;
00134   struct Gt_Viewer *anim_next;
00135   struct timeval timer;
00136   int anim_loop;
00137   
00138 } Gt_Viewer;
00139 
00140 const char *program_name = "gifview";
00141 
00142 static char *cur_display_name = 0;
00143 static Display *cur_display = 0;
00144 static char *cur_geometry_spec = 0;
00145 static Cursor cur_arrow_cursor = 0;
00146 static Cursor cur_wait_cursor = 0;
00147 static const char *cur_resource_name;
00148 static Window cur_use_window = None;
00149 static const char *cur_background_color = "black";
00150 
00151 static Gt_Viewer *viewers;
00152 static Gt_Viewer *animations;
00153 static int animating = 0;
00154 static int unoptimizing = 0;
00155 static int install_colormap = 0;
00156 static int interactive = 1;
00157 
00158 
00159 #define DISPLAY_OPT             300
00160 #define UNOPTIMIZE_OPT          301
00161 #define VERSION_OPT             302
00162 #define ANIMATE_OPT             303
00163 #define GEOMETRY_OPT            304
00164 #define NAME_OPT                305
00165 #define HELP_OPT                306
00166 #define WINDOW_OPT              307
00167 #define INSTALL_COLORMAP_OPT    308
00168 #define INTERACTIVE_OPT         309
00169 #define BACKGROUND_OPT          310
00170 
00171 #define WINDOW_TYPE             (Clp_MaxDefaultType + 1)
00172 
00173 Clp_Option options[] = {
00174   { "animate", 'a', ANIMATE_OPT, 0, Clp_Negate },
00175   { "background", 'b', BACKGROUND_OPT, Clp_ArgString, 0 },
00176   { "bg", 0, BACKGROUND_OPT, Clp_ArgString, 0 },
00177   { "display", 'd', DISPLAY_OPT, Clp_ArgStringNotOption, 0 },
00178   { "geometry", 'g', GEOMETRY_OPT, Clp_ArgString, 0 },
00179   { "install-colormap", 'i', INSTALL_COLORMAP_OPT, 0, Clp_Negate },
00180   { "interactive", 'e', INTERACTIVE_OPT, 0, Clp_Negate },
00181   { "help", 0, HELP_OPT, 0, 0 },
00182   { "name", 0, NAME_OPT, Clp_ArgString, 0 },
00183   { "unoptimize", 'U', UNOPTIMIZE_OPT, 0, Clp_Negate },
00184   { "version", 0, VERSION_OPT, 0, 0 },
00185   { "window", 'w', WINDOW_OPT, WINDOW_TYPE, 0 },
00186 };
00187 
00188 
00189 /*****
00190  * Diagnostics
00191  **/
00192 
00193 void
00194 fatal_error(char *message, ...)
00195 {
00196   va_list val;
00197   va_start(val, message);
00198   fprintf(stderr, "%s: ", program_name);
00199   vfprintf(stderr, message, val);
00200   fputc('\n', stderr);
00201   exit(1);
00202 }
00203 
00204 void
00205 error(char *message, ...)
00206 {
00207   va_list val;
00208   va_start(val, message);
00209   fprintf(stderr, "%s: ", program_name);
00210   vfprintf(stderr, message, val);
00211   fputc('\n', stderr);
00212 }
00213 
00214 void
00215 warning(char *message, ...)
00216 {
00217   va_list val;
00218   va_start(val, message);
00219   fprintf(stderr, "%s: warning: ", program_name);
00220   vfprintf(stderr, message, val);
00221   fputc('\n', stderr);
00222 }
00223 
00224 void
00225 short_usage(void)
00226 {
00227   fprintf(stderr, "Usage: %s [--display DISPLAY] [OPTION]... [FILE | FRAME]...\n\
00228 Try `%s --help' for more information.\n",
00229           program_name, program_name);
00230 }
00231 
00232 void
00233 usage(void)
00234 {
00235   printf("\
00236 `Gifview' is a lightweight GIF viewer for X. It can display animated GIFs as\n\
00237 slideshows, one frame at a time, or as animations.\n\
00238 \n\
00239 Usage: %s [--display DISPLAY] [OPTION]... [FILE | FRAME]...\n\
00240 \n\
00241 Options are:\n\
00242   -a, --animate                 Animate multiframe GIFs.\n\
00243   -U, --unoptimize              Unoptimize displayed GIFs.\n\
00244   -d, --display DISPLAY         Set display to DISPLAY.\n\
00245       --name NAME               Set application resource name to NAME.\n\
00246   -g, --geometry GEOMETRY       Set window geometry.\n\
00247   -w, --window WINDOW           Show GIF in existing WINDOW.\n\
00248   -i, --install-colormap        Use a private colormap.\n\
00249   --bg, --background COLOR      Use COLOR for transparent pixels.\n\
00250   +e, --no-interactive          Ignore buttons and keystrokes.\n\
00251       --help                    Print this message and exit.\n\
00252       --version                 Print version number and exit.\n\
00253 \n\
00254 Frame selections:               #num, #num1-num2, #num1-, #name\n\
00255 \n\
00256 Keystrokes:\n\
00257   [Space] Go to next frame.             [B] Go to previous frame.\n\
00258   [R]/[<] Go to first frame.            [>] Go to last frame.\n\
00259   [ESC] Stop animation.                 [S]/[A] Toggle animation.\n\
00260   [U] Toggle unoptimization.            [Backspace]/[W] Delete window.\n\
00261   [Q] Quit.\n\
00262 \n\
00263 Left mouse button goes to next frame, right mouse button deletes window.\n\
00264 \n\
00265 Report bugs to <eddietwo@lcs.mit.edu>.\n", program_name);
00266 }
00267 
00268 
00269 /*****
00270  * Window creation
00271  **/
00272 
00273 #if defined(__cplusplus) || defined(c_plusplus)
00274 #define VISUAL_CLASS c_class
00275 #else
00276 #define VISUAL_CLASS class
00277 #endif
00278 
00279 static void
00280 choose_visual(Gt_Viewer *viewer)
00281 {
00282   Display *display = viewer->display;
00283   int screen_number = viewer->screen_number;
00284   VisualID default_visualid = DefaultVisual(display, screen_number)->visualid;
00285   
00286   XVisualInfo visi_template;
00287   int nv, i;
00288   XVisualInfo *v, *best_v = 0;
00289   Gt_Viewer *trav;
00290   
00291   /* Look for an existing Gt_Viewer with the same display and screen number */
00292   if (!install_colormap)
00293     for (trav = viewers; trav; trav = trav->next)
00294       if (trav != viewer && trav->display == display
00295           && trav->screen_number == screen_number) {
00296         viewer->visual = trav->visual;
00297         viewer->depth = trav->depth;
00298         viewer->colormap = trav->colormap;
00299         viewer->gfx = trav->gfx;
00300         viewer->gfx->refcount++;
00301         return;
00302       }
00303   
00304   /* Find the default visual's XVisualInfo & put it in best_v */
00305   visi_template.screen = screen_number;
00306   v = XGetVisualInfo(display, VisualScreenMask, &visi_template, &nv);
00307   for (i = 0; i < nv && !best_v; i++)
00308     if (v[i].visualid == default_visualid)
00309       best_v = &v[i];
00310   
00311   if (!best_v) {
00312     
00313     /* This should never happen. If we can't find the default visual's
00314        XVisualInfo, we just use the default visual */
00315     viewer->visual = DefaultVisual(display, screen_number);
00316     viewer->depth = DefaultDepth(display, screen_number);
00317     viewer->colormap = DefaultColormap(display, screen_number);
00318     
00319   } else {
00320     
00321     /* Which visual to choose? This isn't exactly a simple decision, since
00322        we want to avoid colormap flashing while choosing a nice visual. So
00323        here's the algorithm: Prefer the default visual, or take a TrueColor
00324        visual with strictly greater depth. */
00325     for (i = 0; i < nv; i++)
00326       if (v[i].depth > best_v->depth && v[i].VISUAL_CLASS == TrueColor)
00327         best_v = &v[i];
00328     
00329     viewer->visual = best_v->visual;
00330     viewer->depth = best_v->depth;
00331     if (best_v->visualid != default_visualid
00332         || (best_v->VISUAL_CLASS == PseudoColor && install_colormap))
00333       viewer->colormap =
00334         XCreateColormap(display, RootWindow(display, screen_number),
00335                         viewer->visual, AllocNone);
00336     else
00337       viewer->colormap = DefaultColormap(display, screen_number);
00338     
00339   }
00340   
00341   viewer->gfx = Gif_NewXContextFromVisual
00342     (display, screen_number, viewer->visual, viewer->depth, viewer->colormap);
00343   viewer->gfx->refcount++;
00344   
00345   if (v) XFree(v);
00346 }
00347 
00348 
00349 Gt_Viewer *
00350 new_viewer(Display *display, Window use_window, Gif_Stream *gfs, char *name)
00351 {
00352   Gt_Viewer *viewer;
00353   int i;
00354   
00355   /* Make the Gt_Viewer structure */
00356   viewer = Gif_New(Gt_Viewer);
00357   viewer->display = display;
00358   
00359   if (use_window) {
00360     XWindowAttributes attr;
00361 
00362     if (use_window == -1) {     /* means use root window */
00363       viewer->screen_number = DefaultScreen(display);
00364       use_window = RootWindow(display, viewer->screen_number);
00365     }
00366     
00367     XGetWindowAttributes(display, use_window, &attr);
00368     
00369     viewer->screen_number = -1;
00370     for (i = 0; i < ScreenCount(display); i++)
00371       if (ScreenOfDisplay(display, i) == attr.screen)
00372         viewer->screen_number = i;
00373     assert(viewer->screen_number >= 0);
00374     
00375     viewer->visual = attr.visual;
00376     viewer->depth = attr.depth;
00377     viewer->colormap = attr.colormap;
00378     
00379     viewer->gfx = Gif_NewXContextFromVisual
00380       (display, viewer->screen_number, viewer->visual, viewer->depth,
00381        viewer->colormap);
00382     viewer->gfx->refcount++;
00383 
00384     /* use root window, if that's what we were given; otherwise, create a
00385        child of the window we were given */
00386     viewer->window = (use_window == attr.root ? use_window : None);
00387     viewer->parent = use_window;
00388     viewer->use_window = (use_window == attr.root ? 1 : 0);
00389     viewer->top_level = 0;
00390     viewer->resizable = 0;
00391     
00392   } else {
00393     viewer->screen_number = DefaultScreen(display);
00394     choose_visual(viewer);
00395     viewer->window = None;
00396     viewer->parent = RootWindow(display, viewer->screen_number);
00397     viewer->use_window = 0;
00398     viewer->top_level = 1;
00399     viewer->resizable = 1;
00400   }
00401   
00402   /* assign background color */
00403   if (cur_background_color) {
00404     XColor color;
00405     if (!XParseColor(viewer->display, viewer->colormap, cur_background_color,
00406                      &color)) {
00407       error("invalid background color `%s'", cur_background_color);
00408       cur_background_color = 0;
00409     } else if (!XAllocColor(viewer->display, viewer->colormap, &color))
00410       warning("can't allocate background color");
00411     else {
00412       unsigned long pixel = color.pixel;
00413       Gif_XContext *gfx = viewer->gfx;
00414       if (pixel != gfx->transparent_pixel && gfx->refcount > 1) {
00415         /* copy X context */
00416         viewer->gfx = Gif_NewXContextFromVisual
00417           (gfx->display, gfx->screen_number, gfx->visual, gfx->depth,
00418            gfx->colormap);
00419         viewer->gfx->refcount++;
00420         gfx->refcount--;
00421       }
00422       viewer->gfx->transparent_pixel = pixel;
00423     }
00424   }
00425 
00426   if (!cur_arrow_cursor) {
00427     cur_arrow_cursor = XCreateFontCursor(display, XC_left_ptr);
00428     cur_wait_cursor = XCreateFontCursor(display, XC_watch);
00429   }
00430   viewer->arrow_cursor = cur_arrow_cursor;
00431   viewer->wait_cursor = cur_wait_cursor;
00432   
00433   viewer->being_deleted = 0;
00434   viewer->gfs = gfs;
00435   gfs->refcount++;
00436   viewer->name = name;
00437   viewer->nim = Gif_ImageCount(gfs);
00438   viewer->im = Gif_NewArray(Gif_Image *, viewer->nim);
00439   for (i = 0; i < viewer->nim; i++)
00440     viewer->im[i] = gfs->images[i];
00441   viewer->pixmap = None;
00442   viewer->im_pos = -1;
00443   viewer->was_unoptimized = 0;
00444   viewer->free_pixmaps = 1;
00445   viewer->unoptimized_pixmaps = Gif_NewArray(Pixmap, viewer->nim);
00446   for (i = 0; i < viewer->nim; i++)
00447     viewer->unoptimized_pixmaps[i] = None;
00448   viewer->next = viewers;
00449   viewers = viewer;
00450   viewer->animating = 0;
00451   viewer->unoptimizing = unoptimizing;
00452   viewer->scheduled = 0;
00453   viewer->anim_next = 0;
00454   viewer->anim_loop = 0;
00455   viewer->timer.tv_sec = viewer->timer.tv_usec = 0;
00456   
00457   return viewer;
00458 }
00459 
00460 
00461 void
00462 delete_viewer(Gt_Viewer *viewer)
00463 {
00464   Gt_Viewer *prev = 0, *trav;
00465   int i;
00466   if (viewer->pixmap && !viewer->was_unoptimized)
00467     XFreePixmap(viewer->display, viewer->pixmap);
00468   for (i = 0; i < viewer->nim; i++)
00469     if (viewer->unoptimized_pixmaps[i])
00470       XFreePixmap(viewer->display, viewer->unoptimized_pixmaps[i]);
00471   
00472   for (trav = viewers; trav != viewer; prev = trav, trav = trav->next)
00473     ;
00474   if (prev) prev->next = viewer->next;
00475   else viewers = viewer->next;
00476   
00477   Gif_DeleteStream(viewer->gfs);
00478   Gif_DeleteArray(viewer->unoptimized_pixmaps);
00479   Gif_DeleteArray(viewer->im);
00480   Gif_DeleteXContext(viewer->gfx);
00481   Gif_Delete(viewer);
00482 }
00483 
00484 
00485 static Gt_Viewer *
00486 next_viewer(Gif_Stream *gfs, char *name)
00487 {
00488   Gt_Viewer *viewer = new_viewer(cur_display, cur_use_window, gfs, name);
00489   if (cur_use_window)
00490     cur_use_window = None;
00491   return viewer;
00492 }
00493 
00494 static Gt_Viewer *
00495 get_input_stream(char *name)
00496 {
00497   FILE *f;
00498   Gif_Stream *gfs = 0;
00499   
00500   if (name == 0 || strcmp(name, "-") == 0) {
00501     f = stdin;
00502     name = "<stdin>";
00503   } else
00504     f = fopen(name, "rb");
00505   if (!f) {
00506     error("%s: %s", name, strerror(errno));
00507     return 0;
00508   }
00509   
00510   gfs = Gif_FullReadFile(f, GIF_READ_COMPRESSED, 0, 0);
00511   fclose(f);
00512   
00513   if (!gfs || Gif_ImageCount(gfs) == 0) {
00514     error("`%s' doesn't seem to contain a GIF", name);
00515     Gif_DeleteStream(gfs);
00516     return 0;
00517   }
00518   
00519   if (!cur_display) {
00520     cur_display = XOpenDisplay(cur_display_name);
00521     if (!cur_display) {
00522       error("can't open display");
00523       return 0;
00524     }
00525   }
00526 
00527   return next_viewer(gfs, name);
00528 }
00529 
00530 
00531 /*****
00532  * Schedule stuff
00533  **/
00534 
00535 void
00536 switch_animating(Gt_Viewer *viewer, int animating)
00537 {
00538   int i;
00539   Gif_Stream *gfs = viewer->gfs;
00540   if (animating == viewer->animating || !viewer->can_animate)
00541     return;
00542   for (i = 0; i < gfs->nimages; i++)
00543     viewer->im[i] = gfs->images[i];
00544   viewer->animating = animating;
00545   if (!animating)
00546     viewer->timer.tv_sec = viewer->timer.tv_usec = 0;
00547 }
00548 
00549 
00550 void
00551 unschedule(Gt_Viewer *viewer)
00552 {
00553   Gt_Viewer *prev, *trav;
00554   if (!viewer->scheduled) return;
00555   for (prev = 0, trav = animations; trav; prev = trav, trav = trav->anim_next)
00556     if (trav == viewer)
00557       break;
00558   if (trav) {
00559     if (prev) prev->anim_next = viewer->anim_next;
00560     else animations = viewer->anim_next;
00561   }
00562   viewer->scheduled = 0;
00563   viewer->timer.tv_sec = viewer->timer.tv_usec = 0;
00564 }
00565 
00566 
00567 void
00568 schedule_next_frame(Gt_Viewer *viewer)
00569 {
00570   struct timeval interval;
00571   Gt_Viewer *prev, *trav;
00572   int delay = viewer->im[viewer->im_pos]->delay;
00573 
00574   if (viewer->timer.tv_sec == 0 && viewer->timer.tv_usec == 0)
00575     xwGETTIME(viewer->timer);
00576   interval.tv_sec = delay / 100;
00577   interval.tv_usec = (delay % 100) * (MICRO_PER_SEC / 100);
00578   xwADDTIME(viewer->timer, viewer->timer, interval);
00579 
00580   if (viewer->scheduled)
00581     unschedule(viewer);
00582   
00583   prev = 0;
00584   for (trav = animations; trav; prev = trav, trav = trav->anim_next)
00585     if (xwTIMEGEQ(trav->timer, viewer->timer))
00586       break;
00587   if (prev) {
00588     viewer->anim_next = trav;
00589     prev->anim_next = viewer;
00590   } else {
00591     viewer->anim_next = animations;
00592     animations = viewer;
00593   }
00594   viewer->scheduled = 1;
00595 }
00596 
00597 
00598 /*****
00599  * X stuff
00600  **/
00601 
00602 int
00603 parse_geometry(const char *const_g, XSizeHints *sh, int screen_width,
00604                int screen_height)
00605 {
00606   char *g = (char *)const_g;
00607   sh->flags = 0;
00608   
00609   if (isdigit(*g)) {
00610     sh->flags |= USSize;
00611     sh->width = strtol(g, &g, 10);
00612     if (g[0] == 'x' && isdigit(g[1]))
00613       sh->height = strtol(g + 1, &g, 10);
00614     else
00615       goto error;
00616   } else if (!*g)
00617     goto error;
00618   
00619   if (*g == '+' || *g == '-') {
00620     int x_minus, y_minus;
00621     sh->flags |= USPosition | PWinGravity;
00622     x_minus = *g == '-';
00623     sh->x = strtol(g + 1, &g, 10);
00624     if (x_minus) sh->x = screen_width - sh->x - sh->width;
00625     
00626     y_minus = *g == '-';
00627     if (*g == '-' || *g == '+')
00628       sh->y = strtol(g + 1, &g, 10);
00629     else
00630       goto error;
00631     if (y_minus) sh->y = screen_height - sh->y - sh->height;
00632     
00633     if (x_minus)
00634       sh->win_gravity = y_minus ? SouthEastGravity : NorthEastGravity;
00635     else
00636       sh->win_gravity = y_minus ? SouthWestGravity : NorthWestGravity;
00637     
00638   } else if (*g)
00639     goto error;
00640   
00641   return 1;
00642   
00643  error:
00644   warning("bad geometry specification");
00645   sh->flags = 0;
00646   return 0;
00647 }
00648 
00649 
00650 static Atom wm_delete_window_atom;
00651 static Atom wm_protocols_atom;
00652 
00653 void
00654 create_viewer_window(Gt_Viewer *viewer, int w, int h)
00655 {
00656   Display *display = viewer->display;
00657   char *stringlist[2];
00658   XTextProperty window_name, icon_name;
00659   XClassHint classh;
00660   XSizeHints *sizeh = XAllocSizeHints(); /* sets all fields to 0 */
00661 
00662   /* Set the window's geometry */
00663   sizeh->width = w;
00664   sizeh->height = h;
00665   if (cur_geometry_spec) {
00666     int scr_width = DisplayWidth(viewer->display, viewer->screen_number);
00667     int scr_height = DisplayHeight(viewer->display, viewer->screen_number);
00668     parse_geometry(cur_geometry_spec, sizeh, scr_width, scr_height);
00669   }
00670   
00671   /* Open the display and create the window */
00672   if (!viewer->window) {
00673     XSetWindowAttributes x_set_attr;
00674     unsigned long x_set_attr_mask;
00675     x_set_attr.colormap = viewer->colormap;
00676     x_set_attr.backing_store = NotUseful;
00677     x_set_attr.save_under = False;
00678     x_set_attr.border_pixel = 0;
00679     x_set_attr.background_pixel = 0;
00680     x_set_attr_mask = CWColormap | CWBorderPixel | CWBackPixel
00681       | CWBackingStore | CWSaveUnder;
00682     
00683     viewer->window = XCreateWindow
00684       (display, viewer->parent,
00685        sizeh->x, sizeh->y, sizeh->width, sizeh->height, 0,
00686        viewer->depth, InputOutput, viewer->visual,
00687        x_set_attr_mask, &x_set_attr);
00688     XDefineCursor(display, viewer->window, viewer->arrow_cursor);
00689   }
00690 
00691   /* If user gave us geometry, don't change the size later */
00692   if (sizeh->flags & USSize)
00693     viewer->resizable = 0;
00694   viewer->width = w;
00695   viewer->height = h;
00696   
00697   /* Set the window's title and class (for window manager resources) */
00698   if (viewer->top_level) {
00699     stringlist[0] = "gifview";
00700     stringlist[1] = 0;
00701     XStringListToTextProperty(stringlist, 1, &window_name);
00702     XStringListToTextProperty(stringlist, 1, &icon_name);
00703     classh.res_name = (char *)cur_resource_name;
00704     classh.res_class = "Gifview";
00705     XSetWMProperties(display, viewer->window, &window_name, &icon_name,
00706                      NULL, 0, sizeh, NULL, &classh);
00707     XFree(window_name.value);
00708     XFree(icon_name.value);
00709   
00710     if (!wm_delete_window_atom) {
00711       wm_delete_window_atom = XInternAtom(display, "WM_DELETE_WINDOW", False);
00712       wm_protocols_atom = XInternAtom(display, "WM_PROTOCOLS", False);
00713     }
00714     XSetWMProtocols(display, viewer->window, &wm_delete_window_atom, 1);
00715   }
00716 
00717   if (interactive)
00718     XSelectInput(display, viewer->window, ButtonPressMask | KeyPressMask
00719                  | StructureNotifyMask);
00720   else
00721     XSelectInput(display, viewer->window, StructureNotifyMask);
00722   
00723   XFree(sizeh);
00724 }
00725 
00726 
00727 void
00728 pre_delete_viewer(Gt_Viewer *viewer)
00729 {
00730   if (viewer->being_deleted) return;
00731   viewer->being_deleted = 1;
00732   
00733   if (viewer->scheduled) unschedule(viewer);
00734   
00735   if (viewer->window && !viewer->use_window)
00736     XDestroyWindow(viewer->display, viewer->window);
00737   else
00738     delete_viewer(viewer);
00739 }
00740 
00741 
00742 Gt_Viewer *
00743 find_viewer(Display *display, Window window)
00744 {
00745   Gt_Viewer *v;
00746   for (v = viewers; v; v = v->next)
00747     if (v->display == display && v->window == window)
00748       return v;
00749   return 0;
00750 }
00751 
00752 
00753 void
00754 set_viewer_name(Gt_Viewer *viewer, int slow_number)
00755 {
00756   Gif_Image *gfi;
00757   char *strs[2];
00758   char *identifier;
00759   XTextProperty name_prop;
00760   int im_pos = (slow_number >= 0 ? slow_number : viewer->im_pos);
00761   int len;
00762   
00763   if (!viewer->top_level || im_pos >= viewer->nim || viewer->being_deleted)
00764     return;
00765 
00766   gfi = viewer->im[im_pos];
00767   len = strlen(viewer->name) + 21;
00768   identifier = (slow_number >= 0 ? (char *)0 : gfi->identifier);
00769   if (identifier)
00770     len += 2 + strlen(identifier);
00771   
00772   strs[0] = Gif_NewArray(char, len);
00773   if (slow_number >= 0)
00774     sprintf(strs[0], "gifview: %s [#%d]", viewer->name, im_pos);
00775   else if (viewer->nim == 1 && identifier)
00776     sprintf(strs[0], "gifview: %s #%s", viewer->name, identifier);
00777   else if (viewer->animating || viewer->nim == 1)
00778     sprintf(strs[0], "gifview: %s", viewer->name);
00779   else if (!identifier)
00780     sprintf(strs[0], "gifview: %s #%d", viewer->name, im_pos);
00781   else
00782     sprintf(strs[0], "gifview: %s #%d #%s", viewer->name, im_pos, identifier);
00783   strs[1] = 0;
00784   
00785   XStringListToTextProperty(strs, 1, &name_prop);
00786   XSetWMName(viewer->display, viewer->window, &name_prop);
00787   XSetWMIconName(viewer->display, viewer->window, &name_prop);
00788   
00789   XFree(name_prop.value);
00790   Gif_DeleteArray(strs[0]);
00791 }
00792 
00793 static Pixmap
00794 unoptimized_frame(Gt_Viewer *viewer, int frame, int slow)
00795 {
00796   /* create a new unoptimized frame; maybe kill some of the old ones */
00797   if (!viewer->unoptimized_pixmaps[frame]) {
00798     Gif_Stream *gfs = viewer->gfs;
00799     Pixmap last = None, last_last = None;
00800     if (frame >= 2 && gfs->images[frame-1]->disposal == GIF_DISPOSAL_PREVIOUS)
00801       last_last = unoptimized_frame(viewer, frame - 2, slow);
00802     if (frame >= 1)
00803       last = unoptimized_frame(viewer, frame - 1, slow);
00804     
00805     viewer->unoptimized_pixmaps[frame] =
00806       Gif_XNextImage(viewer->gfx, last_last, last, gfs, frame);
00807 
00808     if (viewer->free_pixmaps && frame > 1 && frame % SAVE_FRAMES == 1) {
00809       int kill = frame - 1 - SAVE_FRAMES;
00810       assert(kill >= 0);
00811       for (; kill < frame - 1; kill++)
00812         if (viewer->unoptimized_pixmaps[kill] && kill % 50 != 0) {
00813           XFreePixmap(viewer->display, viewer->unoptimized_pixmaps[kill]);
00814           viewer->unoptimized_pixmaps[kill] = None;
00815         }
00816     }
00817 
00818     if (slow) {
00819       set_viewer_name(viewer, frame);
00820       XFlush(viewer->display);
00821     }
00822   }
00823   
00824   return viewer->unoptimized_pixmaps[frame];
00825 }
00826 
00827 static void
00828 kill_unoptimized_frames(Gt_Viewer *viewer, int new_frame)
00829 {
00830   int nimages = viewer->nim;
00831   int kill = nimages;
00832   if (!viewer->free_pixmaps)
00833     return;
00834   
00835   if (new_frame == 0)
00836     kill = 3;
00837   else if (new_frame < viewer->im_pos)
00838     kill = new_frame + 1;
00839   
00840   for (; kill < nimages; kill++)
00841     if (viewer->unoptimized_pixmaps[kill] && kill % 50 != 0) {
00842       XFreePixmap(viewer->display, viewer->unoptimized_pixmaps[kill]);
00843       viewer->unoptimized_pixmaps[kill] = None;
00844     }
00845 }
00846 
00847 void
00848 view_frame(Gt_Viewer *viewer, int frame)
00849 {
00850   Display *display = viewer->display;
00851   Window window = viewer->window;
00852   Pixmap old_pixmap = viewer->pixmap;
00853   int need_set_name = 0;
00854   
00855   if (viewer->being_deleted)
00856     return;
00857   
00858   if (frame < 0)
00859     frame = 0;
00860   if (frame > viewer->nim - 1 && viewer->animating) {
00861     int loopcount = viewer->gfs->loopcount;
00862     if (loopcount == 0 || loopcount > viewer->anim_loop) {
00863       viewer->anim_loop++;
00864       frame = 0;
00865     } else {
00866       switch_animating(viewer, 0);
00867       need_set_name = 1;
00868     }
00869   }
00870   if (frame > viewer->nim - 1)
00871     frame = viewer->nim - 1;
00872   
00873   if (frame != viewer->im_pos) {
00874     Gif_Image *gfi = viewer->im[frame];
00875     int width, height, changed_cursor = 0;
00876 
00877     /* Change cursor if we need to wait. */
00878     if ((viewer->animating || viewer->unoptimizing)
00879         && !viewer->unoptimized_pixmaps[frame]) {
00880       if (frame > viewer->im_pos + 10 || frame < viewer->im_pos) {
00881         changed_cursor = 1;
00882         XDefineCursor(display, window, viewer->wait_cursor);
00883         XFlush(display);
00884       }
00885     }
00886     
00887     /* 5/26/98 Do some noodling around to try and use memory most effectively.
00888        If animating, keep the uncompressed frame; otherwise, throw it away. */
00889     if (viewer->animating || viewer->unoptimizing)
00890       viewer->pixmap = unoptimized_frame(viewer, frame, changed_cursor);
00891     else
00892       viewer->pixmap = Gif_XImage(viewer->gfx, viewer->gfs, gfi);
00893     
00894     /* put the image on the window */
00895     if (viewer->animating || viewer->unoptimizing)
00896       width = viewer->gfs->screen_width, height = viewer->gfs->screen_height;
00897     else
00898       width = Gif_ImageWidth(gfi), height = Gif_ImageHeight(gfi);
00899     if (!window) {
00900       create_viewer_window(viewer, width, height);
00901       window = viewer->window;
00902     }
00903     XSetWindowBackgroundPixmap(display, window, viewer->pixmap);
00904     if (old_pixmap || viewer->use_window) /* clear if using existing window */
00905       XClearWindow(display, window);
00906     /* Only change size after changing pixmap. */
00907     if ((viewer->width != width || viewer->height != height)
00908         && viewer->resizable) {
00909       XWindowChanges winch;
00910       winch.width = viewer->width = width;
00911       winch.height = viewer->height = height;
00912       XReconfigureWMWindow
00913         (display, window, viewer->screen_number,
00914          CWWidth | CWHeight, &winch);
00915     }
00916 
00917     /* Restore cursor */
00918     if (changed_cursor)
00919       XDefineCursor(display, window, viewer->arrow_cursor);
00920     
00921     /* Get rid of old pixmaps */
00922     if (viewer->was_unoptimized)
00923       kill_unoptimized_frames(viewer, frame);
00924     else if (old_pixmap)
00925       XFreePixmap(display, old_pixmap);
00926     viewer->was_unoptimized = viewer->animating || viewer->unoptimizing;
00927     
00928     /* Do we need a new name? */
00929     if ((!viewer->animating && Gif_ImageCount(viewer->gfs) > 1)
00930         || old_pixmap == None)
00931       need_set_name = 1;
00932     
00933     viewer->im_pos = frame;
00934   }
00935   
00936   if (need_set_name)
00937     set_viewer_name(viewer, -1);
00938   
00939   if (!old_pixmap && !viewer->use_window)
00940     /* first image; map the window */
00941     XMapRaised(display, window);
00942   else if (viewer->animating)
00943     /* only schedule next frame if image is already mapped */
00944     schedule_next_frame(viewer);
00945 }
00946 
00947 
00948 /*****
00949  * Command line arguments: marking frames, being done with streams
00950  **/
00951 
00952 int
00953 frame_argument(Gt_Viewer *viewer, char *arg)
00954 {
00955   char *c = arg;
00956   int n1 = 0;
00957   
00958   /* Get a number range (#x, #x-y, #x-, or #-y). First, read x. */
00959   if (isdigit(c[0]))
00960     n1 = strtol(arg, &c, 10);
00961   
00962   /* It really was a number range only if c is now at the end of the
00963      argument. */
00964   if (c[0] != 0) {
00965     Gif_Image *gfi = Gif_GetNamedImage(viewer->gfs, c);
00966     if (!gfi)
00967       error("no frame named `%s'", c);
00968     else
00969       n1 = Gif_ImageNumber(viewer->gfs, gfi);
00970   } else {
00971     if (n1 < 0 || n1 >= viewer->nim) {
00972       error("no frame number %d", n1);
00973       n1 = 0;
00974     }
00975   }
00976   
00977   return n1;
00978 }
00979 
00980 
00981 void
00982 input_stream_done(Gt_Viewer *viewer, int first_frame)
00983 {
00984   viewer->can_animate = Gif_ImageCount(viewer->gfs) > 1;
00985   switch_animating(viewer, animating && viewer->can_animate);
00986   if (first_frame < 0)
00987     first_frame = 0;
00988   view_frame(viewer, first_frame);
00989 }
00990 
00991 
00992 void
00993 key_press(Gt_Viewer *viewer, XKeyEvent *e)
00994 {
00995   char buf[32];
00996   KeySym key;
00997   int nbuf = XLookupString(e, buf, 32, &key, 0);
00998   if (nbuf > 1) buf[0] = 0;     /* ignore multikey sequences */
00999   
01000   if (key == XK_space || key == XK_F || key == XK_f)
01001     /* space or F: one frame ahead */
01002     view_frame(viewer, viewer->im_pos + 1);
01003   
01004   else if (key == XK_B || key == XK_b)
01005     /* B: one frame back */
01006     view_frame(viewer, viewer->im_pos - 1);
01007   
01008   else if (key == XK_W || key == XK_w || key == XK_BackSpace)
01009     /* backspace: delete viewer */
01010     pre_delete_viewer(viewer);
01011   
01012   else if (key == XK_Q || key == XK_q)
01013     /* Q: quit applicaton */
01014     exit(0);
01015   
01016   else if (key == XK_S || key == XK_s || key == XK_a || key == XK_A) {
01017     /* S or A: toggle animation */
01018     switch_animating(viewer, !viewer->animating);
01019     
01020     if (viewer->animating) {
01021       int pos = viewer->im_pos;
01022       if (viewer->im_pos >= viewer->nim - 1) {
01023         pos = 0;
01024         viewer->anim_loop = 0;
01025       }
01026       view_frame(viewer, pos);
01027       
01028     } else
01029       unschedule(viewer);
01030     
01031     set_viewer_name(viewer, -1);
01032     
01033   } else if (key == XK_U || key == XK_u) {
01034     /* U: toggle unoptimizing */
01035     int pos = viewer->im_pos;
01036     viewer->unoptimizing = !viewer->unoptimizing;
01037     if (!viewer->animating) {
01038       viewer->im_pos = -1;
01039       view_frame(viewer, pos);
01040       set_viewer_name(viewer, -1);
01041     }
01042     
01043   } else if (key == XK_R || key == XK_r
01044              || (nbuf == 1 && buf[0] == '<')) {
01045     /* R or <: reset to first frame */
01046     unschedule(viewer);
01047     viewer->anim_loop = 0;
01048     view_frame(viewer, 0);
01049     
01050   } else if (nbuf == 1 && buf[0] == '>') {
01051     /* >: reset to last frame */
01052     unschedule(viewer);
01053     viewer->anim_loop = 0;
01054     view_frame(viewer, viewer->nim - 1);
01055     
01056   } else if (key == XK_Escape && viewer->animating) {
01057     /* Escape: stop animation */
01058     switch_animating(viewer, 0);
01059     unschedule(viewer);
01060     set_viewer_name(viewer, -1);
01061     
01062   } else if (key == XK_Z || key == XK_z) {
01063     /* Z: trigger resizability */
01064     viewer->resizable = !viewer->resizable;
01065   }
01066 }
01067 
01068 
01069 void
01070 loop(void)
01071 {
01072   struct timeval now;
01073   fd_set xfds;
01074   XEvent e;
01075   int pending;
01076   Gt_Viewer *v;
01077   Display *display = viewers->display;
01078   int x_socket = ConnectionNumber(display);
01079   
01080   xwGETTIME(now);
01081   FD_ZERO(&xfds);
01082   
01083   while (viewers) {
01084     
01085     /* Check for any animations */
01086     /* 13.Feb.2001 - Use the 'pending' counter to avoid a tight loop
01087        if all the frames in an animation have delay 0. Reported by
01088        Franc,ois Petitjean. */
01089     pending = 0;
01090     while (animations && xwTIMEGEQ(now, animations->timer) && pending < 25) {
01091       v = animations;
01092       animations = v->anim_next;
01093       v->scheduled = 0;
01094       view_frame(v, v->im_pos + 1);
01095       xwGETTIME(now);
01096       pending++;
01097     }
01098     
01099     pending = XPending(display);
01100     if (!pending) {
01101       /* select() until event arrives */
01102       struct timeval timeout, *timeout_ptr;
01103       int retval;
01104 
01105       if (animations) {
01106         xwSUBTIME(timeout, animations->timer, now);
01107         timeout_ptr = &timeout;
01108       } else
01109         timeout_ptr = 0;
01110     
01111       FD_SET(x_socket, &xfds);
01112       retval = select(x_socket + 1, &xfds, 0, 0, timeout_ptr);
01113       pending = (retval <= 0 ? 0 : FD_ISSET(x_socket, &xfds));
01114     }
01115     
01116     if (pending)
01117       while (XPending(display)) {
01118         XNextEvent(display, &e);
01119         v = find_viewer(e.xany.display, e.xany.window);
01120         if (v) {
01121           if (interactive) {
01122             if (e.type == ButtonPress && e.xbutton.button == 1)
01123               /* Left mouse button: go to next frame */
01124               view_frame(v, v->im_pos + 1);
01125             
01126             else if (e.type == ButtonPress && e.xbutton.button == 3)
01127               /* Right mouse button: delete window */
01128               pre_delete_viewer(v);
01129             
01130             else if (e.type == KeyPress)
01131               /* Key press: call function */
01132               key_press(v, &e.xkey);
01133           }
01134           
01135           if (e.type == ClientMessage
01136               && e.xclient.message_type == wm_protocols_atom
01137               && (Atom)(e.xclient.data.l[0]) == wm_delete_window_atom)
01138             /* WM_DELETE_WINDOW message: delete window */
01139             pre_delete_viewer(v);
01140           
01141           else if (e.type == MapNotify && v->animating
01142                    && v->scheduled == 0)
01143             /* Window was just mapped; now, start animating it */
01144             schedule_next_frame(v);
01145           
01146           else if (e.type == DestroyNotify)
01147             /* Once the window has been destroyed, delete related state */
01148             delete_viewer(v);
01149         }
01150       }
01151     
01152     xwGETTIME(now);
01153   }
01154 }
01155 
01156 
01157 int
01158 main(int argc, char **argv)
01159 {
01160   Gt_Viewer *viewer = 0;
01161   int viewer_given = 0;
01162   int any_errors = 0;
01163   int first_frame = -1;
01164   
01165   Clp_Parser *clp =
01166     Clp_NewParser(argc, argv,
01167                   sizeof(options) / sizeof(options[0]), options);
01168   Clp_SetOptionChar(clp, '+', Clp_ShortNegated);
01169   Clp_AddStringListType
01170     (clp, WINDOW_TYPE, Clp_AllowNumbers,
01171      "root", -1,
01172      0);
01173   program_name = cur_resource_name = Clp_ProgramName(clp);
01174   
01175   xwGETTIMEOFDAY(&genesis_time);
01176   
01177   while (1) {
01178     int opt = Clp_Next(clp);
01179     switch (opt) {
01180       
01181      case DISPLAY_OPT:
01182       if (cur_display)
01183         fatal_error("`--display' must come before all other options");
01184       cur_display_name = clp->arg;
01185       cur_display = 0;
01186       cur_arrow_cursor = cur_wait_cursor = None;
01187       break;
01188       
01189      case GEOMETRY_OPT:
01190       cur_geometry_spec = clp->arg;
01191       break;
01192       
01193      case NAME_OPT:
01194       cur_resource_name = clp->arg;
01195       break;
01196       
01197      case UNOPTIMIZE_OPT:
01198       unoptimizing = clp->negated ? 0 : 1;
01199       break;
01200       
01201      case BACKGROUND_OPT:
01202       cur_background_color = clp->arg;
01203       break;
01204       
01205      case ANIMATE_OPT:
01206       animating = clp->negated ? 0 : 1;
01207       break;
01208       
01209      case INSTALL_COLORMAP_OPT:
01210       install_colormap = clp->negated ? 0 : 1;
01211       break;
01212       
01213      case WINDOW_OPT:
01214       cur_use_window = clp->val.u;
01215       break;
01216       
01217      case INTERACTIVE_OPT:
01218       interactive = clp->negated ? 0 : 1;
01219       break;
01220       
01221      case VERSION_OPT:
01222       printf("gifview (LCDF Gifsicle) %s\n", VERSION);
01223       printf("Copyright (C) 1997-2001 Eddie Kohler\n\
01224 This is free software; see the source for copying conditions.\n\
01225 There is NO warranty, not even for merchantability or fitness for a\n\
01226 particular purpose.\n");
01227       exit(0);
01228       break;
01229       
01230      case HELP_OPT:
01231       usage();
01232       exit(0);
01233       break;
01234       
01235      case Clp_NotOption:
01236       if (clp->arg[0] == '#') {
01237         if (!viewer_given) {
01238           viewer = get_input_stream(0);
01239           viewer_given = 1;
01240         }
01241         if (viewer && first_frame >= 0) {
01242           /* copy viewer if 2 frame specs given */
01243           input_stream_done(viewer, first_frame);
01244           viewer = next_viewer(viewer->gfs, viewer->name);
01245         }
01246         if (viewer)
01247           first_frame = frame_argument(viewer, clp->arg + 1);
01248       } else {
01249         if (viewer) input_stream_done(viewer, first_frame);
01250         first_frame = -1;
01251         viewer = get_input_stream(clp->arg);
01252         viewer_given = 1;
01253       }
01254       break;
01255       
01256      case Clp_Done:
01257       goto done;
01258       
01259      case Clp_BadOption:
01260       any_errors = 1;
01261       break;
01262       
01263      default:
01264       break;
01265       
01266     }
01267   }
01268   
01269  done:
01270   
01271   if (!viewer_given) {
01272     if (any_errors) {
01273       short_usage();
01274       exit(1);
01275     }
01276     viewer = get_input_stream(0);
01277   }
01278   if (viewer) input_stream_done(viewer, first_frame);
01279   
01280   if (viewers) loop();
01281   
01282 #ifdef DMALLOC
01283   dmalloc_report();
01284 #endif
01285   return 0;
01286 }
 

Powered by Plone

This site conforms to the following standards: