/* * Copyright (C) 1997 and 1998 WIDE Project. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * $Id: mgp.c,v 1.145 2007/01/21 17:56:01 nishida Exp $ */ static char *mgp_version = "1.12a (20070123)"; #include "mgp.h" #ifdef USE_SETLOCALE #ifdef HAVE_LOCALE_H #include #endif #endif #include #ifdef TTY_KEYINPUT #include #endif Window plwin[MAXPAGE]; Pixmap maskpix; XFontStruct *plfs; XFontStruct *plkfs; u_int pg_mode; u_long pl_fh, pl_fw; time_t t_start; u_int t_fin; u_int tbar_mode; static int rakugaki = 0; static int rakugaki_x = -1; static int rakugaki_y = -1; static int rakugaki_color = 0; static XColor rakugaki_fore; static XColor rakugaki_back; static char *rakugaki_forecolors[] = { "red", "green", "blue", "yellow", "black", "white", }; static char *rakugaki_backcolors[] = { "black", "black", "black", "black", "gray", "gray", }; static int demointerval = 0; /* XXX define option for this */ u_long depth_mask; char *back_clname = DEFAULT_BACK; #ifdef VFLIB char *vfcap_name = VFCAP; #endif #ifdef FREETYPE char *freetypefontdir = FREETYPEFONTDIR; char *freetypefont0 = NULL; /* font name to be used as a last resort */ char *freetypemfont0 = NULL; /* font name to be used as a last resort */ int unicode_map[65536]; #endif int latin_unicode_map[3][256]; /* currently we have iso8859-2-4 map */ static char *tsfile = NULL; static char *dumpdir = NULL; char *gsdevice = DEFAULT_GSDEV; char *htmlimage; static time_t srctimestamp; static char *xgeometry = NULL; /*default: full screen*/ #ifdef TTY_KEYINPUT static struct termios saveattr, curattr; volatile int ttykey_enable; #endif static pid_t mypid; static void genhtml __P((int)); static void mgp_usage __P((char *)); static void mgp_show_version __P((char *)); static void beep __P((void)); static void main_loop __P((u_int)); static void rakugaki_update __P((struct render_state *, XEvent *)); static void rakugaki_updatecolor __P((Cursor)); static RETSIGTYPE waitkids __P((int)); static int wantreload __P((void)); /*image*/ extern char *expandPath __P((char *)); #ifdef TTY_KEYINPUT static void susp(int sig) { sigset_t mask; if (ttykey_enable) { if (sig == SIGTTIN || sig == SIGTTOU) { ttykey_enable = 0; return; } tcsetattr(0, TCSANOW, &saveattr); } signal(sig, SIG_DFL); sigemptyset(&mask); sigaddset(&mask, sig); sigprocmask(SIG_UNBLOCK, &mask, NULL); kill(mypid, sig); /* resumed */ signal(sig, susp); if (ttykey_enable) { if (tcgetpgrp(0) != mypid) ttykey_enable = 0; else tcsetattr(0, TCSANOW, &curattr); } } void try_enable_ttykey() { if (ttykey_enable) return; if (tcgetpgrp(0) != mypid) return; ttykey_enable = 1; signal(SIGTTIN, susp); signal(SIGTTOU, susp); signal(SIGTSTP, susp); curattr = saveattr; curattr.c_lflag |= ISIG; curattr.c_lflag &= ~(ECHO|ICANON|IEXTEN); curattr.c_iflag &= ~(IXON|IXOFF); curattr.c_cc[VMIN] = 1; curattr.c_cc[VTIME] = 0; /* This call might cause STGTTOU */ tcsetattr(0, TCSANOW, &curattr); } #endif void cleanup(int sig) { sigset_t mask; cleandir(); /* clean up embedded files and its dir */ #ifdef TTY_KEYINPUT if (ttykey_enable) tcsetattr(0, TCSANOW, &saveattr); #endif signal(SIGTERM, SIG_IGN); kill(0, SIGTERM); /*kill all of my kids*/ if (tsfile) unlink(tsfile); if (sig > 0) { signal(sig, SIG_DFL); sigemptyset(&mask); sigaddset(&mask, sig); sigprocmask(SIG_UNBLOCK, &mask, NULL); kill(mypid, sig); } finish_win(); /* finish X connection */ exit(-sig); } int main(argc, argv) int argc; char **argv; { int i, j; /* counters */ int tmp_argc; /* number of current arguments */ char **tmp_argv; /* manipulated 'argv' */ int opt; extern char *optarg; extern int optind; char *progname; int start_page = 1; char buf[BUFSIZ], *p, *p2; extern char *Paths[]; extern int NumPaths; #ifdef VFLIB char *vfont_name = VFONT; #endif char *ep; #ifdef USE_SETLOCALE setlocale(LC_CTYPE, ""); #endif progname = argv[0]; /* secure by default. If you need fork/exec, use -U */ mgp_flag |= FL_NOFORK; if ((p = getenv("MGPWDIR")) != NULL) { mgpwdir = p; } /* * check for the argument '--title' to set the window's title: * go through the whole 'argv' and cut off this option since it * will not be accepted by getopt() * default title is 'MagicPoint' */ tmp_argv=(char**)malloc(argc*sizeof(char*)); tmp_argc=argc; i=j=0; while (i < argc) { if (strcmp(argv[i], "--title") == 0) { tmp_argc--; if (++i < argc) { mgp_wname=strdup(argv[i]); tmp_argc--; } } else tmp_argv[j++]=strdup(argv[i]); i++; } /* set title to default if not set by user */ if (argc-1 <= tmp_argc) mgp_wname=strdup("MagicPoint"); argv=tmp_argv; argc=tmp_argc; #define ACCEPTOPTS "Bd:vVob:c:eg:f:hlGp:qt:Q:PSUT:D:CORw:X:x:nF:E:" while ((opt = getopt(argc, argv, ACCEPTOPTS)) != -1) { #undef ACCEPTOPTS switch (opt) { case 'B': mgp_flag |= FL_BIMAGE; break; case 'd': mgp_flag |= FL_DEMO; if (isdigit(optarg[0])) demointerval = atoi(optarg); else optind --; break; case 'V': mgp_flag |= FL_VERBOSE; verbose++; break; case 'l': mgp_flag |= FL_OUTLINE; break; case 'G': pg_mode = 1; break; case 'o': mgp_flag |= FL_OVER; break; #ifdef VFLIB case 'c': vfcap_name = optarg; break; case 'f': vfont_name = optarg; break; #endif /*VFLIB*/ case 'b': back_clname = optarg; break; case 'p': ep = NULL; start_page = atoi(optarg); if (start_page <= 0) { mgp_usage(progname); /*NOTREACHED*/ } break; case 'e': mgp_flag |= FL_GLYPHEDGE; break; case 'g': xgeometry = optarg; mgp_flag |= FL_OVER; /* -g implies -o */ break; case 'q': mgp_flag |= FL_NOBEEP; break; case 't': t_fin = atoi(optarg); tbar_mode = 1; break; case 'Q': b_quality[caching] = atoi(optarg); quality_flag = 1; break; case 'P': parse_debug++; break; case 'S': mgp_flag |= FL_NOFORK; break; case 'U': mgp_flag &= ~FL_NOFORK; break; case 'T': tsfile = optarg; break; case 'D': dumpdir = optarg; break; case 'C': mgp_flag |= FL_PRIVATE; break; case 'O': mgp_flag |= FL_NODECORATION | FL_OVER; /* -O implies -o */ break; case 'R': mgp_flag |= FL_NOAUTORELOAD; break; case 'w': mgpwdir = optarg; break; case 'X': gsdevice = optarg; break; case 'x': if (strcmp(optarg, "vflib") == 0) mgp_flag |= FL_NOVFLIB; else if (strcmp(optarg, "freetype") == 0) mgp_flag |= FL_NOFREETYPE; else if (strcmp(optarg, "xft") == 0) mgp_flag |= FL_NOXFT; else if (strcmp(optarg, "m17n") == 0) mgp_flag |= FL_NOM17N; else { fprintf(stderr, "unknown rendering engine %s\n", optarg); mgp_usage(progname); /*NOTREACHED*/ } break; case 'v': mgp_show_version(progname); /*NOTREACHED*/ case 'n': mgp_flag |= FL_NOSTDIN; break; case 'F': mgp_flag |= FL_FRDCACHE; p = optarg; if ((p2 = strsep(&p, ","))) cache_mode = atoi(p2); if ((p2 = strsep(&p, ","))) cache_effect = atoi(p2); if ((p2 = strsep(&p, ","))) cache_value = atoi(p2); break; case 'E': htmlimage = optarg; break; case 'h': default: mgp_usage(progname); /*NOTREACHED*/ } } argc -= optind; argv += optind; mypid = getpid(); setpgid(mypid, mypid); signal(SIGHUP, cleanup); signal(SIGINT, cleanup); signal(SIGQUIT, cleanup); signal(SIGTERM, cleanup); if (argc != 1) { mgp_usage(progname); /*NOTREACHED*/ } mgp_fname = argv[0]; { struct stat sb; srctimestamp = (time_t) 0; if (0 <= stat(mgp_fname, &sb)) srctimestamp = sb.st_ctime; } init_win1(xgeometry); strlcpy(buf, mgp_fname, sizeof(buf)); if ((p = rindex(buf, '/'))) { *p = '\0'; Paths[NumPaths++]= expandPath(buf); } loadPathsAndExts(); load_file(mgp_fname); if (parse_error) exit(-1); init_win2(); #ifdef VFLIB vfc_setfont(vfont_name); #endif signal(SIGCHLD, waitkids); if (dumpdir) genhtml(start_page); else if (mgp_flag & FL_DEMO) { struct render_state state; memset(&state, 0, sizeof(struct render_state)); state.target = window; /*XXX*/ state.width = window_width; state.height = window_height; while (start_page <= maxpage) { state_goto(&state, start_page, 0); draw_page(&state, NULL); start_page++; sleep(demointerval); /*XXX*/ } } else { init_win3(); main_loop(start_page); } cleanup(0); /* never returns */ exit(0); /* avoid warnings */ /*NOTREACHED*/ } static void genhtml(start_page) int start_page; { struct render_state state; char buf[BUFSIZ]; int fd; FILE *html; FILE *txt; int page; char *childdebug; char *convdb[][3] = {{ "jpg", "cjpeg", "djpeg" }, { "png", "pnmtopng", "pngtopnm" }, { NULL, NULL, NULL }}; int inum = 0; #ifdef USE_M17N /* initaiize M17N library */ M17N_init(); #endif /* check image type */ if (htmlimage) { for (inum = 0; *convdb[inum] != NULL; inum++) { if (strcmp(*convdb[inum], htmlimage) == 0) break; } if (*convdb[inum] == NULL) { fprintf(stderr, "unknwon image type %s.\n", htmlimage); /* print out valid image types */ fprintf(stderr, "Valid image types: "); for (inum = 0; *convdb[inum] != 0; inum++) { fprintf(stderr, "%s ", *convdb[inum] ); } fprintf(stderr,"\n"); cleanup(-1); } } /* check if we can write to the directory */ sprintf(buf, "%s/%ld", dumpdir, (long)time((time_t *)NULL)); fd = open(buf, O_WRONLY | O_CREAT, 0644); if (fd < 0) { fprintf(stderr, "bad dump directory %s.\n", dumpdir); cleanup(-1); } close(fd); unlink(buf); memset(&state, 0, sizeof(struct render_state)); state.target = window; /*XXX*/ state.width = window_width; state.height = window_height; childdebug = parse_debug ? "" : "2>&-"; for (page = start_page; page <= maxpage; page++) { fprintf(stderr, "generating page %d... ", page); state_goto(&state, page, 0); draw_page(&state, NULL); #define EXT convdb[inum][0] /* * dump out image */ fprintf(stderr, "(full image)"); sprintf(buf, "xwintoppm -silent -name MagicPoint | " "%s %s > %s/mgp%05d.%s", convdb[inum][1], childdebug, dumpdir, page, EXT); if (system(buf) < 0){ /*XXX security hole*/ fprintf(stderr, "system() failed for %s", buf); exit(-1); } fprintf(stderr, "(thumbnail)"); sprintf(buf, "%s %s/mgp%05d.%s | " "pnmscale 0.25 | %s %s > %s/mgp%05d.idx.%s", convdb[inum][2], dumpdir, page, EXT, convdb[inum][1], childdebug, dumpdir, page, EXT); if (system(buf) < 0){ /*XXX security hole*/ fprintf(stderr, "system() failed for %s", buf); exit(-1); } /* * dump out html file */ fprintf(stderr, "(html)"); sprintf(buf, "%s/mgp%05d.html", dumpdir, page); html = fopen(buf, "w"); if (!html) continue; fprintf(html, "\n" "MagicPoint presentation foils\n" "\n"); fprintf(html, "[index] " "[text page] ", page); if (1 < page) { fprintf(html, "[<<start] " "[<prev] ", 1, page - 1); } else fprintf(html, "[<<start] [<prev] "); if (page < maxpage) { fprintf(html, "[next>] " "[last>>]\n", page + 1, maxpage); } else fprintf(html, "[next>] [last>>]\n"); fprintf(html, "
Page %d: %s
\n", page, page_title(page)); fprintf(html, "
\n"); if (window_width < 0 || window_height < 0) { fprintf(html, "\n", page, EXT, page); } else { fprintf(html, "\"Page
\n", page, EXT, window_width, window_height, page); } fprintf(html, "
Generated by " "MagicPoint\n" "\n"); fclose(html); /* * dump out text file */ fprintf(stderr, "(txt)\n"); sprintf(buf, "%s/mgp%05d.txt", dumpdir, page); txt = fopen(buf, "w"); if (!txt) continue; state_goto(&state, page, 0); state_init(&state); while (1) { if (state.phase == P_NONE || state.phase == P_END) break; if (!state.cp) { state_next(&state); continue; } switch (state.cp->ct_op) { case CTL_PAUSE: if (state.cp->cti_value) goto txtdone; break; case CTL_TEXT: if (state.cp->ctc_value) fprintf(txt, "%s", state.cp->ctc_value); break; case CTL_LINEEND: fprintf(txt, "\n"); break; } state_next(&state); } txtdone:; fclose(txt); } fprintf(stderr, "generating top page... "); sprintf(buf, "%s/index.html", dumpdir); html = fopen(buf, "w"); if (!html) { fprintf(stderr, "could not generate top page\n"); cleanup(-1); } fprintf(stderr, "\n"); fprintf(html, "\n" "MagicPoint presentation foils\n" "\n"); for (page = start_page; page <= maxpage; page++) { if (window_width < 0 || window_height < 0) { fprintf(html, "" "\n", page, page, EXT, page); } else { fprintf(html, "" "\n", page, page, EXT, window_width / 4, window_height / 4, page); } } fprintf(html, "
\n"); fprintf(html, "Generated by " "" "MagicPoint\n"); fprintf(html, "
\n\n"); fclose(html); } static void mgp_show_version(name) char *name; { char *p; if ((p = strrchr(name, '/'))) p ++; else p = name; fprintf(stderr, "%s version %s\n", p, mgp_version); exit(0); } static void mgp_usage(name) char *name; { fprintf(stderr, "Usage: %s [opts] mgpfile\n", name); fprintf(stderr, "\t-b : Specify background color\n"); #ifdef VFLIB fprintf(stderr, "\t-c : Specify vfontcap file\n"); #endif /*VFLIB*/ fprintf(stderr, "\t-d: Demo mode - go through the presentation\n"); #ifdef VFLIB fprintf(stderr, "\t-f : Specify default vflib font\n"); #endif /*VFLIB*/ fprintf(stderr, "\t-g : Set window geometry\n"); fprintf(stderr, "\t-h: Display this help message\n"); #ifdef VFLIB fprintf(stderr, "\t-l: Do not use outline font on vflib\n"); #endif /*VFLIB*/ fprintf(stderr, "\t-n: Disables control key input from tty\n"); fprintf(stderr, "\t-o: Do not override the window manager\n"); fprintf(stderr, "\t-p : Start at the specified page\n"); fprintf(stderr, "\t-q Do not beep on errors\n"); fprintf(stderr, "\t-t : Enable presentation timer\n"); fprintf(stderr, "\t-v: Show version number and quit\n"); fprintf(stderr, "\t-w : Specify a working directory\n"); fprintf(stderr, "\t-x : Disable specified rendering engine\n"); fprintf(stderr, "\t-B: Ignore background image\n"); fprintf(stderr, "\t-C: Use private colormap\n"); fprintf(stderr, "\t-D : Generate html pages for the presentation\n"); fprintf(stderr, "\t-E : Use this image format in html (jpg or png)\n"); fprintf(stderr, "\t-F,,: Use forwarding caches\n"); fprintf(stderr, "\t-G: Page guide is on\n"); fprintf(stderr, "\t-O: Obey to the window manager\n"); fprintf(stderr, "\t-Q : Set background image quality(0-100)\n"); fprintf(stderr, "\t-R: Do not perform automatic reload\n"); fprintf(stderr, "\t-S: Do not process directives that forks process (default)\n"); fprintf(stderr, "\t-U: Do process directives that forks process\n\t or allow to use non-ASCII filenames (unsecure mode)\n"); fprintf(stderr, "\t-T : Update timestampfile on page refresh\n"); fprintf(stderr, "\t-P: print stderr from image conversion tools (by default it's discarded)\n"); fprintf(stderr, "\t-V: Be verbose\n"); fprintf(stderr, "\t-X : ghostscript device to use\n"); fprintf(stderr, "\t--title : Set window title\n"); exit(0); } static void beep() { if (!(mgp_flag & FL_NOBEEP)) XBell(display, 0); } static void main_loop(start_page) u_int start_page; { XEvent e, ahead; KeySym key; u_int i; u_int number = 0; u_int shift = 0; u_int control = 0; static struct render_state state; static Cursor pen_curs; u_int prevpage; memset(&state, 0, sizeof(struct render_state)); state.target = window; /*XXX*/ state.width = window_width; state.height = window_height; if (!pen_curs) { pen_curs = XCreateFontCursor(display, XC_pencil); rakugaki_updatecolor(pen_curs); } state_goto(&state, start_page, 0); #if 0 /* be conservative about first page... */ draw_page(&state, NULL); #endif if (pg_mode) { pg_on(); pg_draw(&state); XFlush(display); } #ifdef TTY_KEYINPUT if (!(mgp_flag & FL_NOSTDIN)) { if (tcgetattr(0, &saveattr) < 0) mgp_flag |= FL_NOSTDIN; else try_enable_ttykey(); } #endif while (1) { if (!t_start && 1 < state.page) t_start = time(NULL); if (rakugaki) XDefineCursor(display, window, pen_curs); else XUndefineCursor(display, window); do { ; /*nothing*/ } while (draw_one(&state, &e) == False); if (t_fin) timebar(&state); #if 0 /* last page reload bug fix? */ if (state.cp && state.cp->ct_op == CTL_PAUSE) { #else if ((state.cp && state.cp->ct_op == CTL_PAUSE) || (state.page == maxpage)){ #endif if (tsfile) { int fd; fd = open(tsfile, O_WRONLY|O_TRUNC|O_CREAT, 0644); if (fd < 0) { fprintf(stderr, "timestamp file %s " "write failed; " "timestamp turning off\n", tsfile); tsfile = NULL; } else { if (write(fd, &state, sizeof(state)) < 0) fprintf(stderr, "timestamp file write failed\n"); close(fd); } } if (wantreload()) { draw_reinit(&state); cleanup_file(); load_file(mgp_fname); if (maxpage < state.page) state.page = 1; state_goto(&state, state.page, 1); /*repaint*/ } } prevpage = state.page; switch (e.type) { case EnterNotify: for (i = 1; i <= maxpage; i++) { if (e.xany.window == plwin[i]) { XClearWindow(display, plwin[i]); pl_pdraw(&state, i, gc_plrev); pl_title(i); } } XFlush(display); break; case LeaveNotify: for (i = 1; i <= maxpage; i++) { if (e.xany.window == plwin[i]) { XClearWindow(display, plwin[i]); pl_pdraw(&state, i, gc_pl); pl_title(0); } } XFlush(display); break; case ButtonPress: if (rakugaki && e.xany.window == window) { rakugaki_update(&state, &e); break; } for (i = 1; i <= maxpage; i++) { if (e.xany.window == plwin[i]) { state_goto(&state, i, 0); pl_off(&state); break; } } if (e.xany.window == window) { if (e.xbutton.button == 1) { struct render_state tstate; tstate = state; if (!shift && state.cp && state.cp->ct_op == CTL_PAUSE) { state_next(&tstate); } else if (state.page + 1 < maxpage) { state_goto(&tstate, state.page + 1, 0); } else { beep(); break; } if (memcmp(&state, &tstate, sizeof(state)) != 0) { state = tstate; } else { /* cannot make a progress */ beep(); } } else if (e.xbutton.button == 3) { if (state.page > 1) { state_goto(&state, state.page - 1, 0); } else beep(); } } break; case ButtonRelease: if (rakugaki && e.xany.window == window) rakugaki_update(&state, &e); if (e.xbutton.button == 2) goto rakugaki_toggle; break; case MotionNotify: if (!rakugaki) break; if (e.xany.window == window) rakugaki_update(&state, &e); break; case KeyPress: key = XLookupKeysym((XKeyEvent *)&e, 0); switch (key) { case XK_q: case XK_Escape: return; /*NOTREACHED*/ case XK_f: case XK_j: case XK_n: case XK_Down: case XK_Next: case XK_space: { struct render_state tstate; tstate = state; if (number == 0 && state.cp && state.cp->ct_op == CTL_PAUSE) { state_next(&tstate); } else { if (number == 0) number = 1; if (state.page + number <= maxpage) { state_goto(&tstate, state.page + number, 0); } else { beep(); break; } } if (memcmp(&state, &tstate, sizeof(state)) != 0) { state = tstate; } else { /* cannot make a progress */ beep(); } number = 0; break; } case XK_b: case XK_k: case XK_p: case XK_Up: case XK_Prior: case XK_BackSpace: case XK_Delete: if (number == 0) number = 1; if (state.page - number >= 1) { state_goto(&state, state.page - number, 0); } else beep(); number = 0; break; case XK_x: if (shift) { rakugaki_color++; rakugaki_updatecolor(pen_curs); XUndefineCursor(display, window); XDefineCursor(display, window, pen_curs); XFlush(display); } else { rakugaki_toggle: rakugaki = 1 - rakugaki; rakugaki_x = rakugaki_y = -1; if (rakugaki) { XDefineCursor(display, window, pen_curs); } else { XUndefineCursor(display, window); } XFlush(display); } break; case XK_t: if (tbar_mode) tbar_mode = 0; else { if (t_fin) tbar_mode = 1; } break; case XK_Control_L: case XK_Control_R: pl_on(&state); control = 1; number = 0; break; case XK_Shift_L: case XK_Shift_R: shift = 1; number = 0; break; case XK_r: if (control) { reload: pl_off(&state); cached_page = 0; reset_background_pixmap(); draw_reinit(&state); cleanup_file(); load_file(mgp_fname); if (maxpage < state.page) state.page = 1; state_goto(&state, state.page, 1); goto repaint; } break; case XK_l: /* used to be control-L */ repaint:; { struct ctrl *lastcp; lastcp = state.cp; state.repaint = 1; state_goto(&state, state.page, 1); draw_page(&state, lastcp); state.repaint = 0; number = 0; } break; case XK_g: if (shift) { pg_mode = 1 - pg_mode; if (pg_mode) { pg_on(); pg_draw(&state); } else pg_off(); } else { if (number == 0) number = maxpage; if (number <= maxpage && number > 0) { state_goto(&state, number, 0); state_newpage(&state); state_init(&state); } else beep(); } number = 0; break; case XK_0: case XK_1: case XK_2: case XK_3: case XK_4: case XK_5: case XK_6: case XK_7: case XK_8: case XK_9: number = number * 10 + key - XK_0; break; case XK_c: if (verbose) if (mgp_flag & FL_FRDCACHE) printf("turn off forward cache\n"); else printf("turn on forward cache\n"); mgp_flag ^= FL_FRDCACHE; break; case XK_a: XCopyArea(display, cachewin, window, gc_cache, 0, 0, window_width, window_height, 0, 0); break; case XK_w: toggle_fullscreen(); break; default: number = 0; break; } break; case KeyRelease: key = XLookupKeysym((XKeyEvent *)&e, 0); switch (key) { case XK_Control_L: case XK_Control_R: pl_off(&state); control = 0; break; case XK_Shift_L: case XK_Shift_R: shift = 0; break; } break; case Expose: if (e.xexpose.window != window) break; if (state.repaint) break; /* compress expose event */ while (XEventsQueued(display, QueuedAfterReading) > 0) { XPeekEvent(display, &ahead); if (ahead.type != Expose && ahead.type != ConfigureNotify) break; if (ahead.xexpose.window != window) break; XNextEvent(display, &e); } if (wantreload()) goto reload; if ((state.cp && state.cp->ct_op == CTL_PAUSE) || (state.page == maxpage)) goto repaint; break; case ConfigureNotify: if ((e.xconfigure.window != window) && ((mgp_flag & FL_OVER) || e.xconfigure.window != RootWindow(display, screen))) break; /* compress expose event */ while (XEventsQueued(display, QueuedAfterReading) > 0) { XPeekEvent(display, &ahead); if (ahead.type != Expose && ahead.type != ConfigureNotify) break; if (ahead.xconfigure.window != window) break; XNextEvent(display, &e); } if (window_width != e.xconfigure.width || window_height != e.xconfigure.height) { struct ctrl *lastcp; if (!(mgp_flag & FL_OVER)) XMoveResizeWindow(display, window, 0, 0, e.xconfigure.width, e.xconfigure.height); if (pg_mode) pg_off(); pl_off(&state); window_width = e.xconfigure.width; window_height = e.xconfigure.height; state.width = e.xconfigure.width; state.height = e.xconfigure.height; if (mgp_flag & FL_FRDCACHE) { cached_page = 0; reset_background_pixmap(); } reset_background_pixmap(); draw_reinit(&state); /*notify*/ lastcp = state.cp; state_goto(&state, state.page, 1); draw_page(&state, lastcp); } if (pg_mode) { pg_on(); pg_draw(&state); } if (wantreload()) goto reload; break; } /* page may have changed... */ if (pg_mode) pg_draw(&state); if (prevpage != state.page) { pl_pdraw(&state, prevpage, gc_pl); pl_pdraw(&state, state.page, gc_plrev); pl_title(state.page); } if (state.phase == P_END) { if (state.page < maxpage) state_goto(&state, state.page + 1, 0); } } } static void rakugaki_update(state, e) struct render_state *state; XEvent *e; { int x, y; if (e->type == MotionNotify) { XMotionEvent *em; em = (XMotionEvent *)e; x = em->x; y = em->y; } else if (e->type == ButtonPress) { XButtonPressedEvent *eb; eb = (XButtonPressedEvent *)e; x = eb->x; y = eb->y; if (e->xbutton.button != 1) return; } else { rakugaki_x = rakugaki_y = -1; return; } if (rakugaki_x < 0 || rakugaki_y < 0) XDrawRectangle(display, state->target, gcpen, x, y, 1, 1); else { XDrawLine(display, state->target, gcpen, x, y, rakugaki_x, rakugaki_y); XDrawLine(display, state->target, gcpen, x + 1, y, rakugaki_x + 1, rakugaki_y); XDrawLine(display, state->target, gcpen, x, y + 1, rakugaki_x, rakugaki_y + 1); XDrawLine(display, state->target, gcpen, x + 1, y + 1, rakugaki_x + 1, rakugaki_y + 1); } rakugaki_x = x; rakugaki_y = y; } static void rakugaki_updatecolor(cursor) Cursor cursor; { XColor junk; int maxidx; maxidx = sizeof(rakugaki_forecolors)/sizeof(rakugaki_forecolors[0]); if (maxidx <= rakugaki_color) rakugaki_color %= maxidx; XAllocNamedColor(display, DefaultColormap(display, screen), rakugaki_forecolors[rakugaki_color], &rakugaki_fore, &junk); XAllocNamedColor(display, DefaultColormap(display, screen), rakugaki_backcolors[rakugaki_color], &rakugaki_back, &junk); /* * due to the design of the cursor, it looks more natural if we swap * the background color and foreground color. */ XRecolorCursor(display, cursor, &rakugaki_back, &rakugaki_fore); XSetForeground(display, gcpen, rakugaki_fore.pixel); } static struct { pid_t pid; void *key; Window window_id; int flag; } childtab[64]; static int childidx = 0; pid_t checkchild(key) void *key; { int i; for (i = 0; i < childidx; i++) { if (childtab[i].pid == (pid_t)-1) continue; if (childtab[i].key == key) return childtab[i].pid; } return (pid_t)-1; } Window checkchildwin(key) void *key; { int i; for (i = 0; i < childidx; i++) { if (childtab[i].pid == (pid_t)-1) continue; if (childtab[i].key == key) return childtab[i].window_id; } return (Window)-1; } void regchild(pid, key, window_id, flag) pid_t pid; void *key; Window window_id; int flag; { int i; for (i = 0; i < childidx; i++) { if (childtab[i].pid == (pid_t)-1) { childtab[i].pid = pid; childtab[i].key = key; childtab[i].window_id = window_id; childtab[i].flag = flag; return; } } childtab[childidx].pid = pid; childtab[childidx].key = key; childtab[childidx].flag = flag; childtab[childidx].window_id = window_id; childidx++; } void purgechild(flag) int flag; { int i; for (i = 0; i < childidx; i++) { if (childtab[i].pid == (pid_t)-1) continue; if (childtab[i].flag == flag){ kill(childtab[i].pid, SIGTERM); } } } static RETSIGTYPE waitkids(sig) int sig; { int status; int i; pid_t pid; if (sig != SIGCHLD) { fprintf(stderr, "signal different from expected: %d\n", sig); cleanup(-1); } while ((pid_t) 0 < (pid = waitpid(-1, &status, WNOHANG))) { for (i = 0; i < childidx; i++) { if (childtab[i].pid == pid) { childtab[i].pid = (pid_t)-1; childtab[i].window_id = (Window)-1; } } } } static int wantreload() { struct stat sb; if (mgp_flag & FL_NOAUTORELOAD) return 0; if (0 <= stat(mgp_fname, &sb)) { if (srctimestamp < sb.st_ctime) { srctimestamp = sb.st_ctime; return 1; } } return 0; } /* remap child window which was invoked by xsystem directive. this is an adhoc solution for window-maker. */ void remapchild() { int i; for (i = 0; i < childidx; i++) { if (childtab[i].window_id > 0){ XMapSubwindows(display, window); XFlush(display); return; } } return; }