gifsicle  1.92
About: Gifsicle is a UNIX command-line tool for creating, editing, and getting information about GIF images and animations.
  Fossies Dox: gifsicle-1.92.tar.gz  ("inofficial" and yet experimental doxygen-generated source code documentation)  

gifview.c
Go to the documentation of this file.
1 /* gifview.c - gifview's main loop.
2  Copyright (C) 1997-2019 Eddie Kohler, ekohler@gmail.com
3  This file is part of gifview, in the gifsicle package.
4 
5  Gifview is free software. It is distributed under the GNU Public License,
6  version 2; you can copy, distribute, or alter it at will, as long
7  as this notice is kept intact and this source code is made available. There
8  is no warranty, express or implied. */
9 
10 #include <config.h>
11 #ifdef X_DISPLAY_MISSING
12 #error "You can't compile gifview without X."
13 #endif
14 
15 #include <lcdfgif/gifx.h>
16 #include <lcdf/clp.h>
17 #include <X11/Xlib.h>
18 #include <X11/Xutil.h>
19 #include <X11/Xos.h>
20 #include <X11/keysym.h>
21 #include <X11/cursorfont.h>
22 #if HAVE_SYS_SELECT_H
23 # include <sys/select.h>
24 #endif
25 #include <string.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <stdarg.h>
29 #include <ctype.h>
30 #include <errno.h>
31 #include <assert.h>
32 #if HAVE_UNISTD_H
33 # include <unistd.h>
34 #endif
35 
36 #ifdef __cplusplus
37 #define EXTERN extern "C"
38 #else
39 #define EXTERN extern
40 #endif
41 
42 /*****
43  * TIME STUFF (from xwrits)
44  **/
45 
46 #define MICRO_PER_SEC 1000000
47 
48 #define xwADDTIME(result, a, b) do { \
49  (result).tv_sec = (a).tv_sec + (b).tv_sec; \
50  if (((result).tv_usec = (a).tv_usec + (b).tv_usec) >= MICRO_PER_SEC) { \
51  (result).tv_sec++; \
52  (result).tv_usec -= MICRO_PER_SEC; \
53  } } while (0)
54 
55 #define xwSUBTIME(result, a, b) do { \
56  (result).tv_sec = (a).tv_sec - (b).tv_sec; \
57  if (((result).tv_usec = (a).tv_usec - (b).tv_usec) < 0) { \
58  (result).tv_sec--; \
59  (result).tv_usec += MICRO_PER_SEC; \
60  } } while (0)
61 
62 #define xwSETMINTIME(a, b) do { \
63  if ((b).tv_sec < (a).tv_sec || \
64  ((b).tv_sec == (a).tv_sec && (b).tv_usec < (a).tv_usec)) \
65  (a) = (b); \
66  } while (0)
67 
68 #define xwTIMEGEQ(a, b) ((a).tv_sec > (b).tv_sec || \
69  ((a).tv_sec == (b).tv_sec && (a).tv_usec >= (b).tv_usec))
70 
71 #define xwTIMEGT(a, b) ((a).tv_sec > (b).tv_sec || \
72  ((a).tv_sec == (b).tv_sec && (a).tv_usec > (b).tv_usec))
73 
74 #define xwTIMELEQ0(a) ((a).tv_sec < 0 || ((a).tv_sec == 0 && (a).tv_usec <= 0))
75 
76 #ifdef X_GETTIMEOFDAY
77 # define xwGETTIMEOFDAY(a) X_GETTIMEOFDAY(a)
78 #elif GETTIMEOFDAY_PROTO == 0
79 EXTERN int gettimeofday(struct timeval *, struct timezone *);
80 # define xwGETTIMEOFDAY(a) gettimeofday((a), 0)
81 #elif GETTIMEOFDAY_PROTO == 1
82 # define xwGETTIMEOFDAY(a) gettimeofday((a))
83 #else
84 # define xwGETTIMEOFDAY(a) gettimeofday((a), 0)
85 #endif
86 
87 #define xwGETTIME(a) do { xwGETTIMEOFDAY(&(a)); xwSUBTIME((a), (a), genesis_time); } while (0)
88 struct timeval genesis_time;
89 
90 
91 /*****
92  * THE VIEWER STRUCTURE
93  **/
94 
95 static unsigned pixel_memory_limit_kb = 40000;
96 static unsigned pixel_memory_kb;
97 
98 typedef struct Gt_Viewer {
99 
100  Display *display;
102  Visual *visual;
103  int depth;
104  Colormap colormap;
106  Cursor arrow_cursor;
107  Cursor wait_cursor;
108 
109  Window parent;
111 
112  Window window;
114  int width;
115  int height;
116  int resizable;
118 
120  const char *name;
121  const char *title;
122 
123  Gif_Image **im;
124  int nim;
125 
126  Pixmap pixmap;
127  int im_pos;
129 
132 
133  struct Gt_Viewer *next;
134 
141  struct timeval timer;
142  int anim_loop;
143 
145 
146 const char *program_name = "gifview";
147 static Clp_Parser* clp;
148 
149 static const char *cur_display_name = 0;
150 static Display *cur_display = 0;
151 static const char *cur_geometry_spec = 0;
152 static Cursor cur_arrow_cursor = 0;
153 static Cursor cur_wait_cursor = 0;
154 static const char *cur_resource_name;
155 static const char *cur_window_title = 0;
156 static Window cur_use_window = None;
157 static int cur_use_window_new = 0;
158 static const char *cur_background_color = "black";
159 
162 static int animating = 0;
163 static int unoptimizing = 0;
164 static int install_colormap = 0;
165 static int interactive = 1;
166 static int min_delay = 0;
167 static int fallback_delay = 0;
168 
169 static struct timeval preparation_time;
170 
171 
172 #define DISPLAY_OPT 300
173 #define UNOPTIMIZE_OPT 301
174 #define VERSION_OPT 302
175 #define ANIMATE_OPT 303
176 #define GEOMETRY_OPT 304
177 #define NAME_OPT 305
178 #define HELP_OPT 306
179 #define WINDOW_OPT 307
180 #define INSTALL_COLORMAP_OPT 308
181 #define INTERACTIVE_OPT 309
182 #define BACKGROUND_OPT 310
183 #define NEW_WINDOW_OPT 311
184 #define TITLE_OPT 312
185 #define MIN_DELAY_OPT 313
186 #define FALLBACK_DELAY_OPT 314
187 #define MEMORY_LIMIT_OPT 315
188 
189 #define WINDOW_TYPE (Clp_ValFirstUser)
190 
191 const Clp_Option options[] = {
192  { "animate", 'a', ANIMATE_OPT, 0, Clp_Negate },
193  { "background", 'b', BACKGROUND_OPT, Clp_ValString, 0 },
194  { "bg", 't', BACKGROUND_OPT, Clp_ValString, 0 },
195  { "display", 'd', DISPLAY_OPT, Clp_ValStringNotOption, 0 },
196  { "geometry", 'g', GEOMETRY_OPT, Clp_ValString, 0 },
197  { "install-colormap", 'i', INSTALL_COLORMAP_OPT, 0, Clp_Negate },
198  { "interactive", 'e', INTERACTIVE_OPT, 0, Clp_Negate },
199  { "help", 0, HELP_OPT, 0, 0 },
200  { "memory-limit", 0, MEMORY_LIMIT_OPT, Clp_ValUnsigned, Clp_Negate },
201  { "min-delay", 0, MIN_DELAY_OPT, Clp_ValInt, Clp_Negate },
202  { "fallback-delay", 0, FALLBACK_DELAY_OPT, Clp_ValInt, Clp_Negate },
203  { "name", 0, NAME_OPT, Clp_ValString, 0 },
204  { "title", 'T', TITLE_OPT, Clp_ValString, 0 },
205  { "unoptimize", 'U', UNOPTIMIZE_OPT, 0, Clp_Negate },
206  { "version", 0, VERSION_OPT, 0, 0 },
207  { "window", 'w', WINDOW_OPT, WINDOW_TYPE, 0 },
208  { "new-window", 0, NEW_WINDOW_OPT, WINDOW_TYPE, 0 }
209 };
210 
211 
212 /*****
213  * Diagnostics
214  **/
215 
216 void fatal_error(const char* format, ...) {
217  char buf[BUFSIZ];
218  int n = snprintf(buf, BUFSIZ, "%s: ", program_name);
219  va_list val;
220  va_start(val, format);
221  Clp_vsnprintf(clp, buf + n, BUFSIZ - n, format, val);
222  va_end(val);
223  fputs(buf, stderr);
224  exit(1);
225 }
226 
227 void error(const char* format, ...) {
228  char buf[BUFSIZ];
229  int n = snprintf(buf, BUFSIZ, "%s: ", program_name);
230  va_list val;
231  va_start(val, format);
232  Clp_vsnprintf(clp, buf + n, BUFSIZ - n, format, val);
233  va_end(val);
234  fputs(buf, stderr);
235 }
236 
237 void warning(const char* format, ...) {
238  char buf[BUFSIZ];
239  int n = snprintf(buf, BUFSIZ, "%s: warning: ", program_name);
240  va_list val;
241  va_start(val, format);
242  Clp_vsnprintf(clp, buf + n, BUFSIZ - n, format, val);
243  va_end(val);
244  fputs(buf, stderr);
245 }
246 
247 void short_usage(void) {
248  Clp_fprintf(clp, stderr, "\
249 Usage: %s [--display DISPLAY] [OPTION]... [FILE | FRAME]...\n\
250 Try %<%s --help%> for more information.\n",
252 }
253 
254 void usage(void) {
255  Clp_fprintf(clp, stdout, "\
256 %<Gifview%> is a lightweight GIF viewer for X. It can display animated GIFs as\n\
257 slideshows, one frame at a time, or as animations.\n\
258 \n\
259 Usage: %s [--display DISPLAY] [OPTION]... [FILE | FRAME]...\n\n", program_name);
260  Clp_fprintf(clp, stdout, "\
261 Options are:\n\
262  -a, --animate Animate multiframe GIFs.\n\
263  -U, --unoptimize Unoptimize displayed GIFs.\n\
264  -d, --display DISPLAY Set display to DISPLAY.\n\
265  --name NAME Set application resource name to NAME.\n\
266  -g, --geometry GEOMETRY Set window geometry.\n\
267  -T, --title TITLE Set window title.\n");
268  Clp_fprintf(clp, stdout, "\
269  -w, --window WINDOW Show GIF in existing WINDOW.\n\
270  --new-window WINDOW Show GIF in new child of existing WINDOW.\n\
271  -i, --install-colormap Use a private colormap.\n\
272  --bg, --background COLOR Use COLOR for transparent pixels.\n\
273  --min-delay DELAY Set minimum frame delay to DELAY/100 sec.\n\
274  --fallback-delay DELAY Set fallback frame delay to DELAY/100 sec.\n\
275  +e, --no-interactive Ignore buttons and keystrokes.\n");
276  Clp_fprintf(clp, stdout, "\
277  --memory-limit LIM Cache at most LIM megabytes of animation.\n\
278  --help Print this message and exit.\n\
279  --version Print version number and exit.\n\
280 \n\
281 Frame selections: #num, #num1-num2, #num1-, #name\n\n");
282  Clp_fprintf(clp, stdout, "\
283 Keystrokes:\n\
284  [N]/[Space] Go to next frame. [P]/[B] Go to previous frame.\n\
285  [R]/[<] Go to first frame. [>] Go to last frame.\n\
286  [ESC] Stop animation. [S]/[A] Toggle animation.\n\
287  [U] Toggle unoptimization. [Backspace]/[W] Delete window.\n\
288  [Q] Quit.\n\
289 \n\
290 Left mouse button goes to next frame, right mouse button deletes window.\n\
291 \n\
292 Report bugs to <ekohler@gmail.com>.\n");
293 }
294 
295 
296 /*****
297  * Window creation
298  **/
299 
300 #if defined(__cplusplus) || defined(c_plusplus)
301 #define VISUAL_CLASS c_class
302 #else
303 #define VISUAL_CLASS class
304 #endif
305 
306 static void
307 choose_visual(Gt_Viewer *viewer)
308 {
309  Display *display = viewer->display;
310  int screen_number = viewer->screen_number;
311  VisualID default_visualid = DefaultVisual(display, screen_number)->visualid;
312 
313  XVisualInfo visi_template;
314  int nv, i;
315  XVisualInfo *v, *best_v = 0;
316  Gt_Viewer *trav;
317 
318  /* Look for an existing Gt_Viewer with the same display and screen number */
319  if (!install_colormap)
320  for (trav = viewers; trav; trav = trav->next)
321  if (trav != viewer && trav->display == display
322  && trav->screen_number == screen_number) {
323  viewer->visual = trav->visual;
324  viewer->depth = trav->depth;
325  viewer->colormap = trav->colormap;
326  viewer->gfx = trav->gfx;
327  viewer->gfx->refcount++;
328  return;
329  }
330 
331  /* Find the default visual's XVisualInfo & put it in best_v */
332  visi_template.screen = screen_number;
333  v = XGetVisualInfo(display, VisualScreenMask, &visi_template, &nv);
334  for (i = 0; i < nv && !best_v; i++)
335  if (v[i].visualid == default_visualid)
336  best_v = &v[i];
337 
338  if (!best_v) {
339 
340  /* This should never happen. If we can't find the default visual's
341  XVisualInfo, we just use the default visual */
342  viewer->visual = DefaultVisual(display, screen_number);
343  viewer->depth = DefaultDepth(display, screen_number);
344  viewer->colormap = DefaultColormap(display, screen_number);
345 
346  } else {
347 
348  /* Which visual to choose? This isn't exactly a simple decision, since
349  we want to avoid colormap flashing while choosing a nice visual. So
350  here's the algorithm: Prefer the default visual, or take a TrueColor
351  visual with strictly greater depth. */
352  for (i = 0; i < nv; i++)
353  if (v[i].depth > best_v->depth && v[i].VISUAL_CLASS == TrueColor)
354  best_v = &v[i];
355 
356  viewer->visual = best_v->visual;
357  viewer->depth = best_v->depth;
358  if (best_v->visualid != default_visualid
359  || (best_v->VISUAL_CLASS == PseudoColor && install_colormap))
360  viewer->colormap =
361  XCreateColormap(display, RootWindow(display, screen_number),
362  viewer->visual, AllocNone);
363  else
364  viewer->colormap = DefaultColormap(display, screen_number);
365 
366  }
367 
369  (display, screen_number, viewer->visual, viewer->depth, viewer->colormap);
370  viewer->gfx->refcount++;
371 
372  if (v) XFree(v);
373 }
374 
375 
376 Gt_Viewer *
377 new_viewer(Display *display, Gif_Stream *gfs, const char *name)
378 {
379  Gt_Viewer *viewer;
380  int i;
381 
382  /* Make the Gt_Viewer structure */
383  viewer = Gif_New(Gt_Viewer);
384  viewer->display = display;
385 
386  if (cur_use_window) {
387  XWindowAttributes attr;
388 
389  if (cur_use_window == (Window)(-1)) { /* means use root window */
390  viewer->screen_number = DefaultScreen(display);
391  cur_use_window = RootWindow(display, viewer->screen_number);
392  }
393 
394  XGetWindowAttributes(display, cur_use_window, &attr);
395 
396  viewer->screen_number = -1;
397  for (i = 0; i < ScreenCount(display); i++)
398  if (ScreenOfDisplay(display, i) == attr.screen)
399  viewer->screen_number = i;
400  assert(viewer->screen_number >= 0);
401 
402  viewer->visual = attr.visual;
403  viewer->depth = attr.depth;
404  viewer->colormap = attr.colormap;
405 
407  (display, viewer->screen_number, viewer->visual, viewer->depth,
408  viewer->colormap);
409  viewer->gfx->refcount++;
410 
411  /* Before -- use root window, if that's what we were given; otherwise,
412  create a child of the window we were given */
413  /* 13.Nov.2001 - don't make a child of the window we were given! */
414  if (cur_use_window_new) {
415  viewer->window = None;
416  viewer->use_window = 0;
417  } else {
418  viewer->window = cur_use_window;
419  viewer->use_window = 1;
420  }
421  viewer->parent = cur_use_window;
422  viewer->top_level = 0;
423  viewer->resizable = 0;
424 
425  } else {
426  viewer->screen_number = DefaultScreen(display);
427  choose_visual(viewer);
428  viewer->window = None;
429  viewer->parent = RootWindow(display, viewer->screen_number);
430  viewer->use_window = 0;
431  viewer->top_level = 1;
432  viewer->resizable = 1;
433  }
434 
435  /* assign background color */
436  if (cur_background_color) {
437  XColor color;
438  if (!XParseColor(viewer->display, viewer->colormap, cur_background_color,
439  &color)) {
440  error("invalid background color %<%s%>\n", cur_background_color);
442  } else if (!XAllocColor(viewer->display, viewer->colormap, &color))
443  warning("can%,t allocate background color\n");
444  else {
445  unsigned long pixel = color.pixel;
446  Gif_XContext *gfx = viewer->gfx;
447  if (pixel != gfx->transparent_pixel && gfx->refcount > 1) {
448  /* copy X context */
450  (gfx->display, gfx->screen_number, gfx->visual, gfx->depth,
451  gfx->colormap);
452  viewer->gfx->refcount++;
453  gfx->refcount--;
454  }
455  viewer->gfx->transparent_pixel = pixel;
456  }
457  }
458 
459  if (!cur_arrow_cursor) {
460  cur_arrow_cursor = XCreateFontCursor(display, XC_left_ptr);
461  cur_wait_cursor = XCreateFontCursor(display, XC_watch);
462  }
463  viewer->arrow_cursor = cur_arrow_cursor;
464  viewer->wait_cursor = cur_wait_cursor;
465 
466  viewer->being_deleted = 0;
467  viewer->gfs = gfs;
468  gfs->refcount++;
469  viewer->name = name;
470  viewer->title = cur_window_title;
471  viewer->nim = Gif_ImageCount(gfs);
472  viewer->im = Gif_NewArray(Gif_Image *, viewer->nim);
473  for (i = 0; i < viewer->nim; i++)
474  viewer->im[i] = gfs->images[i];
475  viewer->pixmap = None;
476  viewer->im_pos = -1;
477  viewer->was_unoptimized = 0;
478  viewer->unoptimized_frames = Gif_NewXFrames(gfs);
479  viewer->n_unoptimized_frames = 0;
480  viewer->next = viewers;
481  viewers = viewer;
482  viewer->animating = 0;
483  viewer->unoptimizing = unoptimizing;
484  viewer->scheduled = 0;
485  viewer->preparing = 0;
486  viewer->anim_next = 0;
487  viewer->anim_loop = 0;
488  viewer->timer.tv_sec = viewer->timer.tv_usec = 0;
489 
490  return viewer;
491 }
492 
493 
494 void
495 delete_viewer(Gt_Viewer *viewer)
496 {
497  Gt_Viewer *prev = 0, *trav;
498  if (viewer->pixmap && !viewer->was_unoptimized)
499  XFreePixmap(viewer->display, viewer->pixmap);
500 
501  for (trav = viewers; trav != viewer; prev = trav, trav = trav->next)
502  ;
503  if (prev) prev->next = viewer->next;
504  else viewers = viewer->next;
505 
506  Gif_DeleteXFrames(viewer->gfx, viewer->gfs, viewer->unoptimized_frames);
507  Gif_DeleteStream(viewer->gfs);
508  Gif_DeleteArray(viewer->im);
509  Gif_DeleteXContext(viewer->gfx);
510  Gif_Delete(viewer);
511 }
512 
513 
514 static Gt_Viewer *
515 next_viewer(Gif_Stream *gfs, const char *name)
516 {
517  Gt_Viewer *viewer = new_viewer(cur_display, gfs, name);
518  cur_use_window = None;
519  return viewer;
520 }
521 
522 static Gt_Viewer *
523 get_input_stream(const char *name)
524 {
525  FILE *f;
526  Gif_Stream *gfs = 0;
527 
528  if (name == 0 || strcmp(name, "-") == 0) {
529 #ifndef OUTPUT_GIF_TO_TERMINAL
530  if (isatty(fileno(stdin))) {
531  error("<stdin>: is a terminal\n");
532  return NULL;
533  }
534 #endif
535  f = stdin;
536 #if defined(_MSDOS) || defined(_WIN32)
537  _setmode(_fileno(stdin), _O_BINARY);
538 #elif defined(__DJGPP__)
539  setmode(fileno(stdin), O_BINARY);
540 #elif defined(__EMX__)
541  _fsetmode(stdin, "b");
542 #endif
543  name = "<stdin>";
544  } else
545  f = fopen(name, "rb");
546  if (!f) {
547  error("%s: %s\n", name, strerror(errno));
548  return 0;
549  }
550 
551  gfs = Gif_FullReadFile(f, GIF_READ_COMPRESSED, 0, 0);
552  fclose(f);
553 
554  if (!gfs || Gif_ImageCount(gfs) == 0) {
555  error("%s: file not in GIF format\n", name);
556  Gif_DeleteStream(gfs);
557  return 0;
558  }
559 
560  if (!cur_display) {
561  cur_display = XOpenDisplay(cur_display_name);
562  if (!cur_display) {
563  error("can%,t open display\n");
564  return 0;
565  }
566  }
567 
568  return next_viewer(gfs, name);
569 }
570 
571 
572 /*****
573  * Schedule stuff
574  **/
575 
576 void
578 {
579  int i;
580  Gif_Stream *gfs = viewer->gfs;
581  if (animating == viewer->animating || !viewer->can_animate)
582  return;
583  for (i = 0; i < gfs->nimages; i++)
584  viewer->im[i] = gfs->images[i];
585  viewer->animating = animating;
586  if (!animating)
587  viewer->timer.tv_sec = viewer->timer.tv_usec = 0;
588 }
589 
590 
591 void
592 unschedule(Gt_Viewer *viewer)
593 {
594  Gt_Viewer *prev, *trav;
595  if (!viewer->scheduled) return;
596  for (prev = 0, trav = animations; trav; prev = trav, trav = trav->anim_next)
597  if (trav == viewer)
598  break;
599  if (trav) {
600  if (prev) prev->anim_next = viewer->anim_next;
601  else animations = viewer->anim_next;
602  }
603  viewer->scheduled = 0;
604  viewer->timer.tv_sec = viewer->timer.tv_usec = 0;
605 }
606 
607 void
608 schedule(Gt_Viewer *viewer)
609 {
610  Gt_Viewer *prev, *trav;
611 
612  if (viewer->scheduled)
613  unschedule(viewer);
614 
615  prev = 0;
616  for (trav = animations; trav; prev = trav, trav = trav->anim_next)
617  if (xwTIMEGEQ(trav->timer, viewer->timer))
618  break;
619  if (prev) {
620  viewer->anim_next = trav;
621  prev->anim_next = viewer;
622  } else {
623  viewer->anim_next = animations;
624  animations = viewer;
625  }
626 
627  viewer->scheduled = 1;
628 }
629 
630 void
632 {
633  struct timeval interval;
634  int delay = viewer->im[viewer->im_pos]->delay;
635  int next_pos = viewer->im_pos + 1;
636  if (delay < 1)
637  delay = fallback_delay;
638  if (delay < min_delay)
639  delay = min_delay;
640  if (next_pos == viewer->nim)
641  next_pos = 0;
642 
643  if (viewer->timer.tv_sec == 0 && viewer->timer.tv_usec == 0)
644  xwGETTIME(viewer->timer);
645 
646  interval.tv_sec = delay / 100;
647  interval.tv_usec = (delay % 100) * (MICRO_PER_SEC / 100);
648  if (delay == 0)
649  interval.tv_usec = 2000;
650  xwADDTIME(viewer->timer, viewer->timer, interval);
651 
652  /* 1.Aug.2002 - leave some time to prepare the frame if necessary */
653  if (viewer->unoptimized_frames[next_pos].pixmap) {
654  xwSUBTIME(viewer->timer, viewer->timer, preparation_time);
655  viewer->preparing = 1;
656  }
657 
658  schedule(viewer);
659 }
660 
661 
662 /*****
663  * X stuff
664  **/
665 
666 int
667 parse_geometry(const char *const_g, XSizeHints *sh, int screen_width,
668  int screen_height)
669 {
670  char *g = (char *)const_g;
671  sh->flags = 0;
672 
673  if (isdigit(*g)) {
674  sh->flags |= USSize;
675  sh->width = strtol(g, &g, 10);
676  if (g[0] == 'x' && isdigit(g[1]))
677  sh->height = strtol(g + 1, &g, 10);
678  else
679  goto error;
680  } else if (!*g)
681  goto error;
682 
683  if (*g == '+' || *g == '-') {
684  int x_minus, y_minus;
685  sh->flags |= USPosition | PWinGravity;
686  x_minus = *g == '-';
687  sh->x = strtol(g + 1, &g, 10);
688  if (x_minus) sh->x = screen_width - sh->x - sh->width;
689 
690  y_minus = *g == '-';
691  if (*g == '-' || *g == '+')
692  sh->y = strtol(g + 1, &g, 10);
693  else
694  goto error;
695  if (y_minus) sh->y = screen_height - sh->y - sh->height;
696 
697  if (x_minus)
698  sh->win_gravity = y_minus ? SouthEastGravity : NorthEastGravity;
699  else
700  sh->win_gravity = y_minus ? SouthWestGravity : NorthWestGravity;
701 
702  } else if (*g)
703  goto error;
704 
705  return 1;
706 
707  error:
708  warning("bad geometry specification\n");
709  sh->flags = 0;
710  return 0;
711 }
712 
713 
714 static Atom wm_delete_window_atom;
715 static Atom wm_protocols_atom;
716 
717 void
718 create_viewer_window(Gt_Viewer *viewer, int w, int h)
719 {
720  Display *display = viewer->display;
721  char *stringlist[2];
722  XTextProperty window_name, icon_name;
723  XClassHint classh;
724  XSizeHints *sizeh = XAllocSizeHints(); /* sets all fields to 0 */
725 
726  /* Set the window's geometry */
727  sizeh->width = w ? w : 1;
728  sizeh->height = h ? h : 1;
729  if (cur_geometry_spec) {
730  int scr_width = DisplayWidth(viewer->display, viewer->screen_number);
731  int scr_height = DisplayHeight(viewer->display, viewer->screen_number);
732  parse_geometry(cur_geometry_spec, sizeh, scr_width, scr_height);
733  }
734 
735  /* Open the display and create the window */
736  if (!viewer->window) {
737  XSetWindowAttributes x_set_attr;
738  unsigned long x_set_attr_mask;
739  x_set_attr.colormap = viewer->colormap;
740  x_set_attr.backing_store = NotUseful;
741  x_set_attr.save_under = False;
742  x_set_attr.border_pixel = 0;
743  x_set_attr.background_pixel = 0;
744  x_set_attr_mask = CWColormap | CWBorderPixel | CWBackPixel
745  | CWBackingStore | CWSaveUnder;
746 
747  viewer->window = XCreateWindow
748  (display, viewer->parent,
749  sizeh->x, sizeh->y, sizeh->width, sizeh->height, 0,
750  viewer->depth, InputOutput, viewer->visual,
751  x_set_attr_mask, &x_set_attr);
752  XDefineCursor(display, viewer->window, viewer->arrow_cursor);
753  }
754 
755  /* If user gave us geometry, don't change the size later */
756  if (sizeh->flags & USSize)
757  viewer->resizable = 0;
758  viewer->width = w;
759  viewer->height = h;
760 
761  /* Set the window's title and class (for window manager resources) */
762  if (viewer->top_level) {
763  stringlist[0] = "gifview";
764  stringlist[1] = 0;
765  XStringListToTextProperty(stringlist, 1, &window_name);
766  XStringListToTextProperty(stringlist, 1, &icon_name);
767  classh.res_name = (char *)cur_resource_name;
768  classh.res_class = "Gifview";
769  XSetWMProperties(display, viewer->window, &window_name, &icon_name,
770  NULL, 0, sizeh, NULL, &classh);
771  XFree(window_name.value);
772  XFree(icon_name.value);
773 
774  if (!wm_delete_window_atom) {
775  wm_delete_window_atom = XInternAtom(display, "WM_DELETE_WINDOW", False);
776  wm_protocols_atom = XInternAtom(display, "WM_PROTOCOLS", False);
777  }
778  XSetWMProtocols(display, viewer->window, &wm_delete_window_atom, 1);
779  }
780 
781  if (interactive)
782  XSelectInput(display, viewer->window, ButtonPressMask | KeyPressMask
783  | StructureNotifyMask);
784  else
785  XSelectInput(display, viewer->window, StructureNotifyMask);
786 
787  XFree(sizeh);
788 }
789 
790 
791 void
793 {
794  if (viewer->being_deleted) return;
795  viewer->being_deleted = 1;
796 
797  if (viewer->scheduled) unschedule(viewer);
798 
799  if (viewer->window && !viewer->use_window)
800  XDestroyWindow(viewer->display, viewer->window);
801  else
802  delete_viewer(viewer);
803 }
804 
805 
806 Gt_Viewer *
807 find_viewer(Display *display, Window window)
808 {
809  Gt_Viewer *v;
810  for (v = viewers; v; v = v->next)
811  if (v->display == display && v->window == window)
812  return v;
813  return 0;
814 }
815 
816 
817 void
818 set_viewer_name(Gt_Viewer *viewer, int slow_number)
819 {
820  Gif_Image *gfi;
821  char *strs[2];
822  char *identifier;
823  XTextProperty name_prop;
824  int im_pos = (slow_number >= 0 ? slow_number : viewer->im_pos);
825  int len;
826 
827  if (!viewer->top_level || im_pos >= viewer->nim || viewer->being_deleted)
828  return;
829 
830  gfi = viewer->im[im_pos];
831  len = strlen(viewer->title) + strlen(viewer->name) + 14;
832  identifier = (slow_number >= 0 ? (char *)0 : gfi->identifier);
833  if (identifier)
834  len += 2 + strlen(identifier);
835 
836  strs[0] = Gif_NewArray(char, len);
837  if (strcmp(viewer->title, "gifview") != 0)
838  strcpy(strs[0], viewer->title);
839  else if (slow_number >= 0)
840  sprintf(strs[0], "gifview: %s [#%d]", viewer->name, im_pos);
841  else if (viewer->nim == 1 && identifier)
842  sprintf(strs[0], "gifview: %s #%s", viewer->name, identifier);
843  else if (viewer->animating || viewer->nim == 1)
844  sprintf(strs[0], "gifview: %s", viewer->name);
845  else if (!identifier)
846  sprintf(strs[0], "gifview: %s #%d", viewer->name, im_pos);
847  else
848  sprintf(strs[0], "gifview: %s #%d #%s", viewer->name, im_pos, identifier);
849  strs[1] = 0;
850 
851  XStringListToTextProperty(strs, 1, &name_prop);
852  XSetWMName(viewer->display, viewer->window, &name_prop);
853  XSetWMIconName(viewer->display, viewer->window, &name_prop);
854 
855  XFree(name_prop.value);
856  Gif_DeleteArray(strs[0]);
857 }
858 
859 static unsigned screen_memory_kb(const Gt_Viewer* viewer) {
860  return 1 + ((unsigned) (viewer->gfs->screen_width
861  * viewer->gfs->screen_height) / 334);
862 }
863 
864 static Pixmap
865 unoptimized_frame(Gt_Viewer *viewer, int frame, int slow)
866 {
867  /* create a new unoptimized frame if necessary */
868  if (!viewer->unoptimized_frames[frame].pixmap) {
869  (void) Gif_XNextImage(viewer->gfx, viewer->gfs, frame,
870  viewer->unoptimized_frames);
873  frame;
874  ++viewer->n_unoptimized_frames;
875  if (slow) {
876  set_viewer_name(viewer, frame);
877  XFlush(viewer->display);
878  }
879  }
880 
881  /* kill some old frames if over the memory limit */
882  while (pixel_memory_limit_kb != (unsigned) -1
884  && viewer->n_unoptimized_frames > 1) {
885  int killidx, killframe, i = 0;
886  do {
887  killidx = random() % viewer->n_unoptimized_frames;
888  killframe = viewer->unoptimized_frames[killidx].user_data;
889  ++i;
890  } while (killframe == frame
891  || (i < 10 && killframe > frame && killframe < frame + 5)
892  || (i < 10 && (killframe % 50) == 0));
893  XFreePixmap(viewer->display, viewer->unoptimized_frames[killframe].pixmap);
894  viewer->unoptimized_frames[killframe].pixmap = None;
895  --viewer->n_unoptimized_frames;
896  viewer->unoptimized_frames[killidx].user_data =
899  }
900 
901  return viewer->unoptimized_frames[frame].pixmap;
902 }
903 
904 void
905 prepare_frame(Gt_Viewer *viewer, int frame)
906 {
907  Display *display = viewer->display;
908  Window window = viewer->window;
909  int changed_cursor = 0;
910 
911  if (viewer->being_deleted || !viewer->animating)
912  return;
913 
914  if (frame < 0 || frame > viewer->nim - 1)
915  frame = 0;
916 
917  /* Change cursor if we need to wait. */
918  if ((viewer->animating || viewer->unoptimizing)
919  && !viewer->unoptimized_frames[frame].pixmap) {
920  if (frame > viewer->im_pos + 10 || frame < viewer->im_pos) {
921  changed_cursor = 1;
922  XDefineCursor(display, window, viewer->wait_cursor);
923  XFlush(display);
924  }
925  }
926 
927  /* Prepare the frame */
928  (void) unoptimized_frame(viewer, frame, changed_cursor && !viewer->animating);
929 
930  /* Restore cursor */
931  if (changed_cursor)
932  XDefineCursor(display, window, viewer->arrow_cursor);
933 
934  /* schedule actual view of window */
935  xwADDTIME(viewer->timer, viewer->timer, preparation_time);
936  viewer->preparing = 0;
937  schedule(viewer);
938 }
939 
940 void
941 view_frame(Gt_Viewer *viewer, int frame)
942 {
943  Display *display = viewer->display;
944  Window window = viewer->window;
945  Pixmap old_pixmap = viewer->pixmap;
946  int need_set_name = 0;
947 
948  if (viewer->being_deleted)
949  return;
950 
951  if (frame < 0)
952  frame = 0;
953  if (frame > viewer->nim - 1 && viewer->animating) {
954  int loopcount = viewer->gfs->loopcount;
955  if (loopcount == 0 || loopcount > viewer->anim_loop) {
956  viewer->anim_loop++;
957  frame = 0;
958  } else {
959  switch_animating(viewer, 0);
960  need_set_name = 1;
961  }
962  }
963  if (frame > viewer->nim - 1)
964  frame = viewer->nim - 1;
965 
966  if (frame != viewer->im_pos) {
967  Gif_Image *gfi = viewer->im[frame];
968  int width, height, changed_cursor = 0;
969 
970  /* Change cursor if we need to wait. */
971  if ((viewer->animating || viewer->unoptimizing)
972  && !viewer->unoptimized_frames[frame].pixmap) {
973  if (frame > viewer->im_pos + 10 || frame < viewer->im_pos) {
974  changed_cursor = 1;
975  XDefineCursor(display, window, viewer->wait_cursor);
976  XFlush(display);
977  }
978  }
979 
980  /* 5/26/98 Do some noodling around to try and use memory most effectively.
981  If animating, keep the uncompressed frame; otherwise, throw it away. */
982  if (viewer->animating || viewer->unoptimizing)
983  viewer->pixmap = unoptimized_frame(viewer, frame,
984  changed_cursor && !viewer->animating);
985  else
986  viewer->pixmap = Gif_XImage(viewer->gfx, viewer->gfs, gfi);
987 
988  /* put the image on the window */
989  if (viewer->animating || viewer->unoptimizing)
990  width = viewer->gfs->screen_width, height = viewer->gfs->screen_height;
991  else
992  width = Gif_ImageWidth(gfi), height = Gif_ImageHeight(gfi);
993  if (!window) {
994  create_viewer_window(viewer, width, height);
995  window = viewer->window;
996  }
997  XSetWindowBackgroundPixmap(display, window, viewer->pixmap);
998  if (old_pixmap || viewer->use_window) /* clear existing window */
999  XClearWindow(display, window);
1000  /* Only change size after changing pixmap. */
1001  if ((viewer->width != width || viewer->height != height)
1002  && viewer->resizable) {
1003  XWindowChanges winch;
1004  winch.width = viewer->width = width;
1005  winch.height = viewer->height = height;
1006  XReconfigureWMWindow
1007  (display, window, viewer->screen_number,
1008  CWWidth | CWHeight, &winch);
1009  }
1010 
1011  /* Get rid of old pixmaps */
1012  if (!viewer->was_unoptimized && old_pixmap)
1013  XFreePixmap(display, old_pixmap);
1014  viewer->was_unoptimized = viewer->animating || viewer->unoptimizing;
1015 
1016  /* Restore cursor */
1017  if (changed_cursor)
1018  XDefineCursor(display, window, viewer->arrow_cursor);
1019 
1020  /* Do we need a new name? */
1021  if ((!viewer->animating && Gif_ImageCount(viewer->gfs) > 1)
1022  || old_pixmap == None)
1023  need_set_name = 1;
1024  }
1025 
1026  viewer->im_pos = frame;
1027  viewer->preparing = 0;
1028 
1029  if (need_set_name)
1030  set_viewer_name(viewer, -1);
1031 
1032  if (!old_pixmap && !viewer->use_window)
1033  /* first image; map the window */
1034  XMapRaised(display, window);
1035  else if (viewer->animating)
1036  /* only schedule next frame if image is already mapped */
1037  schedule_next_frame(viewer);
1038 }
1040 
1041 /*****
1042  * Command line arguments: marking frames, being done with streams
1043  **/
1044 
1045 int
1046 frame_argument(Gt_Viewer *viewer, const char *arg)
1047 {
1048  const char *c = arg;
1049  int n1 = 0;
1050 
1051  /* Get a number range (#x, #x-y, #x-, or #-y). First, read x. */
1052  if (isdigit(c[0]))
1053  n1 = strtol(arg, (char **)&c, 10);
1054 
1055  /* It really was a number range only if c is now at the end of the
1056  argument. */
1057  if (c[0] != 0) {
1058  Gif_Image *gfi = Gif_GetNamedImage(viewer->gfs, c);
1059  if (!gfi)
1060  error("no frame named %<%s%>\n", c);
1061  else
1062  n1 = Gif_ImageNumber(viewer->gfs, gfi);
1063  } else {
1064  if (n1 < 0 || n1 >= viewer->nim) {
1065  error("no frame number %d\n", n1);
1066  n1 = 0;
1067  }
1068  }
1069 
1070  return n1;
1071 }
1072 
1073 
1074 void
1075 input_stream_done(Gt_Viewer *viewer, int first_frame)
1076 {
1077  viewer->can_animate = Gif_ImageCount(viewer->gfs) > 1;
1078  switch_animating(viewer, animating && viewer->can_animate);
1079  if (first_frame < 0)
1080  first_frame = 0;
1081  view_frame(viewer, first_frame);
1082 }
1083 
1084 
1085 void
1086 key_press(Gt_Viewer *viewer, XKeyEvent *e)
1087 {
1088  char buf[32];
1089  KeySym key;
1090  int nbuf = XLookupString(e, buf, 32, &key, 0);
1091  if (nbuf > 1) buf[0] = 0; /* ignore multikey sequences */
1092 
1093  if (key == XK_space || key == XK_F || key == XK_f || key == XK_N || key == XK_n)
1094  /* space, N or F: one frame ahead */
1095  view_frame(viewer, viewer->im_pos + 1);
1096 
1097  else if (key == XK_B || key == XK_b || key == XK_P || key == XK_p)
1098  /* B or P: one frame back */
1099  view_frame(viewer, viewer->im_pos - 1);
1100 
1101  else if (key == XK_W || key == XK_w || key == XK_BackSpace)
1102  /* backspace: delete viewer */
1103  pre_delete_viewer(viewer);
1104 
1105  else if (key == XK_Q || key == XK_q)
1106  /* Q: quit applicaton */
1107  exit(0);
1108 
1109  else if (key == XK_S || key == XK_s || key == XK_a || key == XK_A) {
1110  /* S or A: toggle animation */
1111  switch_animating(viewer, !viewer->animating);
1112 
1113  if (viewer->animating) {
1114  int pos = viewer->im_pos;
1115  if (viewer->im_pos >= viewer->nim - 1) {
1116  pos = 0;
1117  viewer->anim_loop = 0;
1118  }
1119  view_frame(viewer, pos);
1120 
1121  } else
1122  unschedule(viewer);
1123 
1124  set_viewer_name(viewer, -1);
1125 
1126  } else if (key == XK_U || key == XK_u) {
1127  /* U: toggle unoptimizing */
1128  int pos = viewer->im_pos;
1129  viewer->unoptimizing = !viewer->unoptimizing;
1130  if (!viewer->animating) {
1131  viewer->im_pos = -1;
1132  view_frame(viewer, pos);
1133  set_viewer_name(viewer, -1);
1134  }
1135 
1136  } else if (key == XK_R || key == XK_r
1137  || (nbuf == 1 && buf[0] == '<')) {
1138  /* R or <: reset to first frame */
1139  unschedule(viewer);
1140  viewer->anim_loop = 0;
1141  view_frame(viewer, 0);
1142 
1143  } else if (nbuf == 1 && buf[0] == '>') {
1144  /* >: reset to last frame */
1145  unschedule(viewer);
1146  viewer->anim_loop = 0;
1147  view_frame(viewer, viewer->nim - 1);
1148 
1149  } else if (key == XK_Escape && viewer->animating) {
1150  /* Escape: stop animation */
1151  switch_animating(viewer, 0);
1152  unschedule(viewer);
1153  set_viewer_name(viewer, -1);
1154 
1155  } else if (key == XK_Z || key == XK_z) {
1156  /* Z: trigger resizability */
1157  viewer->resizable = !viewer->resizable;
1158  }
1159 }
1160 
1161 
1162 void
1163 loop(void)
1164 {
1165  struct timeval now, stop_loop, stop_delta;
1166  fd_set xfds;
1167  XEvent e;
1168  int pending;
1169  Gt_Viewer *v;
1170  Display *display = viewers->display;
1171  int x_socket = ConnectionNumber(display);
1172  stop_delta.tv_sec = 0;
1173  stop_delta.tv_usec = 200000;
1174 
1175  xwGETTIME(now);
1176  FD_ZERO(&xfds);
1177 
1178  while (viewers) {
1179 
1180  /* Check for any animations */
1181  /* 13.Feb.2001 - Use the 'pending' counter to avoid a tight loop
1182  if all the frames in an animation have delay 0. Reported by
1183  Franc,ois Petitjean. */
1184  /* 1.Aug.2002 - Switch to running the loop for max 0.2s. */
1185  xwADDTIME(stop_loop, now, stop_delta);
1186  while (animations && xwTIMEGEQ(now, animations->timer)
1187  && xwTIMEGEQ(stop_loop, now)) {
1188  v = animations;
1189  animations = v->anim_next;
1190  v->scheduled = 0;
1191  if (v->preparing)
1192  prepare_frame(v, v->im_pos + 1);
1193  else {
1194  if (xwTIMEGEQ(now, v->timer))
1195  v->timer = now;
1196  view_frame(v, v->im_pos + 1);
1197  }
1198  xwGETTIME(now);
1199  }
1200 
1201  pending = XPending(display);
1202  if (!pending) {
1203  /* select() until event arrives */
1204  struct timeval timeout, *timeout_ptr;
1205  int retval;
1206 
1207  if (animations) {
1208  xwSUBTIME(timeout, animations->timer, now);
1209  timeout_ptr = &timeout;
1210  } else
1211  timeout_ptr = 0;
1212 
1213  FD_SET(x_socket, &xfds);
1214  retval = select(x_socket + 1, &xfds, 0, 0, timeout_ptr);
1215  pending = (retval <= 0 ? 0 : FD_ISSET(x_socket, &xfds));
1216  }
1217 
1218  if (pending)
1219  while (XPending(display)) {
1220  XNextEvent(display, &e);
1221  v = find_viewer(e.xany.display, e.xany.window);
1222  if (v) {
1223  if (interactive) {
1224  if (e.type == ButtonPress && e.xbutton.button == 1)
1225  /* Left mouse button: go to next frame */
1226  view_frame(v, v->im_pos + 1);
1227 
1228  else if (e.type == ButtonPress && e.xbutton.button == 4)
1229  /* mousewheel forward */
1230  view_frame(v, v->im_pos + 1);
1231 
1232  else if (e.type == ButtonPress && e.xbutton.button == 5)
1233  /* mousewheel backward */
1234  view_frame(v, v->im_pos - 1);
1235 
1236  else if (e.type == ButtonPress && e.xbutton.button == 3)
1237  /* Right mouse button: delete window */
1238  pre_delete_viewer(v);
1239 
1240  else if (e.type == KeyPress)
1241  /* Key press: call function */
1242  key_press(v, &e.xkey);
1243  }
1244 
1245  if (e.type == ClientMessage
1246  && e.xclient.message_type == wm_protocols_atom
1247  && (Atom)(e.xclient.data.l[0]) == wm_delete_window_atom)
1248  /* WM_DELETE_WINDOW message: delete window */
1249  pre_delete_viewer(v);
1250 
1251  else if (e.type == MapNotify && v->animating
1252  && v->scheduled == 0)
1253  /* Window was just mapped; now, start animating it */
1255 
1256  else if (e.type == DestroyNotify)
1257  /* Once the window has been destroyed, delete related state */
1258  delete_viewer(v);
1259  }
1260  }
1262  xwGETTIME(now);
1263  }
1264 }
1265 
1266 
1267 int
1268 main(int argc, char *argv[])
1269 {
1270  Gt_Viewer *viewer = 0;
1271  int viewer_given = 0;
1272  int any_errors = 0;
1273  int first_frame = -1;
1274 
1275  clp = Clp_NewParser(argc, (const char * const *)argv,
1276  sizeof(options) / sizeof(options[0]), options);
1280  "root", (long) -1,
1281  (const char*) 0);
1283 
1285  preparation_time.tv_sec = 0;
1286  preparation_time.tv_usec = 200000;
1287 
1288  while (1) {
1289  int opt = Clp_Next(clp);
1290  switch (opt) {
1291 
1292  case DISPLAY_OPT:
1293  if (cur_display)
1294  fatal_error("%<--display%> must come before all other options\n");
1296  cur_display = 0;
1298  break;
1299 
1300  case TITLE_OPT:
1302  break;
1303 
1304  case GEOMETRY_OPT:
1306  break;
1307 
1308  case NAME_OPT:
1310  break;
1311 
1312  case UNOPTIMIZE_OPT:
1313  unoptimizing = clp->negated ? 0 : 1;
1314  break;
1315 
1316  case BACKGROUND_OPT:
1318  break;
1319 
1320  case ANIMATE_OPT:
1321  animating = clp->negated ? 0 : 1;
1322  break;
1323 
1324  case INSTALL_COLORMAP_OPT:
1325  install_colormap = clp->negated ? 0 : 1;
1326  break;
1327 
1328  case WINDOW_OPT:
1329  cur_use_window = clp->val.ul;
1330  cur_use_window_new = 0;
1331  break;
1332 
1333  case NEW_WINDOW_OPT:
1334  cur_use_window = clp->val.ul;
1335  cur_use_window_new = 1;
1336  break;
1337 
1338  case INTERACTIVE_OPT:
1339  interactive = clp->negated ? 0 : 1;
1340  break;
1341 
1342  case MIN_DELAY_OPT:
1343  min_delay = clp->negated ? 0 : clp->val.i;
1344  break;
1345 
1346  case FALLBACK_DELAY_OPT:
1347  fallback_delay = clp->negated ? 0 : clp->val.i;
1348  break;
1349 
1350  case MEMORY_LIMIT_OPT:
1351  if (clp->negated || clp->val.u >= ((unsigned) -1 / 1000))
1352  pixel_memory_limit_kb = (unsigned) -1;
1353  else
1354  pixel_memory_limit_kb = clp->val.u * 1000;
1355  break;
1356 
1357  case VERSION_OPT:
1358  printf("gifview (LCDF Gifsicle) %s\n", VERSION);
1359  printf("Copyright (C) 1997-2019 Eddie Kohler\n\
1360 This is free software; see the source for copying conditions.\n\
1361 There is NO warranty, not even for merchantability or fitness for a\n\
1362 particular purpose.\n");
1363  exit(0);
1364  break;
1365 
1366  case HELP_OPT:
1367  usage();
1368  exit(0);
1369  break;
1370 
1371  case Clp_NotOption:
1372  if (clp->vstr[0] == '#') {
1373  if (!viewer_given) {
1374  viewer = get_input_stream(0);
1375  viewer_given = 1;
1376  }
1377  if (viewer && first_frame >= 0) {
1378  /* copy viewer if 2 frame specs given */
1379  input_stream_done(viewer, first_frame);
1380  viewer = next_viewer(viewer->gfs, viewer->name);
1381  }
1382  if (viewer)
1383  first_frame = frame_argument(viewer, clp->vstr + 1);
1384  } else {
1385  if (viewer) input_stream_done(viewer, first_frame);
1386  first_frame = -1;
1387  viewer = get_input_stream(clp->vstr);
1388  viewer_given = 1;
1389  }
1390  break;
1391 
1392  case Clp_Done:
1393  goto done;
1394 
1395  case Clp_BadOption:
1396  any_errors = 1;
1397  break;
1398 
1399  default:
1400  break;
1401 
1402  }
1403  }
1404 
1405  done:
1406 
1407  if (!viewer_given) {
1408  if (any_errors) {
1409  short_usage();
1410  exit(1);
1411  }
1412  viewer = get_input_stream(0);
1413  }
1414  if (viewer) input_stream_done(viewer, first_frame);
1415 
1416  if (viewers) loop();
1417 
1418  return 0;
1419 }
screen_memory_kb
static unsigned screen_memory_kb(const Gt_Viewer *viewer)
Definition: gifview.c:853
Gt_Viewer::nim
int nim
Definition: gifview.c:122
install_colormap
static int install_colormap
Definition: gifview.c:162
pixel_memory_limit_kb
static unsigned pixel_memory_limit_kb
Definition: gifview.c:93
NEW_WINDOW_OPT
#define NEW_WINDOW_OPT
Definition: gifview.c:181
Gt_Viewer::window
Window window
Definition: gifview.c:110
switch_animating
void switch_animating(Gt_Viewer *viewer, int animating)
Definition: gifview.c:572
MEMORY_LIMIT_OPT
#define MEMORY_LIMIT_OPT
Definition: gifview.c:185
Gt_Viewer::parent
Window parent
Definition: gifview.c:107
cur_window_title
static const char * cur_window_title
Definition: gifview.c:153
Gt_Viewer::unoptimized_frames
Gif_XFrame * unoptimized_frames
Definition: gifview.c:128
Gt_Viewer::unoptimizing
int unoptimizing
Definition: gifview.c:135
xwGETTIME
#define xwGETTIME(a)
Definition: gifview.c:86
DISPLAY_OPT
#define DISPLAY_OPT
Definition: gifview.c:170
Gif_XContext::screen_number
int screen_number
Definition: gifx.h:28
Gif_ImageHeight
#define Gif_ImageHeight(gfi)
Definition: gif.h:135
Gt_Viewer::gfs
Gif_Stream * gfs
Definition: gifview.c:117
unoptimized_frame
static Pixmap unoptimized_frame(Gt_Viewer *viewer, int frame, int slow)
Definition: gifview.c:859
Gt_Viewer::depth
int depth
Definition: gifview.c:101
cur_background_color
static const char * cur_background_color
Definition: gifview.c:156
Gt_Viewer::display
Display * display
Definition: gifview.c:98
unoptimizing
static int unoptimizing
Definition: gifview.c:161
Clp_fprintf
int Clp_fprintf(Clp_Parser *clp, FILE *f, const char *format,...)
Print a message.
Definition: clp.c:2285
cur_geometry_spec
static const char * cur_geometry_spec
Definition: gifview.c:149
Clp_StringListLong
#define Clp_StringListLong
String list flag: values have long type.
Definition: clp.h:334
xwSUBTIME
#define xwSUBTIME(result, a, b)
Definition: gifview.c:54
Gif_NewXContextFromVisual
Gif_XContext * Gif_NewXContextFromVisual(Display *display, int screen_number, Visual *visual, int depth, Colormap cmap)
Definition: gifx.c:790
Gif_DeleteXContext
void Gif_DeleteXContext(Gif_XContext *gfx)
Definition: gifx.c:834
Clp_Negate
#define Clp_Negate
Option flag: option may be negated.
Definition: clp.h:139
Gif_Stream::loopcount
long loopcount
Definition: gif.h:51
viewers
static Gt_Viewer * viewers
Definition: gifview.c:158
Gt_Viewer::next
struct Gt_Viewer * next
Definition: gifview.c:131
error
void error(const char *format,...)
Definition: gifview.c:224
xwTIMEGEQ
#define xwTIMEGEQ(a, b)
Definition: gifview.c:67
cur_arrow_cursor
static Cursor cur_arrow_cursor
Definition: gifview.c:150
Clp_ValStringNotOption
#define Clp_ValStringNotOption
Option value is a non-option string.
Definition: clp.h:62
gifx.h
Gif_XFrame
Definition: gifx.h:49
Gt_Viewer::timer
struct timeval timer
Definition: gifview.c:139
Gif_XNextImage
Pixmap Gif_XNextImage(Gif_XContext *gfx, Gif_Stream *gfs, int i, Gif_XFrame *frames)
Definition: gifx.c:672
short_usage
void short_usage(void)
Definition: gifview.c:244
Clp_NewParser
Clp_Parser * Clp_NewParser(int argc, const char *const *argv, int nopt, const Clp_Option *opt)
Create a new Clp_Parser.
Definition: clp.c:503
Clp_AddStringListType
int Clp_AddStringListType(Clp_Parser *clp, int val_type, int flags,...) CLP_SENTINEL
Define a new string list value type for clp.
Definition: clp.c:1285
min_delay
static int min_delay
Definition: gifview.c:164
Gif_ImageCount
#define Gif_ImageCount(gfs)
Definition: gif.h:71
usage
void usage(void)
Definition: gifview.c:251
Gif_ImageWidth
#define Gif_ImageWidth(gfi)
Definition: gif.h:134
Clp_ValUnsigned
#define Clp_ValUnsigned
Option value is an unsigned int.
Definition: clp.h:91
INSTALL_COLORMAP_OPT
#define INSTALL_COLORMAP_OPT
Definition: gifview.c:178
cur_wait_cursor
static Cursor cur_wait_cursor
Definition: gifview.c:151
Gif_XContext::display
Display * display
Definition: gifx.h:27
Gt_Viewer::use_window
int use_window
Definition: gifview.c:111
pixel_memory_kb
static unsigned pixel_memory_kb
Definition: gifview.c:94
Gt_Viewer::im
Gif_Image ** im
Definition: gifview.c:121
set_viewer_name
void set_viewer_name(Gt_Viewer *viewer, int slow_number)
Definition: gifview.c:812
cur_use_window_new
static int cur_use_window_new
Definition: gifview.c:155
Clp_Done
#define Clp_Done
Clp_Next value: there are no more arguments.
Definition: clp.h:194
Clp_ValInt
#define Clp_ValInt
Option value is a signed int.
Definition: clp.h:78
cur_display
static Display * cur_display
Definition: gifview.c:148
Gif_XContext::visual
Visual * visual
Definition: gifx.h:30
preparation_time
static struct timeval preparation_time
Definition: gifview.c:167
screen_height
static unsigned screen_height
Definition: gifdiff.c:46
view_frame
void view_frame(Gt_Viewer *viewer, int frame)
Definition: gifview.c:935
Clp_NotOption
#define Clp_NotOption
Clp_Next value: argument was not an option.
Definition: clp.h:191
xwADDTIME
#define xwADDTIME(result, a, b)
Definition: gifview.c:47
Gt_Viewer::anim_next
struct Gt_Viewer * anim_next
Definition: gifview.c:138
Gt_Viewer::resizable
int resizable
Definition: gifview.c:114
Gt_Viewer::arrow_cursor
Cursor arrow_cursor
Definition: gifview.c:104
Clp_Parser
Command line parser.
Definition: clp.h:234
gettimeofday
int gettimeofday(struct timeval *, struct timezone *)
WINDOW_OPT
#define WINDOW_OPT
Definition: gifview.c:177
Gt_Viewer::width
int width
Definition: gifview.c:112
FALLBACK_DELAY_OPT
#define FALLBACK_DELAY_OPT
Definition: gifview.c:184
Gt_Viewer::title
const char * title
Definition: gifview.c:119
new_viewer
Gt_Viewer * new_viewer(Display *display, Gif_Stream *gfs, const char *name)
Definition: gifview.c:373
genesis_time
struct timeval genesis_time
Definition: gifview.c:87
fallback_delay
static int fallback_delay
Definition: gifview.c:165
ANIMATE_OPT
#define ANIMATE_OPT
Definition: gifview.c:173
wm_protocols_atom
static Atom wm_protocols_atom
Definition: gifview.c:709
UNOPTIMIZE_OPT
#define UNOPTIMIZE_OPT
Definition: gifview.c:171
Gif_ImageNumber
int Gif_ImageNumber(Gif_Stream *gfs, Gif_Image *gfi)
Definition: giffunc.c:294
BACKGROUND_OPT
#define BACKGROUND_OPT
Definition: gifview.c:180
Gt_Viewer::anim_loop
int anim_loop
Definition: gifview.c:140
VERSION_OPT
#define VERSION_OPT
Definition: gifview.c:172
unschedule
void unschedule(Gt_Viewer *viewer)
Definition: gifview.c:587
strerror
char * strerror(int errno)
Definition: strerror.c:13
Gif_NewXFrames
Gif_XFrame * Gif_NewXFrames(Gif_Stream *gfs)
Definition: gifx.c:643
Gif_Delete
#define Gif_Delete(p)
Definition: gif.h:313
Clp_ShortNegated
#define Clp_ShortNegated
Option character begins a set of negated short options.
Definition: clp.h:178
Gif_NewArray
#define Gif_NewArray(t, n)
Definition: gif.h:311
Gif_Stream::nimages
int nimages
Definition: gif.h:43
interactive
static int interactive
Definition: gifview.c:163
warning
void warning(const char *format,...)
Definition: gifview.c:234
Gif_Stream::refcount
int refcount
Definition: gif.h:60
clp
static Clp_Parser * clp
Definition: gifview.c:145
Clp_Parser::negated
int negated
Definition: clp.h:237
Gt_Viewer::visual
Visual * visual
Definition: gifview.c:100
Gt_Viewer::screen_number
int screen_number
Definition: gifview.c:99
create_viewer_window
void create_viewer_window(Gt_Viewer *viewer, int w, int h)
Definition: gifview.c:712
clp.h
Functions for parsing command line options.
Gt_Viewer::was_unoptimized
int was_unoptimized
Definition: gifview.c:126
MICRO_PER_SEC
#define MICRO_PER_SEC
Definition: gifview.c:45
Gt_Viewer::name
const char * name
Definition: gifview.c:118
Gif_XContext::transparent_pixel
unsigned long transparent_pixel
Definition: gifx.h:44
GIF_READ_COMPRESSED
#define GIF_READ_COMPRESSED
Definition: gif.h:251
animating
static int animating
Definition: gifview.c:160
cur_use_window
static Window cur_use_window
Definition: gifview.c:154
Gif_XContext::colormap
Colormap colormap
Definition: gifx.h:33
schedule_next_frame
void schedule_next_frame(Gt_Viewer *viewer)
Definition: gifview.c:626
input_stream_done
void input_stream_done(Gt_Viewer *viewer, int first_frame)
Definition: gifview.c:1068
program_name
const char * program_name
Definition: gifview.c:144
Gif_DeleteXFrames
void Gif_DeleteXFrames(Gif_XContext *gfx, Gif_Stream *gfs, Gif_XFrame *frames)
Definition: gifx.c:662
Gif_Stream::screen_height
uint16_t screen_height
Definition: gif.h:50
prepare_frame
void prepare_frame(Gt_Viewer *viewer, int frame)
Definition: gifview.c:899
find_viewer
Gt_Viewer * find_viewer(Display *display, Window window)
Definition: gifview.c:801
Gif_FullReadFile
Gif_Stream * Gif_FullReadFile(FILE *f, int flags, const char *landmark, Gif_ReadErrorHandler handler)
Definition: gifread.c:920
Gif_XContext::depth
uint16_t depth
Definition: gifx.h:31
Clp_Parser::u
unsigned u
Definition: clp.h:245
Gt_Viewer::wait_cursor
Cursor wait_cursor
Definition: gifview.c:105
Clp_BadOption
#define Clp_BadOption
Clp_Next value: argument was an erroneous option.
Definition: clp.h:197
Gif_Stream::screen_width
uint16_t screen_width
Definition: gif.h:49
VERSION
#define VERSION
Definition: win32cfg.h:147
Gif_Image
Definition: gif.h:84
Gif_XImage
Pixmap Gif_XImage(Gif_XContext *gfx, Gif_Stream *gfs, Gif_Image *gfi)
Definition: gifx.c:421
choose_visual
static void choose_visual(Gt_Viewer *viewer)
Definition: gifview.c:303
NAME_OPT
#define NAME_OPT
Definition: gifview.c:175
Clp_Next
int Clp_Next(Clp_Parser *clp)
Parse and return the next argument from clp.
Definition: clp.c:1835
cur_display_name
static const char * cur_display_name
Definition: gifview.c:147
Gif_New
#define Gif_New(t)
Definition: gif.h:310
cur_resource_name
static const char * cur_resource_name
Definition: gifview.c:152
next_viewer
static Gt_Viewer * next_viewer(Gif_Stream *gfs, const char *name)
Definition: gifview.c:511
Gif_Stream::images
Gif_Image ** images
Definition: gif.h:42
Clp_Option
Option description.
Definition: clp.h:41
Clp_Parser::ul
unsigned long ul
Definition: clp.h:247
Gif_Stream
Definition: gif.h:41
Gif_XContext::refcount
int refcount
Definition: gifx.h:46
Gt_Viewer::height
int height
Definition: gifview.c:113
Gif_Image::identifier
char * identifier
Definition: gif.h:99
Gt_Viewer::animating
int animating
Definition: gifview.c:134
INTERACTIVE_OPT
#define INTERACTIVE_OPT
Definition: gifview.c:179
get_input_stream
static Gt_Viewer * get_input_stream(const char *name)
Definition: gifview.c:519
EXTERN
#define EXTERN
Definition: gifview.c:39
screen_width
static unsigned screen_width
Definition: gifdiff.c:46
Gif_XContext
Definition: gifx.h:26
Gt_Viewer::gfx
Gif_XContext * gfx
Definition: gifview.c:103
Clp_Parser::vstr
const char * vstr
Definition: clp.h:240
Gif_DeleteStream
void Gif_DeleteStream(Gif_Stream *)
Definition: giffunc.c:494
Gt_Viewer
struct Gt_Viewer Gt_Viewer
WINDOW_TYPE
#define WINDOW_TYPE
Definition: gifview.c:187
Gt_Viewer::n_unoptimized_frames
int n_unoptimized_frames
Definition: gifview.c:129
xwGETTIMEOFDAY
#define xwGETTIMEOFDAY(a)
Definition: gifview.c:79
Gt_Viewer::preparing
int preparing
Definition: gifview.c:137
Gt_Viewer::pixmap
Pixmap pixmap
Definition: gifview.c:124
TITLE_OPT
#define TITLE_OPT
Definition: gifview.c:182
Gif_Image::delay
uint16_t delay
Definition: gif.h:92
key_press
void key_press(Gt_Viewer *viewer, XKeyEvent *e)
Definition: gifview.c:1079
MIN_DELAY_OPT
#define MIN_DELAY_OPT
Definition: gifview.c:183
delete_viewer
void delete_viewer(Gt_Viewer *viewer)
Definition: gifview.c:491
Clp_vsnprintf
int Clp_vsnprintf(Clp_Parser *clp, char *str, size_t size, const char *format, va_list val)
Format a message.
Definition: clp.c:2336
main
int main(int argc, char *argv[])
Definition: gifview.c:1261
Gt_Viewer::colormap
Colormap colormap
Definition: gifview.c:102
GEOMETRY_OPT
#define GEOMETRY_OPT
Definition: gifview.c:174
Gif_XFrame::user_data
int user_data
Definition: gifx.h:52
Gif_DeleteArray
#define Gif_DeleteArray(p)
Definition: gif.h:314
animations
static Gt_Viewer * animations
Definition: gifview.c:159
options
const Clp_Option options[]
Definition: gifview.c:189
parse_geometry
int parse_geometry(const char *const_g, XSizeHints *sh, int screen_width, int screen_height)
Definition: gifview.c:661
Clp_Parser::val
union Clp_Parser::@0 val
Gt_Viewer::being_deleted
int being_deleted
Definition: gifview.c:115
HELP_OPT
#define HELP_OPT
Definition: gifview.c:176
Clp_ValString
#define Clp_ValString
Option value is an arbitrary string.
Definition: clp.h:59
Clp_ProgramName
const char * Clp_ProgramName(Clp_Parser *clp)
Return clp's program name.
Definition: clp.c:1407
Gif_XFrame::pixmap
Pixmap pixmap
Definition: gifx.h:50
Clp_AllowNumbers
#define Clp_AllowNumbers
String list flag: allow explicit numbers.
Definition: clp.h:327
Gt_Viewer::im_pos
int im_pos
Definition: gifview.c:125
Clp_SetOptionChar
int Clp_SetOptionChar(Clp_Parser *clp, int c, int type)
Set clp's treatment of character c.
Definition: clp.c:710
fatal_error
void fatal_error(const char *format,...)
Definition: gifview.c:213
Gt_Viewer::can_animate
int can_animate
Definition: gifview.c:133
Clp_Parser::i
int i
Definition: clp.h:244
frame_argument
int frame_argument(Gt_Viewer *viewer, const char *arg)
Definition: gifview.c:1039
Gt_Viewer::top_level
int top_level
Definition: gifview.c:108
Gif_GetNamedImage
Gif_Image * Gif_GetNamedImage(Gif_Stream *gfs, const char *name)
Definition: giffunc.c:685
schedule
void schedule(Gt_Viewer *viewer)
Definition: gifview.c:603
pre_delete_viewer
void pre_delete_viewer(Gt_Viewer *viewer)
Definition: gifview.c:786
loop
void loop(void)
Definition: gifview.c:1156
wm_delete_window_atom
static Atom wm_delete_window_atom
Definition: gifview.c:708
Gt_Viewer
Definition: gifview.c:96
Gt_Viewer::scheduled
int scheduled
Definition: gifview.c:136