magicpoint-1.10a.tar.gz (MirPorts)
[alioth/magicpoint.git] / mgp.c
1 /*
2  * Copyright (C) 1997 and 1998 WIDE Project.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  * 3. Neither the name of the project nor the names of its contributors
13  *    may be used to endorse or promote products derived from this software
14  *    without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 /*
29  * $Id: mgp.c,v 1.129 2003/06/24 05:26:33 kazu Exp $
30  */
31
32 static char *mgp_version = "1.10a (20030624)";
33
34 #include "mgp.h"
35 #ifdef USE_SETLOCALE
36 #ifdef HAVE_LOCALE_H
37 #include <locale.h>
38 #endif
39 #endif
40 #include <fcntl.h>
41 #ifdef TTY_KEYINPUT
42 #include <termios.h>
43 #endif
44
45 Window plwin[MAXPAGE];
46 Pixmap maskpix;
47 XFontStruct *plfs;
48 XFontStruct *plkfs;
49
50 u_int pg_mode;
51 u_long pl_fh, pl_fw;
52 time_t t_start;
53 u_int t_fin;
54 u_int tbar_mode;
55
56 static int rakugaki = 0;
57 static int rakugaki_x = -1;
58 static int rakugaki_y = -1;
59 static int rakugaki_color = 0;
60 static XColor rakugaki_fore;
61 static XColor rakugaki_back;
62 static char *rakugaki_forecolors[] = {
63         "red", "green", "blue", "yellow", "black", "white",
64 };
65 static char *rakugaki_backcolors[] = {
66         "black", "black", "black", "black", "gray", "gray",
67 };
68 static int demointerval = 0;    /* XXX define option for this */
69
70 u_long depth_mask;
71
72 char *back_clname = DEFAULT_BACK;
73
74 #ifdef VFLIB
75 char *vfcap_name = VFCAP;
76 #endif
77
78 #ifdef FREETYPE
79 char *freetypefontdir = FREETYPEFONTDIR;
80 char *freetypefont0 = NULL;     /* font name to be used as a last resort */
81 char *freetypemfont0 = NULL;    /* font name to be used as a last resort */
82
83 int unicode_map[65536];
84 #endif
85 int latin_unicode_map[3][256]; /* currently we have iso8859-2-4 map */
86
87 static char *tsfile = NULL;
88 static char *dumpdir = NULL;
89
90 char *gsdevice = DEFAULT_GSDEV;
91 char *htmlimage;
92
93 static time_t srctimestamp;
94
95 static char *xgeometry = NULL;  /*default: full screen*/
96 #ifdef TTY_KEYINPUT
97 static struct termios saveattr, curattr;
98 volatile int ttykey_enable;
99 #endif
100 static pid_t mypid;
101
102 static void genhtml __P((int));
103 static void mgp_usage __P((char *));
104 static void mgp_show_version __P((char *));
105 static void beep __P((void));
106 static void main_loop __P((u_int));
107 static void rakugaki_update __P((struct render_state *, XEvent *));
108 static void rakugaki_updatecolor __P((Cursor));
109 static RETSIGTYPE waitkids __P((int));
110 static int wantreload __P((void));
111
112 /*image*/
113 extern char *expandPath __P((char *));
114
115 #ifdef TTY_KEYINPUT
116 static void
117 susp(int sig)
118 {
119         sigset_t mask;
120
121         if (ttykey_enable) {
122                 if (sig == SIGTTIN || sig == SIGTTOU) {
123                         ttykey_enable = 0;
124                         return;
125                 }
126                 tcsetattr(0, TCSANOW, &saveattr);
127         }
128         signal(sig, SIG_DFL);
129         sigemptyset(&mask);
130         sigaddset(&mask, sig);
131         sigprocmask(SIG_UNBLOCK, &mask, NULL);
132         kill(mypid, sig);
133         /* resumed */
134         signal(sig, susp);
135         if (ttykey_enable) {
136                 if (tcgetpgrp(0) != mypid)
137                         ttykey_enable = 0;
138                 else
139                         tcsetattr(0, TCSANOW, &curattr);
140         }
141 }
142
143 void
144 try_enable_ttykey()
145 {
146
147         if (ttykey_enable)
148                 return;
149         if (tcgetpgrp(0) != mypid)
150                 return;
151
152         ttykey_enable = 1;
153
154         signal(SIGTTIN, susp);
155         signal(SIGTTOU, susp);
156         signal(SIGTSTP, susp);
157         curattr = saveattr;
158         curattr.c_lflag |= ISIG;
159         curattr.c_lflag &= ~(ECHO|ICANON|IEXTEN);
160         curattr.c_iflag &= ~(IXON|IXOFF);
161         curattr.c_cc[VMIN] = 1;
162         curattr.c_cc[VTIME] = 0;
163         /* This call might cause STGTTOU */
164         tcsetattr(0, TCSANOW, &curattr);
165 }
166 #endif
167
168 void
169 cleanup(int sig)
170 {
171         sigset_t mask;
172
173         cleandir();             /* clean up embedded files and its dir */
174 #ifdef TTY_KEYINPUT
175         if (ttykey_enable)
176                 tcsetattr(0, TCSANOW, &saveattr);
177 #endif
178         signal(SIGTERM, SIG_IGN);
179         kill(0, SIGTERM);       /*kill all of my kids*/
180         if (tsfile)
181                 unlink(tsfile);
182         if (sig > 0) {
183                 signal(sig, SIG_DFL);
184                 sigemptyset(&mask);
185                 sigaddset(&mask, sig);
186                 sigprocmask(SIG_UNBLOCK, &mask, NULL);
187                 kill(mypid, sig);
188         }
189
190         finish_win(); /* finish X connection */
191         exit(-sig);
192 }
193
194 int
195 main(argc, argv)
196         int argc;
197         char **argv;
198 {
199         int opt;
200         extern char *optarg;
201         extern int optind;
202         char *progname;
203         int start_page = 1;
204         char buf[BUFSIZ], *p, *p2;
205         extern char *Paths[];
206         extern int NumPaths;
207 #ifdef VFLIB
208         char *vfont_name = VFONT;
209 #endif
210         char *ep;
211
212 #ifdef USE_SETLOCALE
213         setlocale(LC_CTYPE, "");
214 #endif
215         progname = argv[0];
216
217         /* secure by default.  If you need fork/exec, use -U */
218         mgp_flag |= FL_NOFORK;
219
220         if ((p = getenv("MGPWDIR")) != NULL) {
221                 mgpwdir = p;
222         }
223
224 #define ACCEPTOPTS      "Bd:vVob:c:eg:f:hlGp:qt:Q:PSUT:D:CORw:X:x:nF:E:"
225         while ((opt = getopt(argc, argv, ACCEPTOPTS)) != -1) {
226 #undef ACCEPTOPTS
227                 switch (opt) {
228                 case 'B':
229                         mgp_flag |= FL_BIMAGE;
230                         break;
231
232                 case 'd':
233                         mgp_flag |= FL_DEMO;
234                         if (isdigit(optarg[0])) demointerval = atoi(optarg);
235                         else optind --;
236                         break;
237
238                 case 'V':
239                         mgp_flag |= FL_VERBOSE;
240                         verbose++;
241                         break;
242
243                 case 'l':
244                         mgp_flag |= FL_OUTLINE;
245                         break;
246
247                 case 'G':
248                         pg_mode = 1;
249                         break;
250
251                 case 'o':
252                         mgp_flag |= FL_OVER;
253                         break;
254
255 #ifdef VFLIB
256                 case 'c':
257                         vfcap_name = optarg;
258                         break;
259
260                 case 'f':
261                         vfont_name = optarg;
262                         break;
263 #endif /*VFLIB*/
264
265                 case 'b':
266                         back_clname = optarg;
267                         break;
268
269                 case 'p':
270                         ep = NULL;
271                         start_page = atoi(optarg);
272                         if (start_page <= 0) {
273                                 mgp_usage(progname);
274                                 /*NOTREACHED*/
275                         }
276                         break;
277
278                 case 'e':
279                         mgp_flag |= FL_GLYPHEDGE;
280                         break;
281
282                 case 'g':
283                         xgeometry = optarg;
284                         mgp_flag |= FL_OVER;    /* -g implies -o */
285                         break;
286
287                 case 'q':
288                         mgp_flag |= FL_NOBEEP;
289                         break;
290
291                 case 't':
292                         t_fin = atoi(optarg);
293                         tbar_mode = 1;
294                         break;
295
296                 case 'Q':
297                         b_quality[caching] = atoi(optarg);
298                         quality_flag = 1;
299                         break;
300
301                 case 'P':
302                         parse_debug++;
303                         break;
304
305                 case 'S':
306                         mgp_flag |= FL_NOFORK;
307                         break;
308                 case 'U':
309                         mgp_flag &= ~FL_NOFORK;
310                         break;
311
312                 case 'T':
313                         tsfile = optarg;
314                         break;
315
316                 case 'D':
317                         dumpdir = optarg;
318                         break;
319
320                 case 'C':
321                         mgp_flag |= FL_PRIVATE;
322                         break;
323
324                 case 'O':
325                         mgp_flag |= FL_NODECORATION | FL_OVER;  /* -O implies -o */
326                         break;
327
328                 case 'R':
329                         mgp_flag |= FL_NOAUTORELOAD;
330                         break;
331
332                 case 'w':
333                         mgpwdir = optarg;
334                         break;
335
336                 case 'X':
337                         gsdevice = optarg;
338                         break;
339
340                 case 'x':
341                         if (strcmp(optarg, "vflib") == 0)
342                                 mgp_flag |= FL_NOVFLIB;
343                         else if (strcmp(optarg, "freetype") == 0)
344                                 mgp_flag |= FL_NOFREETYPE;
345                         else if (strcmp(optarg, "xft") == 0)
346                                 mgp_flag |= FL_NOXFT;
347                         else {
348                                 fprintf(stderr, "unknown rendering engine %s\n",
349                                         optarg);
350                                 mgp_usage(progname);
351                                 /*NOTREACHED*/
352                         }
353                         break;
354                 case 'v':
355                         mgp_show_version(progname);
356                         /*NOTREACHED*/
357
358                 case 'n':
359                         mgp_flag |= FL_NOSTDIN;
360                         break;
361
362                 case 'F':
363                         mgp_flag |= FL_FRDCACHE;
364                         p = optarg;
365                         if ((p2 = strsep(&p, ",")))
366                                 cache_mode = atoi(p2);
367                         if ((p2 = strsep(&p, ",")))
368                                 cache_effect = atoi(p2);
369                         if ((p2 = strsep(&p, ",")))
370                                 cache_value = atoi(p2);
371                         break;
372
373                 case 'E':
374                         htmlimage = optarg;
375                         break;
376
377                 case 'h':
378                 default:
379                         mgp_usage(progname);
380                         /*NOTREACHED*/
381                 }
382         }
383
384         argc -= optind;
385         argv += optind;
386
387         mypid = getpid();
388         setpgid(mypid, mypid);
389         signal(SIGHUP, cleanup);
390         signal(SIGINT, cleanup);
391         signal(SIGQUIT, cleanup);
392         signal(SIGTERM, cleanup);
393
394         if (argc != 1) {
395                 mgp_usage(progname);
396                 /*NOTREACHED*/
397         }
398         mgp_fname = argv[0];
399     {
400         struct stat sb;
401         srctimestamp = (time_t) 0;
402         if (0 <= stat(mgp_fname, &sb))
403                 srctimestamp = sb.st_ctime;
404     }
405
406         init_win1(xgeometry);
407         strncpy(buf, mgp_fname, sizeof(buf));
408         if ((p = rindex(buf, '/'))) {
409                 *p = '\0';
410                 Paths[NumPaths++]= expandPath(buf);
411         }
412         loadPathsAndExts();
413         load_file(mgp_fname);
414         if (parse_error)
415                 exit(-1);
416         init_win2();
417 #ifdef VFLIB
418         vfc_setfont(vfont_name);
419 #endif
420
421         signal(SIGCHLD, waitkids);
422
423         if (dumpdir)
424                 genhtml(start_page);
425         else if (mgp_flag & FL_DEMO) {
426                 struct render_state state;
427
428                 memset(&state, 0, sizeof(struct render_state));
429                 state.target = window;  /*XXX*/
430                 state.width = window_width;
431                 state.height = window_height;
432                 while (start_page <= maxpage) {
433                         state_goto(&state, start_page, 0);
434                         draw_page(&state, NULL);
435                         start_page++;
436                         sleep(demointerval);    /*XXX*/
437                 }
438         } else {
439                 init_win3();
440                 main_loop(start_page);
441         }
442
443         cleanup(0);     /* never returns */
444         exit(0);        /* avoid warnings */
445         /*NOTREACHED*/
446 }
447
448 static void
449 genhtml(start_page)
450         int start_page;
451 {
452         struct render_state state;
453         char buf[BUFSIZ];
454         int fd;
455         FILE *html;
456         FILE *txt;
457         int page;
458         char *childdebug;
459         char *convdb[][3] = {{ "jpg", "cjpeg", "djpeg" },
460                                 { "png", "pnmtopng", "pngtopnm" },
461                                 { NULL, NULL, NULL }};
462         int inum = 0;
463
464         /* check image type */
465         if (htmlimage) {
466                 for (inum = 0; *convdb[inum] != NULL; inum++) {
467                         if (strcmp(*convdb[inum], htmlimage) == 0)
468                                 break;
469                 }
470                 if (*convdb[inum] == NULL) {
471                         fprintf(stderr, "unknwon image type %s.\n", htmlimage);
472                         /* print out valid image types */
473                         fprintf(stderr, "Valid image types: ");
474                         for (inum = 0; *convdb[inum] != 0; inum++) {
475                                 fprintf(stderr, "%s ", *convdb[inum] );
476                         }
477                         fprintf(stderr,"\n");
478                         cleanup(-1);
479                 }
480         }
481
482         /* check if we can write to the directory */
483         sprintf(buf, "%s/%ld", dumpdir, (long)time((time_t *)NULL));
484         fd = open(buf, O_WRONLY | O_CREAT, 0644);
485         if (fd < 0) {
486                 fprintf(stderr, "bad dump directory %s.\n", dumpdir);
487                 cleanup(-1);
488         }
489         close(fd);
490         unlink(buf);
491
492         memset(&state, 0, sizeof(struct render_state));
493         state.target = window;  /*XXX*/
494         state.width = window_width;
495         state.height = window_height;
496         childdebug = parse_debug ? "" : "2>&-";
497         for (page = start_page; page <= maxpage; page++) {
498                 fprintf(stderr, "generating page %d... ", page);
499                 state_goto(&state, page, 0);
500                 draw_page(&state, NULL);
501
502
503 #define EXT convdb[inum][0]
504
505                 /*
506                  * dump out image
507                  */
508                 fprintf(stderr, "(full image)");
509                 sprintf(buf, "xwintoppm -silent -name MagicPoint | "
510                         "%s %s > %s/mgp%05d.%s",
511                         convdb[inum][1], childdebug, dumpdir, page, EXT);
512                 system(buf);    /*XXX security hole*/
513                 fprintf(stderr, "(thumbnail)");
514                 sprintf(buf, "%s %s/mgp%05d.%s | "
515                         "pnmscale 0.25 | %s %s > %s/mgp%05d.idx.%s",
516                         convdb[inum][2], dumpdir, page, EXT, convdb[inum][1], childdebug,
517                         dumpdir, page, EXT);
518                 system(buf);    /*XXX security hole*/
519
520                 /*
521                  * dump out html file
522                  */
523                 fprintf(stderr, "(html)");
524                 sprintf(buf, "%s/mgp%05d.html", dumpdir, page);
525                 html = fopen(buf, "w");
526                 if (!html)
527                         continue;
528                 fprintf(html,
529 "<HTML>\n"
530 "<HEAD><TITLE>MagicPoint presentation foils</TITLE></HEAD>\n"
531 "<BODY>\n");
532                 fprintf(html,
533                     "<A HREF=\"index.html\">[index]</A> "
534                     "<A HREF=mgp%05d.txt>[text page]</A> ", page);
535                 if (1 < page) {
536                     fprintf(html,
537                         "<A HREF=mgp%05d.html>[&lt;&lt;start]</A>  "
538                         "<A HREF=mgp%05d.html>[&lt;prev]</A> ",
539                                 1, page - 1);
540                 } else
541                         fprintf(html, "[&lt;&lt;start] [&lt;prev] ");
542                 if (page < maxpage) {
543                     fprintf(html,
544                         "<A HREF=mgp%05d.html>[next&gt;]</A> "
545                         "<A HREF=mgp%05d.html>[last&gt;&gt;]</A>\n",
546                                 page + 1, maxpage);
547                 } else
548                         fprintf(html, "[next&gt;] [last&gt;&gt;]\n");
549                 fprintf(html, "<BR>Page %d: %s<BR>\n", page, page_title(page));
550                 fprintf(html, "<HR>\n");
551                 if (window_width < 0 || window_height < 0) {
552                         fprintf(html, "<IMG SRC=\"mgp%05d.%s\" "
553                                 "ALT=\"Page %d\">\n",
554                                 page, EXT, page);
555                 } else {
556                         fprintf(html, "<IMG SRC=\"mgp%05d.%s\" "
557                                 "WIDTH=%d HEIGHT=%d ALT=\"Page %d\"><BR>\n",
558                                 page, EXT, window_width, window_height,
559                                 page);
560                 }
561                 fprintf(html, "<HR>Generated by "
562                     "<A HREF=\"http://www.mew.org/mgp/\">MagicPoint</A>\n"
563                     "</BODY></HTML>\n");
564                 fclose(html);
565
566                 /*
567                  * dump out text file
568                  */
569                 fprintf(stderr, "(txt)\n");
570                 sprintf(buf, "%s/mgp%05d.txt", dumpdir, page);
571                 txt = fopen(buf, "w");
572                 if (!txt)
573                         continue;
574                 state_goto(&state, page, 0);
575                 state_init(&state);
576                 while (1) {
577                         if (state.phase == P_NONE || state.phase == P_END)
578                                 break;
579                         if (!state.cp) {
580                                 state_next(&state);
581                                 continue;
582                         }
583                         switch (state.cp->ct_op) {
584                         case CTL_PAUSE:
585                                 if (state.cp->cti_value)
586                                         goto txtdone;
587                                 break;
588                         case CTL_TEXT:
589                                 fprintf(txt, "%s", state.cp->ctc_value);
590                                 break;
591                         case CTL_LINEEND:
592                                 fprintf(txt, "\n");
593                                 break;
594                         }
595
596                         state_next(&state);
597                 }
598 txtdone:;
599                 fclose(txt);
600         }
601
602         fprintf(stderr, "generating top page... ");
603         sprintf(buf, "%s/index.html", dumpdir);
604         html = fopen(buf, "w");
605         if (!html) {
606                 fprintf(stderr, "could not generate top page\n");
607                 cleanup(-1);
608         }
609         fprintf(stderr, "\n");
610         fprintf(html,
611 "<HTML>\n"
612 "<HEAD><TITLE>MagicPoint presentation foils</TITLE></HEAD>\n"
613 "<BODY>\n");
614         for (page = start_page; page <= maxpage; page++) {
615                 if (window_width < 0 || window_height < 0) {
616                         fprintf(html, "<A HREF=\"mgp%05d.html\">"
617                                 "<IMG SRC=\"mgp%05d.idx.%s\" "
618                                 "ALT=\"Page %d\"></A>\n",
619                                 page, page, EXT, page);
620                 } else {
621                         fprintf(html, "<A HREF=\"mgp%05d.html\">"
622                                 "<IMG SRC=\"mgp%05d.idx.%s\" "
623                                 "WIDTH=%d HEIGHT=%d "
624                                 "ALT=\"Page %d\"></A>\n",
625                                 page, page, EXT, window_width / 4,
626                                 window_height / 4, page);
627                 }
628         }
629         fprintf(html, "<HR>\n");
630         fprintf(html, "Generated by "
631                 "<A HREF=\"http://www.mew.org/mgp/\">"
632                 "MagicPoint</A>\n");
633         fprintf(html, "<BR>\n</BODY></HTML>\n");
634         fclose(html);
635 }
636
637 static void
638 mgp_show_version(name)
639         char *name;
640 {
641         char *p;
642
643         if ((p = strrchr(name, '/')))
644                 p ++;
645         else
646                 p = name;
647         fprintf(stderr, "%s version %s\n", p, mgp_version);
648         exit(0);
649 }
650
651 static void
652 mgp_usage(name)
653         char *name;
654 {
655         fprintf(stderr, "Usage: %s [opts] mgpfile\n", name);
656
657         fprintf(stderr, "\t-b <color>: Specify background color\n");
658 #ifdef VFLIB
659         fprintf(stderr, "\t-c <vfcap>: Specify vfontcap file\n");
660 #endif /*VFLIB*/
661         fprintf(stderr, "\t-d: Demo mode - go through the presentation\n");
662 #ifdef VFLIB
663         fprintf(stderr, "\t-f <font>: Specify default vflib font\n");
664 #endif /*VFLIB*/
665         fprintf(stderr, "\t-g <geometry>: Set window geometry\n");
666         fprintf(stderr, "\t-h: Display this help message\n");
667 #ifdef VFLIB
668         fprintf(stderr, "\t-l: Do not use outline font on vflib\n");
669 #endif /*VFLIB*/
670         fprintf(stderr, "\t-n: Disables control key input from tty\n");
671         fprintf(stderr, "\t-o: Do not override the window manager\n");
672         fprintf(stderr, "\t-p <page>: Start at the specified page\n");
673         fprintf(stderr, "\t-q Do not beep on errors\n");
674         fprintf(stderr, "\t-t <timeslot>: Enable presentation timer\n");
675         fprintf(stderr, "\t-v: Show version number and quit\n");
676         fprintf(stderr, "\t-w <dir>: Specify a working directory\n");
677         fprintf(stderr, "\t-x <engine>: Disable specified rendering engine\n");
678
679         fprintf(stderr, "\t-B: Ignore background image\n");
680         fprintf(stderr, "\t-C: Use private colormap\n");
681         fprintf(stderr, "\t-D <dir>: Generate html pages for the presentation\n");
682         fprintf(stderr, "\t-E <name>: Use this image format in html (jpg or png)\n");
683         fprintf(stderr, "\t-F<mode>,<effect>,<value>: Use forwarding caches\n");
684         fprintf(stderr, "\t-G: Page guide is on\n");
685         fprintf(stderr, "\t-O: Obey to the window manager\n");
686         fprintf(stderr, "\t-Q <quality>: Set background image quality(0-100)\n");
687         fprintf(stderr, "\t-R: Do not perform automatic reload\n");
688         fprintf(stderr, "\t-S: Do not process directives that forks process (default)\n");
689         fprintf(stderr, "\t-U: Do process directives that forks process (unsecure mode)\n");
690         fprintf(stderr, "\t-T <timestampfile>: Update timestampfile on page refresh\n");
691         fprintf(stderr, "\t-P: print stderr from image conversion tools (by default it's discarded)\n");
692         fprintf(stderr, "\t-V: Be verbose\n");
693         fprintf(stderr, "\t-X <gsdevice>: ghostscript device to use\n");
694
695         exit(0);
696 }
697
698 static void
699 beep()
700 {
701         if (!(mgp_flag & FL_NOBEEP))
702                 XBell(display, 0);
703 }
704
705 static void
706 main_loop(start_page)
707         u_int start_page;
708 {
709         XEvent e, ahead;
710         KeySym key;
711         u_int i;
712         u_int number = 0;
713         u_int shift = 0;
714         u_int control = 0;
715         static struct render_state state;
716         static Cursor pen_curs;
717         u_int prevpage;
718
719         memset(&state, 0, sizeof(struct render_state));
720         state.target = window;  /*XXX*/
721         state.width = window_width;
722         state.height = window_height;
723         if (!pen_curs) {
724                 pen_curs = XCreateFontCursor(display, XC_pencil);
725                 rakugaki_updatecolor(pen_curs);
726         }
727         state_goto(&state, start_page, 0);
728 #if 0
729         /* be conservative about first page... */
730         draw_page(&state, NULL);
731 #endif
732         if (pg_mode) {
733                 pg_on();
734                 pg_draw(&state);
735                 XFlush(display);
736         }
737
738 #ifdef TTY_KEYINPUT
739         if (!(mgp_flag & FL_NOSTDIN)) {
740                 if (tcgetattr(0, &saveattr) < 0)
741                         mgp_flag |= FL_NOSTDIN;
742                 else
743                         try_enable_ttykey();
744         }
745 #endif
746
747         while (1) {
748                 if (!t_start && 1 < state.page)
749                         t_start = time(NULL);
750                 if (rakugaki)
751                         XDefineCursor(display, window, pen_curs);
752                 else
753                         XUndefineCursor(display, window);
754
755                 do {
756                         ; /*nothing*/
757                 } while (draw_one(&state, &e) == False);
758
759                 if (t_fin)
760                         timebar(&state);
761 #if 0 /* last page reload bug fix? */
762                 if (state.cp && state.cp->ct_op == CTL_PAUSE) {
763 #else
764                 if ((state.cp && state.cp->ct_op == CTL_PAUSE) 
765                         || (state.page == maxpage)){ 
766 #endif 
767                         if (tsfile) {
768                                 int fd;
769                                 fd = open(tsfile, O_WRONLY|O_TRUNC|O_CREAT,
770                                     0644);
771                                 if (fd < 0) {
772                                     fprintf(stderr, "timestamp file %s "
773                                         "write failed; "
774                                         "timestamp turning off\n",
775                                         tsfile);
776                                     tsfile = NULL;
777                                 } else {
778                                     if (write(fd, &state, sizeof(state)) < 0)
779                                         fprintf(stderr,
780                                             "timestamp file write failed\n");
781                                     close(fd);
782                                 }
783                         }
784
785                         if (wantreload()) {
786                                 draw_reinit(&state);
787                                 cleanup_file();
788                                 load_file(mgp_fname);
789                                 if (maxpage < state.page)
790                                         state.page = 1;
791                                 state_goto(&state, state.page, 1); /*repaint*/
792                         }
793                 }
794
795                 prevpage = state.page;
796
797                 switch (e.type) {
798                 case EnterNotify:
799                         for (i = 1; i <= maxpage; i++) {
800                                 if (e.xany.window == plwin[i]) {
801                                         XClearWindow(display, plwin[i]);
802                                         pl_pdraw(&state, i, gc_plrev);
803                                         pl_title(i);
804                                 }
805                         }
806                         XFlush(display);
807                         break;
808
809                 case LeaveNotify:
810                         for (i = 1; i <= maxpage; i++) {
811                                 if (e.xany.window == plwin[i]) {
812                                         XClearWindow(display, plwin[i]);
813                                         pl_pdraw(&state, i, gc_pl);
814                                         pl_title(0);
815                                 }
816                         }
817                         XFlush(display);
818                         break;
819
820                 case ButtonPress:
821                         if (rakugaki && e.xany.window == window) {
822                                 rakugaki_update(&state, &e);
823                                 break;
824                         }
825
826                         for (i = 1; i <= maxpage; i++) {
827                                 if (e.xany.window == plwin[i]) {
828                                         state_goto(&state, i, 0);
829                                         pl_off(&state);
830                                         break;
831                                 }
832                         }
833                         if (e.xany.window == window) {
834                                 if (e.xbutton.button == 1) {
835                                         struct render_state tstate;
836                                         tstate = state;
837
838                                         if (!shift && state.cp
839                                          && state.cp->ct_op == CTL_PAUSE) {
840                                                 state_next(&tstate);
841                                         } else if (state.page + 1 < maxpage) {
842                                                 state_goto(&tstate,
843                                                         state.page + 1, 0);
844                                         } else {
845                                                 beep();
846                                                 break;
847                                         }
848
849                                         if (memcmp(&state, &tstate,
850                                                         sizeof(state)) != 0) {
851                                                 state = tstate;
852                                         } else {
853                                                 /* cannot make a progress */
854                                                 beep();
855                                         }
856                                 } else if (e.xbutton.button == 3) {
857                                         if (state.page > 1) {
858                                                 state_goto(&state,
859                                                         state.page - 1, 0);
860                                         } else
861                                                 beep();
862                                 }
863                         }
864                         break;
865
866                 case ButtonRelease:
867                         if (rakugaki && e.xany.window == window)
868                                 rakugaki_update(&state, &e);
869                         if (e.xbutton.button == 2)
870                                 goto rakugaki_toggle;
871                         break;
872
873                 case MotionNotify:
874                         if (!rakugaki)
875                                 break;
876                         if (e.xany.window == window)
877                                 rakugaki_update(&state, &e);
878                         break;
879
880                 case KeyPress:
881                         key = XLookupKeysym((XKeyEvent *)&e, 0);
882
883                         switch (key) {
884                         case XK_q:
885                         case XK_Escape:
886                                 return;
887                                 /*NOTREACHED*/
888
889                         case XK_f:
890                         case XK_j:
891                         case XK_n:
892                         case XK_Down:
893                         case XK_Next:
894                         case XK_space:
895                             {
896                                 struct render_state tstate;
897                                 tstate = state;
898
899                                 if (number == 0)
900                                         number = 1;
901                                 if (state.cp && state.cp->ct_op == CTL_PAUSE){
902                                         state_next(&tstate);
903                                 } else if (state.page + number
904                                                 <= maxpage) {
905                                                 state_goto(&tstate, state.page + number, 0);
906                                 } else {
907                                         beep();
908                                         break;
909                                 }
910
911                                 if (memcmp(&state, &tstate,
912                                                 sizeof(state)) != 0) {
913                                         state = tstate;
914                                 } else {
915                                         /* cannot make a progress */
916                                         beep();
917                                 }
918                                 number = 0;
919                                 break;
920                             }
921
922                         case XK_b:
923                         case XK_k:
924                         case XK_p:
925                         case XK_Up:
926                         case XK_Prior:
927                         case XK_BackSpace:
928                         case XK_Delete:
929                                 if (number == 0) number = 1;
930                                 if (state.page - number >= 1) {
931                                         state_goto(&state,
932                                                 state.page - number, 0);
933                                 } else
934                                         beep();
935                                 number = 0;
936                                 break;
937
938                         case XK_x:
939                                 if (shift) {
940                                         rakugaki_color++;
941                                         rakugaki_updatecolor(pen_curs);
942                                         XUndefineCursor(display, window);
943                                         XDefineCursor(display, window,
944                                                 pen_curs);
945                                         XFlush(display);
946                                 } else {
947 rakugaki_toggle:
948                                         rakugaki = 1 - rakugaki;
949                                         rakugaki_x = rakugaki_y = -1;
950
951                                         if (rakugaki) {
952                                                 XDefineCursor(display, window,
953                                                         pen_curs);
954                                         } else {
955                                                 XUndefineCursor(display,
956                                                         window);
957                                         }
958                                         XFlush(display);
959                                 }
960                                 break;
961
962                         case XK_t:
963                                 if (tbar_mode)
964                                         tbar_mode = 0;
965                                 else {
966                                         if (t_fin)
967                                                 tbar_mode = 1;
968                                 }
969                                 break;
970
971                         case XK_Control_L:
972                         case XK_Control_R:
973                                 pl_on(&state);
974                                 control = 1;
975                                 number = 0;
976                                 break;
977
978                         case XK_Shift_L:
979                         case XK_Shift_R:
980                                 shift = 1;
981                                 number = 0;
982                                 break;
983
984                         case XK_r:
985                                 if (control) {
986 reload:
987                                         pl_off(&state);
988                                         cached_page = 0;
989                                         reset_background_pixmap();
990                                         draw_reinit(&state);
991                                         cleanup_file();
992                                         load_file(mgp_fname);
993                                         if (maxpage < state.page)
994                                                 state.page = 1;
995                                         state_goto(&state, state.page, 1);
996                                         goto repaint;
997                                 }
998                                 break;
999
1000                         case XK_l:      /* used to be control-L */
1001 repaint:;
1002                             {
1003                                 struct ctrl *lastcp;
1004                                 lastcp = state.cp;
1005                                 state.repaint = 1;
1006                                 state_goto(&state, state.page, 1);
1007                                 draw_page(&state, lastcp);
1008                                 state.repaint = 0;
1009                                 number = 0;
1010                             }
1011                                 break;
1012
1013                         case XK_g:
1014                                 if (shift) {
1015                                         pg_mode = 1 - pg_mode;
1016                                         if (pg_mode) {
1017                                                 pg_on();
1018                                                 pg_draw(&state);
1019                                         } else
1020                                                 pg_off();
1021                                 } else {
1022                                         if (number == 0)
1023                                                 number = maxpage;
1024                                         if (number <= maxpage && number > 0) {
1025                                                 state_goto(&state, number, 0);
1026                                                 state_newpage(&state);
1027                                                 state_init(&state);
1028                                         } else
1029                                                 beep();
1030                                 }
1031                                 number = 0;
1032                                 break;
1033
1034                         case XK_0:
1035                         case XK_1:
1036                         case XK_2:
1037                         case XK_3:
1038                         case XK_4:
1039                         case XK_5:
1040                         case XK_6:
1041                         case XK_7:
1042                         case XK_8:
1043                         case XK_9:
1044                                 number = number * 10 + key - XK_0;
1045                                 break;
1046
1047                         case XK_c:
1048                                 if (verbose) 
1049                                         if (mgp_flag & FL_FRDCACHE)
1050                                                 printf("turn off forward cache\n");
1051                                         else                    
1052                                                 printf("turn on forward cache\n");
1053
1054                                 mgp_flag ^= FL_FRDCACHE;
1055                         break;
1056
1057                         case XK_a:
1058                                 XCopyArea(display, cachewin, window, gc_cache,
1059                                                 0, 0, window_width, window_height, 0, 0);
1060                                 break;
1061
1062                         default:
1063                                 number = 0;
1064                                 break;
1065                         }
1066
1067                         break;
1068
1069                 case KeyRelease:
1070                         key = XLookupKeysym((XKeyEvent *)&e, 0);
1071
1072                         switch (key) {
1073                         case XK_Control_L:
1074                         case XK_Control_R:
1075                                 pl_off(&state);
1076                                 control = 0;
1077                                 break;
1078                         case XK_Shift_L:
1079                         case XK_Shift_R:
1080                                 shift = 0;
1081                                 break;
1082                         }
1083                         break;
1084
1085                 case Expose:
1086                         if (e.xexpose.window != window) 
1087                                 break;
1088                         if (state.repaint) 
1089                                 break;
1090
1091                         /* compress expose event */
1092                         while (XEventsQueued(display, QueuedAfterReading) > 0) {
1093                                 XPeekEvent(display, &ahead);
1094                                 if (ahead.type != Expose)
1095                                         break;
1096                                 if (ahead.xexpose.window != window)
1097                                         break;
1098                                 XNextEvent(display, &e);        
1099                         }
1100
1101                         if (wantreload())
1102                                 goto reload;
1103                         if ((state.cp && state.cp->ct_op == CTL_PAUSE) ||
1104                                 (state.page  == maxpage)) 
1105                                 goto repaint;
1106                         break;
1107
1108                 case ConfigureNotify:
1109                         if ((e.xconfigure.window != window) && 
1110                                 ((mgp_flag & FL_OVER) || 
1111                                 e.xconfigure.window != RootWindow(display, screen)))
1112                                 break;
1113                         if (window_width != e.xconfigure.width
1114                          || window_height != e.xconfigure.height) {
1115                                 struct ctrl *lastcp;
1116
1117                                 if (!(mgp_flag & FL_OVER))
1118                                         XMoveResizeWindow(display, window, 0, 0, 
1119                                                 e.xconfigure.width, e.xconfigure.height);
1120
1121                                 if (pg_mode)
1122                                         pg_off();
1123                                 pl_off(&state);
1124                                 window_width = e.xconfigure.width;
1125                                 window_height = e.xconfigure.height;
1126                                 state.width = e.xconfigure.width;
1127                                 state.height = e.xconfigure.height;
1128                                 if (mgp_flag & FL_FRDCACHE) {
1129                                         cached_page = 0;
1130                                         reset_background_pixmap();
1131                                 }
1132                                 reset_background_pixmap();
1133                                 draw_reinit(&state);    /*notify*/
1134                                 lastcp = state.cp;
1135                                 state_goto(&state, state.page, 1);
1136                                 draw_page(&state, lastcp);
1137
1138                         }
1139                         if (pg_mode) {
1140                                 pg_on();
1141                                 pg_draw(&state);
1142                         }
1143                         if (wantreload())
1144                                 goto reload;
1145                         break;
1146                 }
1147
1148                 /* page may have changed... */
1149                 if (pg_mode)
1150                         pg_draw(&state);
1151                 if (prevpage != state.page) {
1152                         pl_pdraw(&state, prevpage, gc_pl);
1153                         pl_pdraw(&state, state.page, gc_plrev);
1154                         pl_title(state.page);
1155                 }
1156
1157                 if (state.phase == P_END) {
1158                         if (state.page < maxpage)
1159                                 state_goto(&state, state.page + 1, 0);
1160                 }
1161         }
1162 }
1163
1164 static void
1165 rakugaki_update(state, e)
1166         struct render_state *state;
1167         XEvent *e;
1168 {
1169         int x, y;
1170
1171         if (e->type == MotionNotify) {
1172                 XMotionEvent *em;
1173                 em = (XMotionEvent *)e;
1174                 x = em->x; y = em->y;
1175         } else if (e->type == ButtonPress) {
1176                 XButtonPressedEvent *eb;
1177                 eb = (XButtonPressedEvent *)e;
1178                 x = eb->x; y = eb->y;
1179                 if (e->xbutton.button != 1) return;
1180         } else {
1181                 rakugaki_x = rakugaki_y = -1;
1182                 return;
1183         }
1184
1185         if (rakugaki_x < 0 || rakugaki_y < 0)
1186                 XDrawRectangle(display, state->target, gcpen, x, y, 1, 1);
1187         else {
1188                 XDrawLine(display, state->target, gcpen,
1189                         x, y,
1190                         rakugaki_x, rakugaki_y);
1191                 XDrawLine(display, state->target, gcpen,
1192                         x + 1, y,
1193                         rakugaki_x + 1, rakugaki_y);
1194                 XDrawLine(display, state->target, gcpen,
1195                         x, y + 1,
1196                         rakugaki_x, rakugaki_y + 1);
1197                 XDrawLine(display, state->target, gcpen,
1198                         x + 1, y + 1,
1199                         rakugaki_x + 1, rakugaki_y + 1);
1200         }
1201         rakugaki_x = x;
1202         rakugaki_y = y;
1203 }
1204
1205 static void
1206 rakugaki_updatecolor(cursor)
1207         Cursor cursor;
1208 {
1209         XColor junk;
1210         int maxidx;
1211
1212         maxidx = sizeof(rakugaki_forecolors)/sizeof(rakugaki_forecolors[0]);
1213         if (maxidx <= rakugaki_color)
1214                 rakugaki_color %= maxidx;
1215
1216         XAllocNamedColor(display, DefaultColormap(display, screen),
1217                 rakugaki_forecolors[rakugaki_color], &rakugaki_fore, &junk);
1218         XAllocNamedColor(display, DefaultColormap(display, screen),
1219                 rakugaki_backcolors[rakugaki_color], &rakugaki_back, &junk);
1220
1221         /*
1222          * due to the design of the cursor, it looks more natural if we swap
1223          * the background color and foreground color.
1224          */
1225         XRecolorCursor(display, cursor, &rakugaki_back, &rakugaki_fore);
1226
1227         XSetForeground(display, gcpen, rakugaki_fore.pixel);
1228 }
1229
1230 static struct {
1231         pid_t pid;
1232         void *key;
1233         Window window_id;
1234         int flag;
1235 } childtab[64];
1236 static int childidx = 0;
1237
1238 pid_t
1239 checkchild(key)
1240         void *key;
1241 {
1242         int i;
1243
1244         for (i = 0; i < childidx; i++) {
1245                 if (childtab[i].pid == (pid_t)-1)
1246                         continue;
1247                 if (childtab[i].key == key)
1248                         return childtab[i].pid;
1249         }
1250         return (pid_t)-1;
1251 }
1252
1253 Window
1254 checkchildwin(key)
1255         void *key;
1256 {
1257         int i;
1258
1259         for (i = 0; i < childidx; i++) {
1260                 if (childtab[i].pid == (pid_t)-1)
1261                         continue;
1262                 if (childtab[i].key == key)
1263                         return childtab[i].window_id;
1264         }
1265         return (Window)-1;
1266 }
1267
1268 void
1269 regchild(pid, key, window_id, flag)
1270         pid_t pid;
1271         void *key;
1272         Window window_id;
1273         int flag;
1274 {
1275         int i;
1276
1277         for (i = 0; i < childidx; i++) {
1278                 if (childtab[i].pid == (pid_t)-1) {
1279                         childtab[i].pid = pid;
1280                         childtab[i].key = key;
1281                         childtab[i].window_id = window_id;
1282                         childtab[i].flag = flag;
1283                         return;
1284                 }
1285         }
1286         childtab[childidx].pid = pid;
1287         childtab[childidx].key = key;
1288         childtab[childidx].flag = flag;
1289         childtab[childidx].window_id = window_id;
1290         childidx++;
1291 }
1292
1293 void
1294 purgechild(flag)
1295         int flag;
1296 {
1297         int i;
1298
1299         for (i = 0; i < childidx; i++) {
1300                 if (childtab[i].pid == (pid_t)-1)
1301                         continue;
1302                 if (childtab[i].flag == flag){
1303                         kill(childtab[i].pid, SIGTERM);
1304                 }
1305         }
1306 }
1307
1308 static RETSIGTYPE
1309 waitkids(sig)
1310         int sig;
1311 {
1312         int status;
1313         int i;
1314         pid_t pid;
1315
1316         if (sig != SIGCHLD) {
1317                 fprintf(stderr, "signal different from expected: %d\n", sig);
1318                 cleanup(-1);
1319         }
1320         while ((pid_t) 0 < (pid = waitpid(-1, &status, WNOHANG))) {
1321                 for (i = 0; i < childidx; i++) {
1322                         if (childtab[i].pid == pid) {
1323                                 childtab[i].pid = (pid_t)-1;
1324                                 childtab[i].window_id = (Window)-1;
1325                         }
1326                 }
1327         }
1328 }
1329
1330 static int
1331 wantreload()
1332 {
1333         struct stat sb;
1334
1335         if (mgp_flag & FL_NOAUTORELOAD)
1336                 return 0;
1337
1338         if (0 <= stat(mgp_fname, &sb)) {
1339                 if (srctimestamp < sb.st_ctime) {
1340                         srctimestamp = sb.st_ctime;
1341                         return 1;
1342                 }
1343         }
1344
1345         return 0;
1346 }
1347
1348 /*
1349   remap child window which was invoked by xsystem directive.
1350   this is an adhoc solution for window-maker.
1351 */
1352 void
1353 remapchild()
1354 {
1355         int     i;
1356
1357         for (i = 0; i < childidx; i++) {
1358                 if (childtab[i].window_id > 0){
1359                         XMapSubwindows(display, window);
1360                         XFlush(display);
1361                         return;
1362                 }
1363         }
1364         return;
1365 }