eh, make ’em volatile, this is not time-critical code anyway
[alioth/magicpoint.git] / draw.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 #include "mgp.h"
30 #include <Imlib2.h>
31
32 /* state associated with the window - how should we treat this? */
33 static struct ctrl *bg_ctl, *bg_ctl_last, *bg_ctl_cache;
34 static int bgindex = 0;
35 struct render_state cache_state;
36
37 static struct pcache {
38         u_int flag;
39         u_int page;
40         u_int mgpflag;
41         u_int mode;
42         u_int effect;
43         u_int value;
44 } pcache;
45
46 #define COMPLEX_BGIMAGE (bg_ctl && \
47         ((bg_ctl->ct_op == CTL_BIMAGE) || bg_ctl->ct_op == CTL_BGRAD))
48
49 #define COMPLEX_BGIMAGE2 (0)
50
51 #define POSY(size)      (-(int)((size)/2))
52
53 static void process_direc(struct render_state *, int *);
54
55 static int set_position(struct render_state *);
56 static void draw_line_output(struct render_state *, char *);
57 static void cutin(struct render_state *, int, int, int);
58
59 static void draw_string(struct render_state *, char *);
60 static u_char *draw_fragment(struct render_state *, u_char *, size_t,
61     const char *, int);
62 static struct render_object *obj_alloc(struct render_state *state);
63 static void obj_free(struct render_state *, struct render_object *);
64 static int obj_new_xfont(struct render_state *, int, int, int,
65         u_int, const char *);
66 static int obj_new_image2(struct render_state *, int, int, Image *, int, int, Imlib_Image *, int);
67 static int obj_new_icon(struct render_state *, int, int, u_int, u_int, u_long, u_int, XPoint *);
68 static Pixel obj_image_color(Image *, Image *, Pixel, int *);
69 static Image *obj_image_trans(Image *, u_int, u_int);
70 static void obj_draw_image(Drawable, u_int, u_int, struct render_object *, int);
71 static void obj_draw(struct render_state *, Drawable, u_int, u_int);
72 static char *x_fontname(char *, int, const char *, int, const char *);
73 static int x_parsefont(char *, int *, int*);
74 static XFontStruct *x_setfont(const char *, u_int, const char *, int *);
75 static u_int draw_onechar_x(struct render_state *, u_int, int, int, int,
76         const char *, int);
77
78 static void back_gradation(struct render_state *, struct ctrl_grad *);
79 static void image_load(struct render_state *, char *, int, int, int, int, int, int, int, int, int);
80 static void image_load_ps(struct render_state *, char *, int, int, int, int, int, int, int, int, int);
81 static void process_icon(struct render_state *, struct ctrl *);
82 static void draw_bar(struct render_state *, struct ctrl *);
83 static void process_system(struct render_state *, struct ctrl *);
84 static void process_xsystem(struct render_state *, struct ctrl *);
85 static void process_tsystem(struct render_state *, struct ctrl *);
86 static Window search_child_window(void);
87 static Window tsearch_child_window(const char *name);
88 static Window getNamedWindow(const char *name, Window top);
89 static void reparent_child_window(Window, int, int);
90 static char *epstoimage(const char *, int, int, int, int,
91     volatile int, volatile int);
92 static void image_setcolor(struct render_state *);
93 static void x_registerseed(struct render_state *, char *, const char *);
94 static const char *x_findseed(struct render_state *, const char *);
95 static int get_regid(const char *);
96
97 static void XClearPixmap(Display *, Drawable);
98 static void cache_page(struct render_state *, unsigned int);
99 static void cache_effect1(void);
100 static void cache_effect2(void);
101 static void set_from_cache(struct render_state *);
102 static void pcache_process(unsigned int);
103 static void predraw(struct render_state *);
104 static void set_background_pixmap(struct ctrl *);
105 static void get_background_pixmap(struct ctrl *, struct render_state *);
106 static void regist_background_pixmap(XImageInfo *, Image *);
107 static int valign = VL_BOTTOM;
108
109 #define CHECK_CACHE     do {if (caching) {caching = -1; return; }} while (0)
110
111 static void set_xrender_color(unsigned long, int);
112 static XftDraw * xft_getdraw(Drawable);
113 static u_char *xft_draw_fragment(struct render_state *,
114     u_char *, size_t, const char *, int);
115 static int obj_new_xftfont(struct render_state *, int, int, u_char *,
116     size_t, const char *, const char *, int, int, XftFont *);
117 static XftFont * xft_setfont(const char *, int, const char *);
118 XftFont *xft_font;
119 XftDraw *xft_draw[2];
120 Drawable xft_xdraw[2];
121 XftColor xft_forecolor;
122 XRenderColor xft_render_color;
123
124 static void regist_zimage_position(struct render_object *, int, int, int, int, int);
125 static void clear_zimage(int);
126 static void clear_region(int, int, int, int);
127 #define ZIMAGENUM 100
128 static Imlib_Image *zimage[ZIMAGENUM];
129 static int zonzoom[ZIMAGENUM];
130 static int zpage[ZIMAGENUM];
131 static int zx[ZIMAGENUM];
132 static int zy[ZIMAGENUM];
133 static int zwidth[ZIMAGENUM];
134 static int zheight[ZIMAGENUM];
135
136 static int
137 ispsfilename(char *p0)
138 {
139         char *p;
140
141         p = p0;
142         while (*p)
143                 p++;
144         if (4 < p - p0 && strcasecmp(p - 4, ".eps") == 0)
145                 return 1;
146         if (3 < p - p0 && strcasecmp(p - 3, ".ps") == 0)
147                 return 1;
148         if (6 < p - p0 && strcasecmp(p - 6, ".idraw") == 0)
149                 return 1;
150         return 0;
151 }
152
153 /*
154  * state management.
155  */
156 void
157 state_goto(struct render_state *state, u_int page, int repaint)
158 {
159         if (!repaint) {
160                 purgechild(state->page);
161                 clear_zimage(state->page);
162         }
163
164         state->page = page;
165         state->line = 0;
166         state->cp = NULL;
167         state->phase = P_NONE;
168         free_alloc_colors(&image_clr);
169         free_alloc_colors(&font_clr);
170
171         colormap = XCopyColormapAndFree(display, colormap);
172         predraw(state);
173 }
174
175 void
176 state_next(struct render_state *state)
177 {
178
179         switch (state->phase) {
180         case P_NONE:
181                 fprintf(stderr, "internal error\n");
182                 break;
183         case P_DEFAULT:
184                 if (state->cp)
185                         state->cp = state->cp->ct_next;
186                 if (!state->cp) {
187                         state->cp = page_control[state->page][state->line];
188                         state->phase = P_PAGE;
189                 }
190                 break;
191         case P_PAGE:
192                 if (state->cp)
193                         state->cp = state->cp->ct_next;
194                 if (!state->cp) {
195                         state->line++;
196                         state->cp = NULL;
197                         state->phase = P_NONE;
198                         state_init(state);
199                 }
200                 break;
201
202         case P_END:
203                 /*nothing*/
204                 break;
205         }
206
207         /* next page */
208         if (page_attribute[state->page].pg_linenum < state->line) {
209                 if (state->page < maxpage) {
210                         purgechild(state->page);
211                         clear_zimage(state->page);
212                         if (mgp_flag & FL_FRDCACHE &&
213                                 cached_page == state->page + 1) {
214                                         /* Hit cache */
215                                         set_from_cache(state);
216                                         pcache_process(state->page);
217                                         cache_hit = 1;
218                         } else {
219                                 state->phase = P_NONE;
220                                 state->page++;
221                                 state->line = 0;
222                                 state_newpage(state);
223                                 state_init(state);
224                         }
225                 } else
226                         state->phase = P_END;
227         }
228 }
229
230 void
231 state_init(struct render_state *state)
232 {
233         assert(state);
234
235         if (state->phase == P_NONE || !state->cp) {
236                 state->cp = page_control[state->page][state->line];
237                 state->phase = P_PAGE;
238         }
239 }
240
241 void
242 state_newpage(struct render_state *state)
243 {
244         state->ypos = 0;
245         state->have_mark = 0;
246         state->charoff = 0;
247         char_size[caching] = nonscaled_size[caching];
248         free_alloc_colors(&image_clr);
249         free_alloc_colors(&font_clr);
250
251         colormap = XCopyColormapAndFree(display, colormap);
252         predraw(state);
253 }
254
255 /*
256  * page management.
257  */
258 void
259 draw_page(struct render_state *state, struct ctrl *lastcp)
260 {
261         int pause2;
262
263         assert(state);
264
265         /* initialize the state, if required. */
266         if (state->phase != P_END && (state->phase == P_NONE || !state->cp)) {
267                 state_newpage(state);
268                 state_init(state);
269         }
270
271         while (1) {
272                 switch (state->phase) {
273                 case P_NONE:
274                         fprintf(stderr, "internal error\n");
275                         cleanup(-1);
276                         /* NOTREACHED */
277                 case P_DEFAULT:
278                 case P_PAGE:
279                         pause2 = 0;
280                         if (state->cp)
281                                 process_direc(state, &pause2);
282                         if (caching == -1) {
283                                 /* caching failed */
284                                 caching = 0;
285                                 return;
286                         }
287                         if (lastcp && state->cp == lastcp)
288                                 goto done;
289                         if (pause2) {
290                                 if (state->cp
291                                  && state->cp->ct_op == CTL_PAUSE
292                                  && state->cp->cti_value) {
293                                         goto done;
294                                 }
295                         }
296                         break;
297                 case P_END:
298                         goto done;
299                 }
300                 state_next(state);
301         }
302 done:
303         XFlush(display);
304 }
305
306 Bool
307 draw_one(struct render_state *state, XEvent *e)
308 {
309         int pause2;
310         fd_set fds;
311         int xfd;
312         struct timeval tout;
313         long emask;
314 #ifdef TTY_KEYINPUT
315         KeySym ks;
316         char c;
317 #endif
318
319         assert(state);
320
321         /* initialize the state, if required. */
322         if (state->phase != P_END && (state->phase == P_NONE || !state->cp)) {
323                 state_newpage(state);
324                 state_init(state);
325         }
326
327         switch (state->phase) {
328         case P_DEFAULT:
329         case P_PAGE:
330                 pause2 = 0;
331                 if (state->cp)
332                         process_direc(state, &pause2);
333                 break;
334         case P_END:
335                 break;
336         case P_NONE:
337         default:
338                 fprintf(stderr, "internal error\n");
339                 cleanup(-1);
340         }
341         xfd = ConnectionNumber(display);
342         if (state->phase != P_END && !pause2)
343                 emask = xeventmask;
344         else
345                 emask = ~NoEventMask;
346         for (;;) {
347                 if (XCheckMaskEvent(display, emask, e) == True) {
348                         /* we got some event in the queue*/
349                         if (2 <= parse_debug) {
350                                 fprintf(stderr,
351                                         "interrupted and "
352                                         "got X11 event type=%d\n",
353                                         e->type);
354                         }
355   got_event:
356                         if (state->phase == P_END)
357                                 XFlush(display);
358                         else if (!pause2)
359                                 state_next(state);
360                         return True;
361                 }
362 #ifdef TTY_KEYINPUT
363                 if (ttykey_enable) {
364                         FD_ZERO(&fds);
365                         FD_SET(0, &fds);
366                         tout.tv_sec = tout.tv_usec = 0;
367                         if (select(1, &fds, NULL, NULL, &tout) > 0
368                         &&  read(0, &c, sizeof(c)) == sizeof(c)) {
369                                 if (c > 0 && c < ' ')
370                                         ks = 0xff00 | c;
371                                 else if (c >= ' ' && c < '\177')
372                                         ks = c;
373                                 else if (c == '\177')
374                                         ks = XK_Delete;
375                                 else
376                                         continue;
377                                 e->xkey.display = display;
378                                 e->xkey.type = KeyPress;
379                                 e->xkey.keycode = XKeysymToKeycode(display, ks);
380                                 if (e->xkey.keycode == 0)
381                                         continue;
382                                 goto got_event;
383                         }
384                 }
385 #endif
386                 if (state->phase != P_END && !pause2) {
387                         state_next(state);
388                         return False;
389                 }
390                 FD_ZERO(&fds);
391                 FD_SET(xfd, &fds);
392 #ifdef TTY_KEYINPUT
393                 if (ttykey_enable)
394                         FD_SET(0, &fds);
395 #endif
396                 remapchild();
397                 /* always cache next page */
398                 if ((mgp_flag & FL_FRDCACHE) && cache_mode) {
399                         if (XCheckMaskEvent(display, emask, e) == True)
400                                 goto got_event;
401                         cache_page(&cache_state, state->page + 1);
402                         /* check if we got some events during caching */
403                         if (XCheckMaskEvent(display, emask, e) == True)
404                                 goto got_event;
405                 }
406
407                 /* wait for something */
408                 tout.tv_sec = 2;
409                 tout.tv_usec = 0;
410                 (void)select(xfd + 1, &fds, NULL, NULL, &tout);
411
412 #ifdef TTY_KEYINPUT
413                 if (!(mgp_flag & FL_NOSTDIN) && !ttykey_enable)
414                         try_enable_ttykey();
415 #endif
416                 /* we have no event in 2sec, so..*/
417                 if (!FD_ISSET(xfd, &fds)) {
418                         if ((mgp_flag & FL_FRDCACHE) && !cache_mode)
419                                 cache_page(&cache_state, state->page + 1);
420                         timebar(state);
421                         e->type = 0;
422                         return True;
423                 }
424         }
425         /*NOTREACHED*/
426 }
427
428 static void
429 process_direc(struct render_state *state, int *seenpause)
430 {
431         struct ctrl *cp;
432
433         if (seenpause)
434                 *seenpause = 0;
435         cp = state->cp;
436
437         if (2 <= parse_debug) {
438                 fprintf(stderr, "p%d/l%d: ", state->page, state->line);
439                 debug0(cp);
440         }
441
442         switch(cp->ct_op) {
443         case CTL_SUP:
444                 if (sup_scale > 1.0 || sup_scale < 0.1) {
445                         sup_scale = DEFAULT_SUPSCALE;
446                 }
447                 if (sup_off > 1.0 || sup_scale < 0.1) {
448                         sup_off = DEFAULT_SUPOFF;
449                 }
450                 state->charoff = -sup_off * nonscaled_size[caching];
451                 char_size[caching] = (int)(nonscaled_size[caching] * sup_scale);
452                 break;
453         case CTL_SUB:
454                 if (sup_scale > 1.0 || sup_scale < 0.1) {
455                         sup_scale = DEFAULT_SUPSCALE;
456                 }
457                 if (sub_off > 1.0 || sub_off < 0.1){
458                         sub_off = DEFAULT_SUBOFF;
459                 }
460                 state->charoff = sub_off * nonscaled_size[caching];
461                 char_size[caching] = (int)(nonscaled_size[caching] * sup_scale);
462                 break;
463         case CTL_SETSUP:
464                 if (cp->cti3_value1 > 100 || cp->cti3_value1 < 10){
465                         sup_off = DEFAULT_SUPOFF;
466                 } else {
467                         sup_off = cp->cti3_value1 / 100.;
468                 }
469                 if (cp->cti3_value2 > 100 || cp->cti3_value2 < 10){
470                         sub_off = DEFAULT_SUBOFF;
471                 } else {
472                         sub_off = cp->cti3_value2 / 100.;
473                 }
474                 if (cp->cti3_value3 > 100 || cp->cti3_value3 < 10){
475                      sup_scale = DEFAULT_SUPSCALE;
476                 } else {
477                      sup_scale = cp->cti3_value3 / 100.;
478                 }
479                 break;
480         case CTL_SIZE:
481                 nonscaled_size[caching] = state->height * cp->ctf_value / 100;
482                 char_size[caching] = nonscaled_size[caching];
483                 break;
484
485         case CTL_VGAP:
486                 vert_gap[caching] = cp->cti_value;
487                 break;
488
489         case CTL_HGAP:
490                 horiz_gap[caching] = cp->cti_value;
491                 break;
492
493         case CTL_GAP:
494                 vert_gap[caching] = horiz_gap[caching] = cp->cti_value;
495                 break;
496
497         case CTL_QUALITY:
498                 if (!quality_flag)
499                         b_quality[caching] = cp->cti_value;
500                 break;
501
502         case CTL_PAUSE:
503                 CHECK_CACHE;
504                 if (seenpause)
505                         *seenpause = 1;
506                 break;
507
508         case CTL_AGAIN:
509                 CHECK_CACHE;
510                 if (state->have_mark)
511                         state->ypos = state->mark_ypos;
512                 state->have_mark = 0;
513                 break;
514
515         case CTL_FORE:
516                 fore_color[caching] = cp->ctl_value;
517                 XSetForeground(display, gcfore, fore_color[caching]);
518                 break;
519
520         case CTL_BACK:
521                 if (state->line){
522                         fprintf(stderr, "warning: %%back directive should be put in the first line of the page. ignored.\n");
523                         break;
524                 }
525                 back_color[caching] = cp->ctl_value;
526                 bg_ctl = cp;    /*update later*/
527                 break;
528
529         case CTL_CCOLOR:
530                 ctrl_color[caching] = cp->ctl_value;
531                 break;
532
533         case CTL_CENTER:
534                 state->align = AL_CENTER;
535                 break;
536
537         case CTL_LEFT:
538                 state->align = AL_LEFT;
539                 break;
540
541         case CTL_LEFTFILL:
542                 state->align = AL_LEFTFILL0;
543                 break;
544
545         case CTL_RIGHT:
546                 state->align = AL_RIGHT;
547                 break;
548
549         case CTL_CONT:
550                 state->charoff = 0;
551                 char_size[caching] = nonscaled_size[caching];
552                 break;
553
554         case CTL_XFONT2:
555                 x_registerseed(state, cp->ctc2_value1, cp->ctc2_value2);
556                 break;
557
558         case CTL_BAR:
559                 draw_bar(state, cp);
560                 break;
561
562         case CTL_IMAGE:
563             {
564                 if (state->align == AL_LEFTFILL0) {
565                         state->align = AL_LEFTFILL1;
566                         state->leftfillpos = state->linewidth;
567                 }
568
569                 /* quickhack for postscript */
570                 if (ispsfilename(cp->ctm_fname)) {
571                         image_load_ps(state, cp->ctm_fname, cp->ctm_numcolor,
572                                 cp->ctm_ximagesize, cp->ctm_yimagesize, 0,
573                                 cp->ctm_zoomflag, 0, cp->ctm_raise, cp->ctm_rotate, cp->ctm_zoomonclk);
574                 } else {
575                         image_load(state, cp->ctm_fname, cp->ctm_numcolor,
576                                 cp->ctm_ximagesize, cp->ctm_yimagesize, 0,
577                                 cp->ctm_zoomflag, 0, cp->ctm_raise, cp->ctm_rotate, cp->ctm_zoomonclk);
578                 }
579                 state->brankline = 0;
580             }
581                 break;
582
583         case CTL_BIMAGE:
584                 if (mgp_flag & FL_BIMAGE)
585                         break;
586                 bg_ctl = cp;    /*update later*/
587                 break;
588
589         case CTL_BGRAD:
590                 if (mgp_flag & FL_BIMAGE)
591                         break;
592                 bg_ctl = cp;    /*update later*/
593                 break;
594
595         case CTL_LCUTIN:
596                 CHECK_CACHE;
597                 state->special = SP_LCUTIN;
598                 break;
599
600         case CTL_RCUTIN:
601                 CHECK_CACHE;
602                 state->special = SP_RCUTIN;
603                 break;
604
605         case CTL_SHRINK:
606                 CHECK_CACHE;
607                 state->special = SP_SHRINK;
608                 break;
609
610         case CTL_PREFIX:
611                 state->curprefix = cp->ctc_value;
612                 break;
613
614         case CTL_PREFIXN:
615                 state->xprefix = state->width * cp->ctf_value / 100;
616                 break;
617
618         case CTL_TABPREFIX:
619                 state->tabprefix = cp->ctc_value;
620                 break;
621
622         case CTL_TABPREFIXN:
623                 state->tabxprefix = state->width * cp->ctf_value / 100;
624                 break;
625
626         case CTL_PREFIXPOS:
627             {
628                 char *p;
629
630                 p = (state->tabprefix) ? state->tabprefix : state->curprefix;
631                 if (p)
632                         draw_line_output(state, p);
633                 break;
634             }
635
636         case CTL_TEXT:
637                 if (!cp->ctc_value)
638                         break;
639                 if (state->align == AL_LEFTFILL0) {
640                         state->align = AL_LEFTFILL1;
641                         state->leftfillpos = state->linewidth;
642                 }
643                 draw_line_output(state, cp->ctc_value);
644                 break;
645
646         case CTL_LINESTART:
647                 state->charoff = 0;
648                 char_size[caching] = nonscaled_size[caching];
649                 if (state->line == 0) {
650                         /*
651                          * set background of target
652                          */
653                         if (bg_ctl) {
654                                 if (!caching){
655                                         /* target is window, so we need care bg_ctl_last */
656                                         if (bg_ctl_last && !ctlcmp(bg_ctl, bg_ctl_last)){
657                                                 /* same as last time, we do nothing  */
658                                                 ;
659                                         } else {
660                                                 /* we have to change background */
661                                                 get_background_pixmap(bg_ctl, state);
662
663                                                 /* set window background */
664                                                 set_background_pixmap(bg_ctl);
665
666                                                 bg_ctl_last = bg_ctl;
667                                         }
668                                         XClearWindow(display, state->target);
669                                 } else {
670                                         get_background_pixmap(bg_ctl, state);
671                                         bg_ctl_cache = bg_ctl;
672
673                                         XClearPixmap(display, state->target);
674                                 }
675                         } else {
676                                 if (!caching)
677                                         XClearWindow(display, state->target);
678                                 else
679                                         XClearPixmap(display, state->target);
680                         }
681
682                         if (t_fin)
683                                 timebar(state);
684                 }
685                 draw_line_start(state);
686                 break;
687
688         case CTL_LINEEND:
689                 /* blank lines */
690                 if (state->brankline) { /*XXX*/
691                         state->max_lineascent = char_size[caching];
692                         state->maxascent = char_size[caching];
693                         state->maxdescent = VERT_GAP(char_size[caching]);
694                 }
695                 draw_line_end(state);
696                 /* reset single-line oriented state */
697                 state->tabprefix = NULL;
698                 state->tabxprefix = 0;
699                 state->special = 0;
700                 if (state->align == AL_LEFTFILL1) {
701                         state->align = AL_LEFTFILL0;
702                         state->leftfillpos = 0;
703                 }
704                 break;
705
706         case CTL_MARK:
707                 state->have_mark = 1;
708                 state->mark_ypos = state->ypos;
709                 break;
710
711         case CTL_SYSTEM:
712                 CHECK_CACHE;
713                 process_system(state, cp);
714                 break;
715
716         case CTL_XSYSTEM:
717                 CHECK_CACHE;
718                 process_xsystem(state, cp);
719                 break;
720
721         case CTL_TSYSTEM:
722                 CHECK_CACHE;
723                 process_tsystem(state, cp);
724                 break;
725
726         case CTL_ICON:
727                 process_icon(state, cp);
728                 break;
729
730         case CTL_NOOP:
731         case CTL_NODEF:
732         case CTL_TITLE:
733                 break;
734
735         case CTL_XFONT:
736                 /* obsolete directives */
737                 fprintf(stderr, "internal error: obsolete directive "
738                         "\"%s\"\n", ctl_words[cp->ct_op].ctl_string);
739                 exit(1);
740                 /*NOTREACHED*/
741
742         case CTL_PCACHE:
743                 if (!caching) {
744                         if (cp->ctch_flag)
745                                 mgp_flag |= FL_FRDCACHE;
746                         else
747                                 mgp_flag ^= FL_FRDCACHE;
748                         cache_mode   = cp->ctch_mode;
749                         cache_effect = cp->ctch_effect;
750                         cache_value  = cp->ctch_value;
751                 } else {
752                         pcache.flag = 1;
753                         pcache.page = state->page;
754                         pcache.mgpflag = cp->ctch_flag;
755                         pcache.mode = cp->ctch_mode;
756                         pcache.effect = cp->ctch_effect;
757                         pcache.value = cp->ctch_value;
758                 }
759                 break;
760
761         case CTL_CHARSET:
762                 if (get_regid(cp->ctc_value) < 0){
763                         fprintf(stderr, "invalid charset \"%s\". ignored\n",
764                                 cp->ctc_value);
765                         break;
766                 }
767                 strlcpy(mgp_charset, cp->ctc_value, sizeof(mgp_charset));
768                 break;
769
770         case CTL_VALIGN:
771                 valign = cp->cti_value;
772                 break;
773
774         case CTL_AREA:
775                 state->width = window_width * cp->ctar_width / 100;
776                 state->height = window_height * cp->ctar_height / 100;
777                 state->xoff = window_width * cp->ctar_xoff / 100;
778                 state->yoff = window_height * cp->ctar_yoff / 100;
779                 state->ypos = 0;
780                 break;
781
782         case CTL_OPAQUE:
783                 if (cp->cti_value > 100){
784                         fprintf(stderr, "%%opaque: value should be 0-100\n");
785                         cp->cti_value = 100;
786                 }
787                 state->opaque = cp->cti_value;
788                 if (mgp_flag & FL_NOXFT && verbose){
789                         fprintf(stderr, "ignored %%opaque.\n");
790                 }
791                 break;
792         case CTL_PSFONT:
793                 break;
794         default:
795                 fprintf(stderr,
796                         "undefined directive %d at page %d line %d:\n\t",
797                         cp->ct_op, state->page, state->line);
798                 debug0(cp);
799                 break;
800         }
801 }
802
803 /*
804  * line management.
805  */
806 static int
807 set_position(struct render_state *state)
808 {
809         int x;
810
811         x = 0;
812         switch (state->align) {
813         case AL_CENTER:
814                 x = (state->width - state->linewidth)/ 2;
815                 break;
816
817         case AL_LEFT:
818         case AL_LEFTFILL0:
819         case AL_LEFTFILL1:
820                 x = 0;
821                 break;
822
823         case AL_RIGHT:
824                 x = state->width - state->linewidth;
825                 break;
826         }
827
828         return x;
829 }
830
831 void
832 draw_line_start(struct render_state *state)
833 {
834         struct render_object *obj;
835
836         state->max_lineascent = 0;
837         state->max_linedescent = 0;
838         state->maxascent = 0;
839         state->maxdescent = 0;
840         state->linewidth = 0;
841         state->brankline = 1;
842         while ((obj = state->obj))
843                 obj_free(state, obj);
844 }
845
846 void
847 draw_line_itemsize(struct render_state *state,
848     unsigned int ascent, unsigned int descent, int flheight)
849 {
850         ascent -= state->charoff;
851         descent += state->charoff;
852         if (ascent > state->maxascent)
853                 state->maxascent = ascent;
854         if (descent > state->maxdescent)
855                 state->maxdescent = descent;
856
857         /*
858          * calculation for the height of a line should ignore
859          * character offset
860          */
861         if (state->charoff == 0) {
862                 if (ascent > state->max_lineascent)
863                         state->max_lineascent = ascent;
864                 if (descent > state->max_linedescent)
865                         state->max_linedescent = descent;
866         }
867
868         if (flheight > state->maxflheight)
869                 state->maxflheight = flheight;
870 }
871
872 static void
873 draw_line_output(struct render_state *state, char *data)
874 {
875         draw_string(state, data);
876 }
877
878 void
879 draw_line_end(struct render_state *state)
880 {
881         int xpos;
882
883         xpos = set_position(state);
884
885         /* process the special attribute. */
886         switch (state->special) {
887         case SP_LCUTIN:
888                 cutin(state, xpos, state->ypos, 1);
889                 break;
890         case SP_RCUTIN:
891                 cutin(state, xpos, state->ypos, -1);
892                 break;
893         default:
894                 break;
895         }
896         if (state->obj) {
897                 obj_draw(state, state->target, xpos, state->ypos);
898                 while (state->obj)
899                         obj_free(state, state->obj);
900         }
901
902         state->ypos += state->max_lineascent;
903
904         /*
905          * we should ignore height of images to calculate line gap.
906          * suggested by Toru Terao
907          */
908         if (VERT_GAP(char_size[caching]) < state->max_linedescent)
909                 state->ypos += state->max_linedescent;
910         else
911                 state->ypos += VERT_GAP(char_size[caching]);
912
913         state->ypos += 2;
914 }
915
916 #undef min
917 #define min(x, y) (x < y ? x: y)
918 static void
919 cutin(struct render_state *state, int lx, int ly, int dir)
920 {
921         u_int x, xoff, yoff;
922         int i, sx, round2;
923         int root_x, root_y, use_copy;
924         Window cutinWin = 0, junkwin;
925         XImage *copywin;
926         static XWindowAttributes xa;
927         XWindowAttributes wa;
928         Pixmap ghostWin;
929         GC saveGC = gc_cache;
930
931         XGetWindowAttributes(display, window, &wa);
932         ghostWin = XCreatePixmap(display, window, wa.width, wa.height, wa.depth);
933         /* all drawing should be done on the image */
934         gc_cache = XCreateGC(display, ghostWin, 0, 0);
935         XCopyArea(display, state->target, ghostWin, gc_cache,
936                         0, 0, wa.width, wa.height, 0, 0);
937
938         if (state->repaint)
939                 return;
940
941         if (!state->linewidth)
942                 return;
943
944         if (!xa.width)
945                 XGetWindowAttributes(display, DefaultRootWindow(display), &xa);
946         XTranslateCoordinates(display, window, DefaultRootWindow(display),
947                 0, 0, &root_x ,&root_y, &junkwin);
948         use_copy = 1;
949         if ((root_x + window_width > xa.width) || (root_y + window_height > xa.height) ||
950                         (root_x < 0 || root_y < 0)) use_copy = 1;
951
952         sx = (0 < dir) ? 0 : state->width - state->linewidth;
953         round2 = 20;    /*XXX*/
954 #ifndef abs
955 #define abs(a)  (((a) < 0) ? -(a) : (a))
956 #endif
957         if (abs(lx - sx) < round2){
958                 round2 = abs(lx - sx);
959                 if (!round2) round2 = 1;
960         }
961
962         if (!use_copy){
963                 cutinWin = XCreateSimpleWindow(display, state->target,
964                         sx, ly, state->linewidth, state->maxascent + state->maxdescent,
965                         0, fore_color[caching], back_color[caching]);
966                 XSetWindowBackgroundPixmap(display, cutinWin, None);
967                 XMapSubwindows(display, state->target);
968         } else {
969                 copywin = XGetImage(display, window, state->xoff + min(sx, lx), ly + state->yoff, state->linewidth + abs(lx - sx),
970                                         state->maxascent + state->maxdescent, AllPlanes, ZPixmap);
971         }
972
973         xoff = state->xoff;
974         yoff = state->yoff;
975         state->xoff = state->yoff = 0;
976         if (state->obj && !use_copy) {
977                 obj_draw(state, cutinWin, 0, 0);
978         }
979         XFlush(display);
980
981         x = sx;
982         for (i = 0; i < round2; i++) {
983                 if (use_copy && state->obj) {
984                                 obj_draw(state, ghostWin, x + xoff, ly + yoff);
985                                 XCopyArea(display, ghostWin, state->target,
986                                     saveGC,
987                                     xoff + min(sx, lx),
988                                     ly + yoff,
989                                     state->linewidth + abs(lx - sx),
990                                     state->maxascent + state->maxdescent,
991                                     xoff + min(sx, lx),
992                                     ly + yoff);
993                 } else
994                         XMoveWindow(display, cutinWin, x + xoff, ly + yoff);
995
996                 XFlush(display);
997                 usleep(CUTIN_DELAY);
998                 if (use_copy && state->obj) {
999                         XPutImage(display, ghostWin, gc_cache, copywin,
1000                                 x - min(sx, lx) , 0, x + xoff, ly + yoff,
1001                                 state->linewidth, state->maxascent + state->maxdescent);
1002                 }
1003                 x = sx + ((i+1)*(lx - sx)) / round2;
1004         }
1005         XCopyArea(display, ghostWin, state->target, saveGC,
1006                 0, 0, wa.width, wa.height, 0, 0);
1007
1008         if (!use_copy) XDestroyWindow(display, cutinWin);
1009         state->xoff = xoff;
1010         state->yoff = yoff;
1011
1012         /* freeing images */
1013         if(use_copy) XFree(copywin);
1014
1015         /* restoring tho old GC */
1016         XFreeGC(display, gc_cache);
1017         XFreePixmap(display, ghostWin);
1018         gc_cache = saveGC;
1019 }
1020
1021 /*
1022  * render characters.
1023  */
1024 static void
1025 draw_string(struct render_state *state, char *data)
1026 {
1027         u_char *p, *q;
1028         const char *registry = NULL;
1029         u_int code2;
1030         static const char *rtab96[] = {
1031                 NULL,                   /* ESC - @ */
1032                 "iso8859-1",            /* ESC - A */
1033                 "iso8859-2",            /* ESC - B */
1034                 "iso8859-3",            /* ESC - C */
1035                 "iso8859-4",            /* ESC - D */
1036         };
1037 #define RTAB96_MAX      (sizeof(rtab96)/sizeof(rtab96[0]))
1038         static const char *rtab9494[] = {
1039                 "jisx0208.1978-*",      /* ESC $ @ or ESC $ ( @ */
1040                 "gb2312.1980-*",        /* ESC $ A or ESC $ ( A */
1041                 "jisx0208.1983-*",      /* ESC $ B or ESC $ ( B */
1042                 "ksc5601.1987-*",       /* ESC $ ( C */
1043                 NULL,                   /* D */
1044                 NULL,                   /* E */
1045                 NULL,                   /* F */
1046                 NULL,                   /* G */
1047                 NULL,                   /* H */
1048                 NULL,                   /* I */
1049                 NULL,                   /* J */
1050                 NULL,                   /* K */
1051                 NULL,                   /* L */
1052                 NULL,                   /* M */
1053                 NULL,                   /* N */
1054                 "jisx0213.2000-1",      /* ESC $ ( O */
1055                 "jisx0213.2000-2",      /* ESC $ ( P */
1056         };
1057 #define RTAB9494_MAX    (sizeof(rtab9494)/sizeof(rtab9494[0]))
1058         int charset16 = 0;
1059
1060         p = (u_char *)data;
1061         while (*p && *p != '\n') {
1062                 /* 94x94 charset */
1063                 if (p[0] == 0x1b && p[1] == '$' &&
1064                     '@' <= p[2] && p[2] < 'C' && rtab9494[p[2] - '@']) {
1065                         registry = rtab9494[p[2] - '@'];
1066                         charset16 = 1;
1067                         p += 3;
1068                         continue;
1069                 }
1070                 if (p[0] == 0x1b && p[1] == '$' && p[2] == '(' &&
1071                     '@' <= p[3] && p[3] < '@' + RTAB9494_MAX &&
1072                     rtab9494[p[3] - '@']) {
1073                         registry = rtab9494[p[3] - '@'];
1074                         charset16 = 1;
1075                         p += 4;
1076                         continue;
1077                 }
1078                 /* ascii (or JIS roman) */
1079                 if (p[0] == 0x1b && p[1] == '(' &&
1080                     (p[2] == 'B' || p[2] == 'J')) {
1081                         registry = NULL;
1082                         charset16 = 0;
1083                         p += 3;
1084                         continue;
1085                 }
1086                 /* 96 charset */
1087                 if (p[0] == 0x1b && p[1] == '-' &&
1088                     '@' < p[2] && p[2] < '@' + RTAB96_MAX &&
1089                     rtab96[p[2] - '@']) {
1090                         registry = rtab96[p[2] - '@'];
1091                         charset16 = 0;
1092                         p += 3;
1093                         continue;
1094                 }
1095
1096                 if (!registry && isspace(p[0])) {
1097                         draw_fragment(state, p, 1, registry, 0);
1098                         p++;
1099                         continue;
1100                 }
1101
1102                 if (charset16) {
1103                         for (q = p + 2; 0x21 <= *q && *q <= 0x7e; q += 2) {
1104                                 code2 = q[0] * 256 + q[1];
1105                                 if (strncmp(registry, "jisx0208", 8) == 0
1106                                  && !iskinsokuchar(code2)) {
1107                                         break;
1108                                 }
1109                         }
1110                 } else {
1111                         q = p;
1112                         while (*q && isprint(*q) && !isspace(*q))
1113                                 q++;
1114                         if (q == p)
1115                                 q++;
1116                         else {
1117                                 /*
1118                                  * append spaces to the end of the word.
1119                                  * fragments in the following line:
1120                                  *      "this is test"
1121                                  * are:
1122                                  *      "this_" "is_" "test"
1123                                  */
1124                                 while (*q && isspace(*q))
1125                                         q++;
1126                         }
1127                 }
1128
1129                 q = draw_fragment(state, p, q - p, registry, charset16);
1130
1131                 p = q;
1132         }
1133 }
1134
1135 static u_char *
1136 draw_fragment(struct render_state *state, u_char *p, size_t len,
1137     const char *registry, /* 2-octet charset? */ int charset16)
1138 {
1139         u_int char_len, i;
1140         u_short code;
1141         struct render_object *tail;
1142         struct render_object *thisline;
1143         struct render_object *thislineend;
1144         u_int startwidth;
1145         struct render_state backup0, backup;
1146         enum { MODE_UNKNOWN, MODE_X }
1147                 mode = MODE_UNKNOWN;
1148
1149         if (!(mgp_flag & FL_NOXFT)){
1150                 u_char *p0 = xft_draw_fragment(state, p, len, registry, charset16);
1151                 if (p0) return p0;
1152         }
1153
1154         if (state->obj)
1155                 tail = state->objlast;
1156         else
1157                 tail = NULL;
1158         startwidth = state->linewidth;
1159
1160         while (len) {
1161                 code = charset16 ? p[0] * 256 + p[1] : p[0];
1162                 if (code != ' ')
1163                         state->brankline = 0; /* This isn't brankline */
1164
1165                 if (code == '\t') {
1166                         char_len = char_size[caching] / 2;
1167                         p++;
1168                         len--;
1169
1170                         char_len = HORIZ_STEP(char_size[caching], char_len) * 8;/*XXX*/
1171                         state->linewidth = (state->linewidth + char_len) / char_len * char_len;
1172                         continue;
1173                 }
1174
1175                 /*
1176                  * decide which font to use.
1177                  * Japanese font:
1178                  *      VFlib - optional
1179                  *      then X.
1180                  * Western font:
1181                  *      X if truely scalable.
1182                  *      otherwise, X.
1183                  */
1184                 mode = MODE_UNKNOWN;
1185                 if (charset16) {
1186                         if (mode == MODE_UNKNOWN)
1187                                 mode = MODE_X;
1188                 } else {
1189                         if (mode == MODE_UNKNOWN) {
1190                                 /*
1191                                  * if we can have X font that is exactly
1192                                  * matches the required size, we use that.
1193                                  */
1194                                 int ts;
1195
1196                                 x_setfont(x_findseed(state, registry),
1197                                     char_size[caching], registry, &ts);
1198                                 if (ts)
1199                                         mode = MODE_X;
1200                         }
1201
1202                         /* last resort: use X font. */
1203                         if (mode == MODE_UNKNOWN)
1204                                 mode = MODE_X;
1205                 }
1206
1207                 /* back it up before drawing anything */
1208                 memcpy(&backup0, state, sizeof(struct render_state));
1209
1210                 switch (mode) {
1211                 default:
1212                         fprintf(stderr, "invalid drawing mode %d for %04x "
1213                                 "- fallback to X11\n", mode, code);
1214                         /* fall through */
1215                 case MODE_UNKNOWN:
1216                 case MODE_X:
1217                         char_len = draw_onechar_x(state, code,
1218                                 state->linewidth, state->charoff, char_size[caching],
1219                                 registry, (len == (charset16 ? 2 : 1)) ? 1 : 0);
1220                         if (char_len == 0) {
1221                                 fprintf(stderr, "can't load font size %d "
1222                                         "(nor font in similar size) for "
1223                                         "font <%s:%d:%s>, glyph 0x%04x\n",
1224                                         char_size[caching], x_findseed(state, registry),
1225                                         char_size[caching], registry?registry:"NULL", code);
1226                         }
1227                         break;
1228                 }
1229
1230                 p += (charset16 ? 2 : 1);
1231                 len -= (charset16 ? 2 : 1);
1232
1233                 state->linewidth += HORIZ_STEP(char_size[caching], char_len);
1234                 /* ukai */
1235                 if (!charset16 && state->linewidth + HORIZ_STEP(char_size[caching],
1236                                 char_len) > state->width) {
1237                         if (len >= 20) break; /* too long word */
1238                         for (i = 0; i < len; i ++){
1239                                 if (isspace(*(p +i))) break;
1240                         }
1241                         if (i == len) break;
1242                 }
1243         }
1244
1245         if (state->width - state->leftfillpos / 2 < state->linewidth) {
1246                 memcpy(&backup, state, sizeof(struct render_state));
1247
1248                 /* strip off the last fragment we wrote. */
1249                 if (tail) {
1250                         thisline = tail->next;
1251                         thislineend = state->objlast;
1252                         tail->next = NULL;
1253                         state->objlast = tail;
1254                         state->maxascent = backup0.maxascent;
1255                         state->maxdescent = backup0.maxdescent;
1256                 } else {
1257                         thisline = state->obj;
1258                         thislineend = state->objlast;
1259                         state->obj = state->objlast = NULL;
1260                         state->maxascent = backup0.maxascent;
1261                         state->maxdescent = backup0.maxdescent;
1262                 }
1263                 state->linewidth = startwidth;
1264                 draw_line_end(state);   /* flush the line. */
1265
1266                 /* start the new line with the last fragment we wrote. */
1267                 draw_line_start(state);
1268                 state->linewidth = state->leftfillpos;
1269                 state->linewidth += (backup.linewidth - startwidth);
1270                 if (state->obj && state->objlast)
1271                         state->objlast->next = thisline;
1272                 else
1273                         state->obj = thisline;
1274                 state->objlast = thislineend;
1275                 state->align = backup.align;
1276                 /* fix up x position and maxascent. */
1277                 for (tail = state->obj; tail; tail = tail->next) {
1278                         tail->x -= startwidth;
1279                         tail->x += state->leftfillpos;
1280                         draw_line_itemsize(state, tail->ascent, tail->descent, 0);
1281                 }
1282         }
1283         return p;
1284 }
1285
1286 static struct render_object *
1287 obj_alloc(struct render_state *state)
1288 {
1289         struct render_object *obj;
1290
1291         obj = malloc(sizeof(*obj));
1292         if (obj == NULL)
1293                 return NULL;
1294         obj->next = NULL;
1295         if (state->obj == NULL)
1296                 state->obj = obj;
1297         else
1298                 state->objlast->next = obj;
1299         state->objlast = obj;
1300         return obj;
1301 }
1302
1303 static void
1304 obj_free(struct render_state *state, struct render_object *obj)
1305 {
1306         struct render_object *o;
1307
1308         if (state->obj == obj)
1309                 state->obj = obj->next;
1310         else {
1311                 for (o = state->obj; o; o = o->next)
1312                         if (o->next == obj)
1313                                 break;
1314                 /* ASSERT(o != NULL); */
1315                 o->next = obj->next;
1316         }
1317         if (state->objlast == obj)
1318                 state->objlast = obj->next;
1319         switch (obj->type) {
1320         case O_IMAGE:
1321                 freeImage(obj->data.image.image);
1322                 break;
1323         case O_XFONT:
1324                 free(obj->data.xfont.xfont);
1325                 break;
1326         case O_ICON:
1327                 if (obj->data.icon.xpoint)
1328                         free(obj->data.icon.xpoint);
1329                 break;
1330         case O_XTFONT:
1331                 if (obj->data.xftfont.data)
1332                         free(obj->data.xftfont.data);
1333                 if (obj->data.xftfont.fontname)
1334                         free(obj->data.xftfont.fontname);
1335                 if (obj->data.xftfont.registry)
1336                         free(obj->data.xftfont.registry);
1337                 break;
1338         }
1339         free(obj);
1340 }
1341
1342 static int
1343 obj_new_xfont(struct render_state *state,
1344     int x, int y, int size, u_int code, const char *registry)
1345 {
1346         struct render_object *obj;
1347
1348         obj = obj_alloc(state);
1349         if (obj == NULL)
1350                 return 0;
1351         obj->x = x;
1352         obj->y = y;
1353         obj->fore = fore_color[caching];
1354         obj->type = O_XFONT;
1355         obj->data.xfont.xfont = strdup(x_findseed(state, registry));
1356         obj->data.xfont.csize = size;
1357         obj->data.xfont.code = code;
1358         obj->data.xfont.registry = registry;
1359         obj->ascent = size - y; /*XXX*/
1360         obj->descent = -y;      /*XXX*/
1361         obj->vertloc = VL_BASE;
1362         return 1;
1363 }
1364
1365 static int
1366 obj_new_image2(struct render_state *state,
1367     int x, int y, Image *image, int xzoom, int yzoom,
1368     Imlib_Image *imimage, int zoomonclk)
1369 {
1370         struct render_object *obj;
1371
1372         obj = obj_alloc(state);
1373         if (obj == NULL)
1374                 return 0;
1375         obj->x = x;
1376         obj->y = y;
1377         obj->type = O_IMAGE;
1378         obj->data.image.image = image;
1379         obj->data.image.xzoom = xzoom;
1380         obj->data.image.yzoom = yzoom;
1381         obj->ascent = 0;        /*XXX*/
1382         obj->descent = image->height * yzoom / 100;     /*XXX*/
1383         obj->vertloc = VL_TOP;
1384         obj->data.image.imimage = imimage;
1385         obj->data.image.zoomonclk = zoomonclk;
1386         return 1;
1387 }
1388
1389 static int
1390 obj_new_icon(struct render_state *state, int x, int y,
1391     u_int itype, u_int isize, u_long color, u_int npoint, XPoint *xpoint)
1392 {
1393         struct render_object *obj;
1394         unsigned int i;
1395
1396         obj = obj_alloc(state);
1397         if (obj == NULL)
1398                 return 0;
1399         obj->x = x;
1400         obj->y = y;
1401         obj->fore = color;
1402         obj->type = O_ICON;
1403         obj->data.icon.itype = itype;
1404         obj->data.icon.isize = isize;
1405         obj->data.icon.npoint = npoint;
1406         if (npoint) {
1407                 obj->data.icon.xpoint = malloc(sizeof(XPoint) * npoint);
1408                 if (obj->data.icon.xpoint == NULL) {
1409                         obj_free(state, obj);
1410                         return 0;
1411                 }
1412                 for (i = 0; i < npoint; i++)
1413                         obj->data.icon.xpoint[i] = xpoint[i];
1414         } else
1415                 obj->data.icon.xpoint = NULL;
1416         obj->ascent = 0;        /*XXX*/
1417         obj->descent = isize;   /*XXX*/
1418         obj->vertloc = VL_CENTER;
1419         return 1;
1420 }
1421
1422 static Pixel
1423 obj_image_color(Image *image, Image *bimage, Pixel d, int *inithist)
1424 {
1425         unsigned int i, j;
1426         RGBMap rgb;
1427         int r, g, b;
1428         static char hist[256];
1429         byte *p;
1430
1431         switch (bimage->type) {
1432         case IBITMAP:
1433                 r = g = b = d ? 0xffff : 0;
1434                 break;
1435         case IRGB:
1436                 r = bimage->rgb.red[d];
1437                 g = bimage->rgb.green[d];
1438                 b = bimage->rgb.blue[d];
1439                 break;
1440         case ITRUE:
1441                 r = TRUE_RED(d) << 8;
1442                 g = TRUE_GREEN(d) << 8;
1443                 b = TRUE_BLUE(d) << 8;
1444                 break;
1445         default:
1446                 return 0;
1447         }
1448         if (image->type == ITRUE)
1449                 return RGB_TO_TRUE(r, g, b);
1450
1451         for (i = 0; i < image->rgb.used; i++) {
1452                 if (image->rgb.red[i] == r &&
1453                     image->rgb.green[i] == g &&
1454                     image->rgb.blue[i] == b)
1455                         return i;
1456         }
1457         if (i >= image->rgb.size) {
1458                 if (i >= 256) {
1459                         /* search a free slot */
1460                         if (image->rgb.size == 256) {
1461                                 if (!*inithist) {
1462                                         *inithist = 1;
1463                                         memset(hist, 0, sizeof(hist));
1464                                         p = image->data;
1465                                         for (j = 0; j < image->height; j++)
1466                                                 for (i = 0; i < image->width; i++)
1467                                                         hist[*p++] = 1;
1468                                 }
1469                                 for (i = 0; i < 256; i++) {
1470                                         if (hist[i] == 0) {
1471                                                 hist[i] = 1;
1472                                                 goto freeslot;
1473                                         }
1474                                 }
1475                         }
1476                         return -1;
1477                 }
1478                 image->depth = 8;
1479                 newRGBMapData(&rgb, depthToColors(image->depth));
1480                 for (i = 0; i < image->rgb.used; i++) {
1481                         rgb.red[i] = image->rgb.red[i];
1482                         rgb.green[i] = image->rgb.green[i];
1483                         rgb.blue[i] = image->rgb.blue[i];
1484                 }
1485                 rgb.used = i;
1486                 freeRGBMapData(&image->rgb);
1487                 image->rgb = rgb;
1488         }
1489   freeslot:
1490         image->rgb.red[i] = r;
1491         image->rgb.green[i] = g;
1492         image->rgb.blue[i] = b;
1493         if (image->rgb.used < i + 1)
1494                 image->rgb.used = i + 1;
1495         return i;
1496 }
1497
1498 static Image *
1499 obj_image_trans(Image *image, u_int x, u_int y)
1500 {
1501         Image *timage;
1502         unsigned int i, j;
1503         byte *p, *b;
1504         Pixel d, n, pd;
1505         static XColor xcol;
1506         int pl, bpl;
1507         int trans;
1508         u_int bw, bh, bx, by;
1509         int inithist;
1510
1511         if (!COMPLEX_BGIMAGE) {
1512                 if (back_color[caching] != xcol.pixel) {
1513                         xcol.pixel = back_color[caching];
1514                         xcol.flags = DoRed|DoGreen|DoBlue;
1515                         XQueryColor(display, colormap, &xcol);
1516                 }
1517                 switch (image->type) {
1518                 case IBITMAP:
1519                 case IRGB:
1520                         image->rgb.red[image->trans] = xcol.red;
1521                         image->rgb.green[image->trans] = xcol.green;
1522                         image->rgb.blue[image->trans] = xcol.blue;
1523                         break;
1524                 case ITRUE:
1525                         d = image->trans;
1526                         n = RGB_TO_TRUE(xcol.red, xcol.green, xcol.blue);
1527                         pl = image->pixlen;
1528                         p = image->data;
1529                         for (j = 0; j < image->height; j++) {
1530                                 for (i = 0; i < image->width; i++, p += pl) {
1531                                         if (memToVal(p, pl) == d)
1532                                                 valToMem(n, p, pl);
1533                                 }
1534                         }
1535                         break;
1536                 }
1537                 bw = bh = 0;    /* for lint */
1538                 goto end;
1539         }
1540         bh = bgpixmap[bgindex].image->height;
1541         bw = bgpixmap[bgindex].image->width;
1542         j = 0;
1543         if (image->type == IBITMAP) {
1544   expand:
1545                 timage = image;
1546                 if (verbose)
1547                         fprintf(stderr, "obj_image_trans: expanding image\n");
1548                 image = expand(image);
1549                 if (image != timage)
1550                         freeImage(timage);
1551         }
1552         pl = image->pixlen;
1553         p = image->data + image->width * j * pl;
1554         bpl = bgpixmap[bgindex].image->pixlen;
1555         pd = -1;
1556         n = 0;  /* for lint */
1557         trans = image->trans;
1558         inithist = 0;
1559         for ( ; j < image->height; j++) {
1560                 by = (y + j) % bh;
1561                 bx = x % bw;
1562                 b = bgpixmap[bgindex].image->data +
1563                         (bgpixmap[bgindex].image->width * by + bx) * bpl;
1564                 for (i = 0; i < image->width; i++, p += pl, b += bpl, bx++) {
1565                         if (bx == bw) {
1566                                 bx = 0;
1567                                 b = bgpixmap[bgindex].image->data +
1568                                         bgpixmap[bgindex].image->width * by * bpl;
1569                         }
1570                         if ((int)memToVal(p, pl) != trans)
1571                                 continue;
1572                         d = memToVal(b, bpl);
1573                         if (d != pd) {
1574                                 pd = d;
1575                                 n = obj_image_color(image,
1576                                                 bgpixmap[bgindex].image, d, &inithist);
1577                                 if (n == (Pixel)-1)
1578                                         goto expand;
1579                         }
1580                         valToMem(n, p, pl);
1581                 }
1582         }
1583   end:
1584         if (verbose) {
1585                 const char *p2;
1586
1587                 switch (image->type) {
1588                 case IBITMAP:   p2 = "bitmap"; break;
1589                 case IRGB:      p2 = "rgb"; break;
1590                 default:        p2 = "true"; break;
1591                 }
1592                 fprintf(stderr, "obj_image_trans: %s: "
1593                         "trans=%d, rgb_used=%d, rgb_size=%d\n",
1594                         p2, image->trans, image->rgb.used, image->rgb.size);
1595                 fprintf(stderr, "  image=%dx%d+%d+%d",
1596                         image->width, image->height, x, y);
1597                 if (COMPLEX_BGIMAGE)
1598                         fprintf(stderr, "  bgpixmap[bgindex].image=%dx%d", bw, bh);
1599                 fprintf(stderr, "\n");
1600         }
1601         image->trans = -1;      /* XXX: need recalculation to redraw? */
1602         return image;
1603 }
1604
1605 static void
1606 obj_draw_image(Drawable target, u_int x, u_int y,
1607     struct render_object *obj, int page)
1608 {
1609         Image *image, *timage;
1610         XImageInfo *ximageinfo;
1611         XImage *xim;
1612         int private = mgp_flag & FL_PRIVATE;
1613
1614         image = obj->data.image.image;
1615         if (obj->data.image.xzoom != 100.0 || obj->data.image.yzoom != 100.0) {
1616                 timage = image;
1617                 image = zoom(image,
1618                         obj->data.image.xzoom, obj->data.image.yzoom, verbose);
1619                 if (!image) {
1620                         fprintf(stderr, "image zoom (%0.2fx%0.2f) failed in obj_draw_image\n",
1621                                 obj->data.image.xzoom, obj->data.image.yzoom);
1622                         exit(1);
1623                 }
1624                 freeImage(timage);
1625         }
1626         if (image->trans >= 0)
1627                 image = obj_image_trans(image, x, y);
1628         obj->data.image.image = image;  /* to free later */
1629         ximageinfo= imageToXImage(display, screen, visual, depth, image,
1630                                 private, 0,0, verbose);
1631         if (ximageinfo == NULL) {
1632                 fprintf(stderr, "Cannot convert Image to XImage\n");
1633                 cleanup(-1);
1634         }
1635         xim = ximageinfo->ximage;
1636         if (xim->format == XYBitmap)
1637                 XSetBackground(display, gcfore, back_color[caching]);
1638         XPutImage(display, target, gcfore, xim, 0, 0,
1639                 x, y, xim->width, xim->height);
1640
1641         if (obj->data.image.zoomonclk) {
1642                 regist_zimage_position(obj, x, y, xim->width, xim->height, page);
1643         }
1644         freeXImage(ximageinfo);
1645 }
1646
1647 static void
1648 obj_draw(struct render_state *state, Drawable target, u_int xpos, u_int ypos)
1649 {
1650         struct render_object *obj;
1651         int x = 0, y = 0;
1652         u_long fore;
1653         u_int code;
1654         const char *registry;
1655         XChar2b kch[2];
1656         u_int isize;
1657         unsigned int i;
1658         int lineoff;   /* ypos correction for lines with superscripts */
1659         XftDraw *dummy;
1660
1661         /*
1662          * very complicated...
1663          *
1664          *      xpos, ypos      x/y position of the target,
1665          *                      leftmost and uppermost dot.
1666          *      state->ypos     absolute y position in main window.
1667          */
1668         xpos += state->tabxprefix ? state->tabxprefix : state->xprefix;
1669         xpos += state->xoff;
1670         ypos += state->yoff;
1671         fore = fore_color[caching];
1672
1673         /*
1674          * only used with superscript offset for calculating the
1675          * exact line position (ypos correction)
1676          */
1677         lineoff = state->maxascent - state->max_lineascent;
1678
1679         for (obj = state->obj; obj; obj = obj->next) {
1680 #if 0
1681                 x = obj->x + offx;
1682                 y = obj->y + offy;
1683 #else
1684                 x = obj->x;
1685                 switch (obj->vertloc) {
1686                 case VL_BASE:
1687                         y = state->maxascent;
1688                         break;
1689                 case VL_ICENTER:
1690                         if (state->maxflheight){
1691                                 y = (state->maxascent + state->maxflheight) / 2;
1692                         } else
1693                                 y = (state->maxascent + state->maxdescent) / 2;
1694                         y += (obj->ascent - obj->descent) / 2;
1695                         break;
1696                 case VL_CENTER:
1697                         y = (state->maxascent + state->maxdescent) / 2;
1698                         y += (obj->ascent - obj->descent) / 2;
1699                         break;
1700                 case VL_TOP:
1701                         y = obj->ascent;
1702                         break;
1703                 case VL_BOTTOM:
1704                         y = state->maxascent + state->maxdescent;
1705                         y -= obj->descent;
1706                         break;
1707                 }
1708                 x += xpos;
1709                 y += ypos;
1710 #endif
1711                 switch (obj->type) {
1712                 case O_IMAGE:
1713                         obj_draw_image(target, x, y, obj, state->page);
1714                         break;
1715                 case O_XTFONT:
1716                         y += obj->y;
1717                         set_xrender_color(obj->fore, state->opaque);
1718                         xft_font = xft_setfont(obj->data.xftfont.fontname,
1719                                                 obj->data.xftfont.size,
1720                                                 obj->data.xftfont.registry);
1721
1722                         dummy = xft_getdraw(target);
1723                         if (obj->data.xftfont.charset16){
1724                                 XftDrawStringUtf8(dummy,
1725                                                 &xft_forecolor, xft_font,
1726                                                 x, y - lineoff,
1727                                                 obj->data.xftfont.data,
1728                                                 obj->data.xftfont.len);
1729                         } else
1730                                 XftDrawString8(dummy,
1731                                                 &xft_forecolor, xft_font,
1732                                                 x, y - lineoff,
1733                                                 obj->data.xftfont.data,
1734                                                 obj->data.xftfont.len);
1735                         XftDrawDestroy(dummy);
1736                         break;
1737                 case O_XFONT:
1738                         y += obj->y;
1739                         code = obj->data.xfont.code;
1740                         registry = obj->data.xfont.registry;
1741                         (void)x_setfont(obj->data.xfont.xfont,
1742                                 obj->data.xfont.csize,
1743                                 registry, NULL);
1744                         if (obj->fore != fore) {
1745                                 fore = obj->fore;
1746                                 XSetForeground(display, gcfore, fore);
1747                         }
1748
1749 #if 1
1750                         /* is it always okay? */
1751                         kch[0].byte1 = (code >> 8) & 0xff;
1752                         kch[0].byte2 = code & 0xff;
1753                         XDrawString16(display, target, gcfore,
1754                                         x, y - lineoff, kch, 1);
1755 #else
1756                         if (registry) {
1757                                 kch[0].byte1 = (code >> 8) & 0xff;
1758                                 kch[0].byte2 = code & 0xff;
1759                                 XDrawString16(display, target, gcfore,
1760                                         x, y - lineoff, kch, 1);
1761                         } else {
1762                                 ch[0] = code & 0xff;
1763                                 XDrawString(display, target, gcfore,
1764                                         x, y - lineoff, ch, 1);
1765                         }
1766 #endif
1767                         break;
1768                 case O_ICON:
1769                         if (obj->fore != fore) {
1770                                 fore = obj->fore;
1771                                 XSetForeground(display, gcfore, fore);
1772                         }
1773                         isize = obj->data.icon.isize;
1774                         switch (obj->data.icon.itype) {
1775                         case 1: /* this is box */
1776                                 XFillRectangle(display, target, gcfore, x, y,
1777                                         isize, isize);
1778                                 break;
1779                         case 2: /* this is arc */
1780                                 XFillArc(display, target, gcfore, x, y,
1781                                         isize, isize, 0, 360 * 64);
1782                                 break;
1783                         case 3: case 4: case 5: case 6:
1784                         case 7:
1785                                 for (i = 0; i < obj->data.icon.npoint; i++) {
1786                                         obj->data.icon.xpoint[i].x += x;
1787                                         obj->data.icon.xpoint[i].y += y;
1788                                 }
1789                                 XFillPolygon(display, target, gcfore,
1790                                         obj->data.icon.xpoint,
1791                                         obj->data.icon.npoint,
1792                                         Convex, CoordModeOrigin);
1793                                 break;
1794                         }
1795                         break;
1796                 default:
1797                         break;
1798                 }
1799         }
1800         if (fore != fore_color[caching]){
1801                 XSetForeground(display, gcfore, fore_color[caching]);
1802         }
1803         /* ASSERT(state->obj == NULL); */
1804         /* ASSERT(state->objlast == NULL); */
1805 }
1806
1807 static char *
1808 x_fontname(char *buf, int bufsiz, const char *seed, int siz,
1809     /* already canonicalised */ const char *registry)
1810 {
1811         int hyphen;
1812         const char *p;
1813         char tmp[BUFSIZ];
1814         char tmp2[BUFSIZ];
1815         char **fontlist;
1816         int count;
1817
1818         if (!registry)
1819                 registry = "iso8859-1";
1820
1821         if (siz < 0)
1822                 strlcpy(tmp2, "*", sizeof(tmp2));
1823         else
1824                 sprintf(tmp2, "%d", siz);
1825
1826         hyphen = 0;
1827         for (p = seed; *p; p++) {
1828                 if (*p == '-')
1829                         hyphen++;
1830         }
1831         switch (hyphen) {
1832         case 0:
1833                 /* for "a14", "5x8", or such an short names */
1834                 if ((fontlist = XListFonts(display, seed, 1, &count))) {
1835                         XFreeFontNames(fontlist);
1836                         strlcpy(buf, seed, bufsiz);
1837                         break;
1838                 }
1839                 sprintf(tmp, "%s-*-*", seed);
1840                 sprintf(buf, FONT_FORMAT, tmp, tmp2, registry);
1841                 break;
1842         case 2:
1843                 sprintf(buf, FONT_FORMAT, seed, tmp2, registry);
1844                 break;
1845         case XLFD_HYPHEN:
1846                 /* as is */
1847                 strlcpy(buf, seed, bufsiz);
1848                 break;
1849         case 1: /* should not happen */
1850                 fprintf(stderr, "internal error: invalid seed <%s>\n", seed);
1851                 exit(1);
1852         }
1853         if (mgp_flag & FL_VERBOSE) {
1854                 fprintf(stderr, "fontname: seed=<%s> siz=<%d> reg=<%s> "
1855                         "result=<%s>\n",
1856                         seed, siz, registry, buf);
1857         }
1858         return buf;
1859 }
1860
1861 static int
1862 x_parsefont(char *xfont, int *pixel, int *truescalable)
1863 {
1864         char *p;
1865         int fsize;
1866         int i;
1867
1868         /* go toward pixel size */
1869         p = xfont;
1870         for (i = 0; *p && i < 7; i++) {
1871                 /* go toward minus sign */
1872                 while (*p && *p != '-')
1873                         p++;
1874                 /* skip minus sign */
1875                 if (*p)
1876                         p++;
1877         }
1878
1879         if (!*p)
1880                 return -1;
1881         fsize = atoi(p);
1882         if (pixel)
1883                 *pixel = fsize;
1884
1885         /* skip pixel size */
1886         while (*p && (isdigit(*p) || *p == '*'))
1887                 p++;
1888         if (*p == '-')
1889                 p++;
1890         else
1891                 return -1;
1892
1893         /* skip point size */
1894         while (*p && (isdigit(*p) || *p == '*'))
1895                 p++;
1896         if (*p == '-')
1897                 p++;
1898         else
1899                 return -1;
1900
1901         if (truescalable) {
1902                 if (fsize == 0 && (p[0] == '0' || p[0] == '*') && p[1] == '-')
1903                         *truescalable = 1;
1904                 else
1905                         *truescalable = 0;
1906         }
1907         return 0;
1908 }
1909
1910 static XFontStruct *
1911 x_setfont(const char *xfont, u_int csize, const char *registry, int *truescalable)
1912 {
1913         static XFontStruct *xfontstruct;
1914         int i, fsize;
1915         char fontstring[BUFSIZ];
1916 #define FONTTYPEMAX     10      /* number of used fontlist type (in cache) */
1917 #define FONTLISTMAX     20      /* number of list for specified font type */
1918 #define FONTALLOWMAX    105     /* % of desired font */
1919 #define FONTALLOWMIN    90      /* % of desired font */
1920         char **fontlist, **font;
1921         u_int error;
1922         int best, freeindex, count;
1923         int maxsize, minsize;
1924         int scalable, tscalable, tsflag;
1925         static struct {
1926                 char *xlfd;
1927                 char **list;
1928                 int count;
1929         } fontnames[FONTTYPEMAX];
1930 #define FONTCACHEMAX    200     /* number of used font type (in cache) */
1931         static struct {
1932                 char *xfont;
1933                 u_int csize;
1934                 const char *registry;
1935                 char *xlfd;
1936                 XFontStruct *xfontstruct;
1937         } fonts[FONTCACHEMAX];
1938
1939         /*
1940          * Check font cache first.
1941          */
1942         for (i = 0; i < FONTCACHEMAX; i++) {
1943                 if (!fonts[i].xfontstruct)
1944                         continue;
1945                 if (fonts[i].csize != csize || fonts[i].registry != registry
1946                  || strcmp(fonts[i].xfont, xfont) != 0) {
1947                         continue;
1948                 }
1949
1950                 XSetFont(display, gcfore, fonts[i].xfontstruct->fid);
1951                 return fonts[i].xfontstruct;
1952         }
1953
1954         /*
1955          * load new font.
1956          */
1957         if (csize < 5) {
1958                 xfontstruct = XLoadQueryFont(display, "nil2");
1959                 goto gotfont;
1960         }
1961
1962         if (verbose) {
1963                 fprintf(stderr, "need font <%s:%d:%s>\n",
1964                         xfont, csize, registry?registry:"NULL");
1965         }
1966
1967         /*
1968          * Look for the best font possible.
1969          * 1. Check for a font that is smaller than the required one.
1970          *    By using smaller font, we won't make the screen garbled.
1971          * 2. If 1. is impossible, look for slightly larger font than
1972          *    the required one.
1973          */
1974         fontlist = NULL;
1975         freeindex = -1;
1976         x_fontname(fontstring, sizeof(fontstring), xfont, -1, registry);
1977         if (verbose)
1978                 fprintf(stderr, "fontstring <%s>\n", fontstring);
1979         for (i = 0; i < FONTTYPEMAX; i++) {
1980                 if (fontnames[i].xlfd == NULL) {
1981                         if (freeindex < 0)
1982                                 freeindex = i;
1983                         continue;
1984                 }
1985                 if (strcmp(fontnames[i].xlfd, fontstring) == 0) {
1986                         fontlist = fontnames[i].list;
1987                         count = fontnames[i].count;
1988                         freeindex = i;
1989                         break;
1990                 }
1991         }
1992         if (fontlist == NULL) {
1993                 fontlist = XListFonts(display, fontstring, FONTLISTMAX, &count);
1994                 if (fontlist == NULL)
1995                         return NULL;
1996                 if (freeindex >= 0) {
1997                         if (fontnames[freeindex].xlfd)
1998                                 free(fontnames[freeindex].xlfd);
1999                         fontnames[freeindex].xlfd = strdup(fontstring);
2000                         fontnames[freeindex].list = fontlist;
2001                         fontnames[freeindex].count = count;
2002                 }
2003         }
2004         error = (u_int)-1;
2005         best = -1;
2006         maxsize = csize * FONTALLOWMAX / 100;           /* truncate */
2007         minsize = (csize * FONTALLOWMIN + 99) / 100;    /* roundup */
2008         if (verbose)
2009                 fprintf(stderr, "checking %d to %d\n", minsize, maxsize);
2010         scalable = tscalable = -1;
2011         if (truescalable)
2012                 *truescalable = 0;
2013         for (i = 0, font = fontlist; i < count; i++, font++) {
2014                 if (x_parsefont(*font, &fsize, &tsflag) < 0) {
2015                         if (verbose) {
2016                                 fprintf(stderr, " [%d] <%s>: nosize\n",
2017                                         i, *font);
2018                         }
2019                         continue;
2020                 }
2021                 if (fsize == 0) {
2022                         if (scalable < 0)
2023                                 scalable = i;
2024                         if (tsflag) {
2025                                 tscalable = i;
2026                                 if (truescalable)
2027                                         *truescalable = 1;
2028                         }
2029                         if (verbose) {
2030                                 fprintf(stderr, " [%d] <%s>: scalable (%d)\n",
2031                                         i, *font, tsflag);
2032                         }
2033                         continue;
2034                 } else if (fsize > maxsize || fsize < minsize) {
2035                         continue;
2036                 }
2037                 if ((unsigned int)fsize > csize) {
2038                         fsize = fsize - csize + 100;
2039                                         /* penalty for larger font */
2040                 } else
2041                         fsize = csize - fsize;
2042                 if (error > (unsigned int)fsize) {
2043                         error = fsize;
2044                         best = i;
2045                         if (verbose) {
2046                                 fprintf(stderr, " [%d] <%s>: score %d best\n",
2047                                         i, *font, error);
2048                         }
2049                 } else {
2050                         if (verbose) {
2051                                 fprintf(stderr, " [%d] <%s>: score %d\n",
2052                                         i, *font, error);
2053                         }
2054                 }
2055         }
2056         if (best >= 0) {
2057                 if (verbose) {
2058                         fprintf(stderr, "using best [%d] <%s>\n",
2059                                 best, fontlist[best]);
2060                 }
2061                 strlcpy(fontstring, fontlist[best], sizeof(fontstring));
2062         } else if (scalable >= 0 || tscalable >= 0) {
2063                 x_fontname(fontstring, sizeof(fontstring), xfont, csize,
2064                     registry);
2065                 if (verbose) {
2066                         fprintf(stderr, "using %sscalable <%s>\n",
2067                                 tscalable >= 0 ? "true" : "", fontstring);
2068                 }
2069         }
2070         xfontstruct = XLoadQueryFont(display, fontstring);
2071
2072         if (freeindex < 0)
2073                 XFreeFontNames(fontlist);
2074
2075         /*
2076          * Fill font cache.
2077          */
2078         for (i = 0; i < FONTCACHEMAX; i++) {
2079                 if (!fonts[i].xfontstruct)
2080                         break;
2081         }
2082         if (FONTTYPEMAX <= i) {
2083                 /* last resort.  always cache the font recently used */
2084                 i = FONTTYPEMAX - 1;
2085                 XFreeFont(display, fonts[i].xfontstruct);
2086                 free(fonts[i].xfont);
2087                 free(fonts[i].xlfd);
2088         }
2089         fonts[i].csize = csize;
2090         fonts[i].registry = registry;
2091         fonts[i].xfont = strdup(xfont);
2092         fonts[i].xlfd = strdup(fontstring);
2093         fonts[i].xfontstruct = xfontstruct;
2094
2095   gotfont:
2096         if (xfontstruct == NULL)
2097                 return NULL;
2098         XSetFont(display, gcfore, xfontstruct->fid);
2099         return xfontstruct;
2100 }
2101
2102 static u_int
2103 draw_onechar_x(struct render_state *state, u_int code,
2104     int x, int y, int size, const char *argregistry, int lastchar)
2105 {
2106         u_int charlen;
2107         static XFontStruct *xfontstruct;
2108         int coffset;
2109         XCharStruct *cs;
2110         const char *metricsource;
2111         const char *seed;
2112         const char *registry;
2113
2114         if (code >= 0xa0 && ((!argregistry || !argregistry[0]) && *mgp_charset))
2115                 registry = mgp_charset;
2116         else
2117                 registry = argregistry;
2118         seed = x_findseed(state, registry);
2119         xfontstruct = x_setfont(seed, char_size[caching], registry, NULL);
2120
2121         if (xfontstruct == NULL)
2122                 return 0;
2123
2124         if (!xfontstruct->per_char) {
2125                 metricsource = "max_bounds";
2126                 coffset = 0;
2127                 cs = &xfontstruct->max_bounds;
2128         } else if (!xfontstruct->min_byte1 && !xfontstruct->max_byte1) {
2129                 metricsource = "bytewise offset";
2130                 coffset = (code & 0xff) - xfontstruct->min_char_or_byte2;
2131                 cs = &xfontstruct->per_char[coffset];
2132         } else {
2133                 metricsource = "wordwise offset";
2134                 coffset = (code & 0xff) - xfontstruct->min_char_or_byte2;
2135                 coffset += (((code >> 8) & 0xff) - xfontstruct->min_byte1)
2136                     * (xfontstruct->max_char_or_byte2 - xfontstruct->min_char_or_byte2);
2137                 cs = &xfontstruct->per_char[coffset];
2138         }
2139
2140         /*
2141          * It looks that there are some Japanese X11 fonts with bogus
2142          * font metric (cs->width == 0).  This is a workaround for that.
2143          * (or is there any mistake in above "coffset" computation?)
2144          *
2145          * TODO: report the X/Open group, or some other guys, about this.
2146          */
2147         if (!cs->width) {
2148                 if (verbose) {
2149                         fprintf(stderr, "X11 font %s:%d:%s has bogus "
2150                                 "font metric for glyph 0x%04x\n"
2151                                 "\tcs->width=%d, source=%s, coffset=0x%04x\n",
2152                                 seed, char_size[caching], registry?registry:"NULL",
2153                                 code, cs->width, metricsource, coffset);
2154                 }
2155                 cs = &xfontstruct->max_bounds;
2156         }
2157
2158         draw_line_itemsize(state, cs->ascent, cs->descent, 0);
2159
2160         /* usually */
2161         charlen = cs->width;
2162
2163         /*
2164          * for the very first char on the line, the char may goes over the
2165          * edge at the lefthand side.  offset the image to the right so that
2166          * whole part of the bitmap appears on the screen.
2167          * beware the sign-ness of cs->lbearing.
2168          */
2169         if (x + cs->lbearing < 0) {
2170                 x -= cs->lbearing;
2171                 charlen -= cs->lbearing;
2172         }
2173
2174         /*
2175          * For the last char, make sure that the whole part of the bitmap
2176          * appears on the screen.
2177          */
2178         if (lastchar && cs->width < cs->rbearing)
2179                 charlen += cs->rbearing - cs->width;
2180
2181         obj_new_xfont(state, x, y, size, code, registry);
2182
2183         return charlen;
2184 }
2185
2186 /*
2187  * render misc items.
2188  */
2189 static void
2190 back_gradation(struct render_state *state, struct ctrl_grad *cg0)
2191 {
2192         struct ctrl_grad cg1;
2193         struct ctrl_grad *cg;
2194         int srcwidth, srcheight;
2195         int dstwidth, dstheight;
2196         int dir, numcolor;
2197         float xzoomrate, yzoomrate;
2198         int hquality, vquality;
2199
2200         Image *myimage, *image;
2201         XImageInfo *ximageinfo;
2202         byte *pic;
2203         int private = mgp_flag & FL_PRIVATE;
2204         static Cursor curs;
2205
2206         /* okay, please wait for a while... */
2207         if (!curs)
2208                 curs = XCreateFontCursor(display, XC_watch);
2209         XDefineCursor(display, window, curs);
2210         XFlush(display);
2211
2212         /* just for safety */
2213         memcpy(&cg1, cg0, sizeof(struct ctrl_grad));
2214         cg = &cg1;
2215
2216         /* grab parameters */
2217         dir = cg->ct_direction;
2218         numcolor = cg->ct_numcolor;
2219         hquality = b_quality[caching];
2220         vquality = b_quality[caching];
2221
2222         /*
2223          * XXX zoomflag is too complex to understand.
2224          */
2225         if (!cg->ct_zoomflag) {
2226                 int t;
2227                 int i;
2228
2229                 dstwidth = window_width * cg->ct_width / 100;
2230                 dstheight = window_height * cg->ct_height / 100;
2231                 srcwidth = dstwidth;
2232                 srcheight = dstheight;
2233
2234                 /*
2235                  * apply quality factor if srcwidth/height are large enough.
2236                  */
2237 #define TOOSMALLFACTOR 8
2238                 t = srcwidth;
2239                 for (i = 100; hquality < i; i--) {
2240                         t = srcwidth * i / 100;
2241                         if (t < cg->ct_g_colors * TOOSMALLFACTOR)
2242                                 break;
2243                 }
2244                 srcwidth = t;
2245
2246                 t = srcheight;
2247                 for (i = 100; vquality < i; i--) {
2248                         t = srcheight * i / 100;
2249                         if (t < cg->ct_g_colors * TOOSMALLFACTOR)
2250                                 break;
2251                 }
2252                 srcheight = t;
2253 #undef TOOSMALLFACTOR
2254         } else {
2255                 dstwidth = window_width;
2256                 dstheight = window_height;
2257                 srcwidth = state->width * cg->ct_width / 100;
2258                 srcheight = state->height * cg->ct_height / 100;
2259
2260                 /*
2261                  * we don't apply quality factor here, since srcwidth/height
2262                  * is already smaller than dstwidth/height.
2263                  */
2264         }
2265
2266         xzoomrate = 100.0 * dstwidth / srcwidth;
2267         yzoomrate = 100.0 * dstheight / srcheight;
2268
2269         /* performace enhance hack for special case */
2270         if (dir % 90 == 0) {
2271                 float *q;
2272                 int *p, *r;
2273
2274                 /*
2275                  * 0 or 180: reduce width
2276                  * 90 or 270: reduce height
2277                  */
2278                 p = (dir % 180 == 0) ? &srcwidth : &srcheight;
2279                 q = (dir % 180 == 0) ? &xzoomrate : &yzoomrate;
2280                 r = (dir % 180 == 0) ? &dstwidth : &dstheight;
2281
2282                 /* rely upon use X11 background image tiling. */
2283                 *q = (float) 100.0;
2284                 *p = 3;
2285                 *r = 3;
2286         }
2287
2288         if (verbose) {
2289                 fprintf(stderr, "raw: %d,%d qu: %d,%d "
2290                         "dst: %d,%d src: %d,%d zoom: %0.2f,%0.2f\n",
2291                         cg->ct_width, cg->ct_height,
2292                         hquality, vquality,
2293                         dstwidth, dstheight, srcwidth, srcheight,
2294                         xzoomrate, yzoomrate);
2295         }
2296
2297         screen = DefaultScreen(display);
2298
2299         /* make gradation image */
2300         pic = draw_gradation(srcwidth, srcheight, cg);
2301         myimage = make_XImage(pic, srcwidth, srcheight);
2302
2303         if (numcolor < 64)
2304                 myimage = reduce(myimage, numcolor, verbose);
2305
2306         if (verbose) {
2307                 fprintf(stderr, "background zoomrate: (%0.2f,%0.2f)\n",
2308                         xzoomrate, yzoomrate);
2309                 fprintf(stderr, "background zoom mode %d: "
2310                         "(%d, %d)->(%d, %d)[%d]\n", cg->ct_zoomflag,
2311                         srcwidth, srcheight, dstwidth, dstheight, b_quality[caching]);
2312         }
2313
2314         if (xzoomrate != 100.0 || yzoomrate != 100.0) {
2315                 image = myimage;
2316                 myimage = zoom(image, xzoomrate, yzoomrate, verbose);
2317                 if (!image) {
2318                         fprintf(stderr, "image zoom (%0.2fx%0.2f) failed in back_gradataion\n",
2319                                 xzoomrate, yzoomrate);
2320                         exit(1);
2321                 }
2322                 freeImage(image);
2323         }
2324
2325         ximageinfo = imageToXImage(display, screen, visual, depth, myimage,
2326                 private, 0, 1, verbose);
2327         if (!ximageinfo) {
2328                 fprintf(stderr, "Cannot convert Image to XImage\n");
2329                 cleanup(-1);
2330         }
2331
2332         regist_background_pixmap(ximageinfo, myimage);
2333
2334         XUndefineCursor(display, window);
2335         XFlush(display);
2336 }
2337
2338 /* !TODO: move rotation code into some library */
2339 /* rotate image by 90 degrees (counter clockwise) */
2340 static void
2341 rotate_image_p90(Image *image)
2342 {
2343         unsigned int row, column, pl = image->pixlen;
2344         unsigned int new_height = image->width, new_width = image->height, new_linelen = new_width * pl;
2345         byte *src, *tgt, *col_head;
2346         Pixel d;
2347         /* allocate buffer for new image */
2348         byte *rot_data = lmalloc(new_linelen * new_height);
2349
2350         /* do the rotation */
2351         for (row = 0, src = image->data, col_head = rot_data + (new_height - 1) * new_linelen;
2352                         row < image->height;
2353                         row++, col_head += pl) {
2354                 for (column = 0, tgt = col_head;
2355                                 column < image->width;
2356                                 column++, src += pl, tgt -= new_linelen) {
2357                         d = memToVal(src, pl);
2358                         valToMem(d, tgt, pl);
2359                 }
2360         }
2361
2362         /* swap to rotated image, exchange height and width
2363            and point to rotated data */
2364         image->height = new_height;
2365         image->width = new_width;
2366         lfree(image->data);
2367         image->data = rot_data;
2368 }
2369
2370 /* rotate image by -90 degrees (clockwise) */
2371 static void
2372 rotate_image_m90(Image *image)
2373 {
2374         unsigned int row, column, pl = image->pixlen;
2375         unsigned int new_height = image->width, new_width = image->height, new_linelen = new_width * pl;
2376         byte *src, *tgt;
2377         Pixel d;
2378         /* allocate buffer for new image */
2379         byte *rot_data = lmalloc(new_linelen * new_height);
2380
2381         /* do the rotation */
2382         for (row = 0, src = image->data; row < image->height; row++) {
2383                 for (column = 0, tgt = rot_data + new_linelen - (row + 1) * pl;
2384                                 column < image->width;
2385                                 column++, src += pl, tgt += new_linelen) {
2386                         d = memToVal(src, pl);
2387                         valToMem(d, tgt, pl);
2388                 }
2389         }
2390
2391         /* swap to rotated image, exchange height and width
2392            and point to rotated data */
2393         image->height = new_height;
2394         image->width = new_width;
2395         lfree(image->data);
2396         image->data = rot_data;
2397
2398         return;
2399 }
2400
2401 /* rotate image by 180 degrees */
2402 static void
2403 rotate_image_180(Image *image)
2404 {
2405         unsigned int row, column, pl = image->pixlen;
2406         unsigned int new_height = image->height, new_width = image->width, new_linelen = new_width * pl;
2407         byte *src, *tgt;
2408         Pixel d;
2409         /* allocate buffer for new image */
2410         byte *rot_data = lmalloc(new_linelen * new_height);
2411
2412         /* do the rotation */
2413         for (row = 0, src = image->data; row < image->height; row++) {
2414                 for (column = 0, tgt = rot_data + (new_height - row) * new_linelen - pl;
2415                                 column < image->width;
2416                                 column++, src += pl, tgt -= pl) {
2417                         d = memToVal(src, pl);
2418                         valToMem(d, tgt, pl);
2419                 }
2420         }
2421
2422         /* swap to rotated image, exchange height and width
2423            and point to rotated data */
2424         image->height = new_height;
2425         image->width = new_width;
2426         lfree(image->data);
2427         image->data = rot_data;
2428
2429         return;
2430 }
2431
2432 static void
2433 image_load(struct render_state *state, char *filename,
2434     int numcolor, int ximagesize, int yimagesize,
2435     int backflag, int zoomflag, int centerflag,
2436     int raise2, int rotate, int zoomonclk)
2437 {
2438         Image *image, *myimage;
2439         XImageInfo *ximageinfo;
2440         u_int image_posx;
2441         int width, height;
2442         float xzoomrate, yzoomrate;
2443         int     private = mgp_flag & FL_PRIVATE;
2444         static Cursor curs;
2445         Imlib_Image *imimage;
2446
2447         if (!caching){
2448                 if (!curs)
2449                         curs = XCreateFontCursor(display, XC_watch);
2450                 XDefineCursor(display, state->target, curs);
2451                 XFlush(display);
2452         }
2453
2454         if ((myimage = loadImage(filename)) == NULL) {
2455                 fprintf(stderr, "failed to load image file\n");
2456                 cleanup(-1);
2457         }
2458         switch (rotate) {
2459                 case 0:
2460                         /* Do nothing */
2461                         break;
2462
2463                 case -90:
2464                 case 270:
2465                         rotate_image_m90(myimage);
2466                         break;
2467
2468                 case 90:
2469                         rotate_image_p90(myimage);
2470                         break;
2471
2472                 case -180:
2473                 case 180:
2474                         rotate_image_180(myimage);
2475                         break;
2476
2477                 default:
2478                         fprintf(stderr, "rotation by %d degrees not supported.\n", rotate);
2479                         cleanup(-1);
2480         }
2481         width = myimage->width;
2482         height = myimage->height;
2483
2484         if (myimage->depth == 1 && myimage->trans < 0) {
2485                 XColor xc;
2486
2487                 xc.flags = DoRed | DoGreen | DoBlue;
2488                 xc.pixel = fore_color[caching];
2489                 XQueryColor(display, colormap, &xc);
2490                 *(myimage->rgb.red + 1) = xc.red;
2491                 *(myimage->rgb.green + 1) = xc.green;
2492                 *(myimage->rgb.blue + 1) = xc.blue;
2493                 myimage->trans = 0;     /* call obj_image_trans() later */
2494         }
2495
2496         if (numcolor)
2497                 myimage = reduce(myimage, numcolor, verbose);
2498
2499         if (!ximagesize) ximagesize = 100;
2500         if (!yimagesize) yimagesize = 100;
2501         xzoomrate = (float) ximagesize;
2502         yzoomrate = (float) yimagesize;
2503         image_zoomratio(state, &xzoomrate, &yzoomrate, zoomflag, width, height);
2504
2505         if (backflag) {
2506                 if (xzoomrate != 100 || yzoomrate != 100) {
2507                         image = myimage;
2508                         myimage = zoom(image, xzoomrate, yzoomrate, verbose);
2509                         if (!image) {
2510                                 fprintf(stderr, "image zoom (%fx%f) failed in image_load\n",
2511                                         xzoomrate, yzoomrate);
2512                                 exit(1);
2513                         }
2514                         freeImage(image);
2515                 }
2516
2517                 ximageinfo= imageToXImage(display, screen, visual, depth,
2518                                 myimage, private, 0, 1, verbose);
2519                 if (ximageinfo == NULL) {
2520                         fprintf(stderr, "Cannot convert Image to XImage\n");
2521                         cleanup(-1);
2522                 }
2523                 regist_background_pixmap(ximageinfo, myimage);
2524                 goto end;
2525         }
2526
2527 #if 1  /* by h.kakugawa@computer.org */
2528         switch(valign){
2529         case VL_TOP:
2530                 draw_line_itemsize(state,
2531                                    (height * raise2) * yzoomrate / 10000,
2532                                    height * (100 + raise2) * yzoomrate / 10000, 0);
2533                 break;
2534         case VL_BOTTOM:
2535                 draw_line_itemsize(state,
2536                                    height * (100 + raise2) * yzoomrate / 10000,
2537                                    (height * raise2) * yzoomrate / 10000, 0);
2538                 break;
2539         case VL_CENTER:
2540                 draw_line_itemsize(state,
2541                                    height * (100 + raise2) * yzoomrate / 20000,
2542                                    height * (100 + raise2) * yzoomrate / 20000, 0);
2543                 break;
2544         }
2545 #else
2546         switch(valign){
2547         case VL_TOP:
2548                 draw_line_itemsize(state, 0, height * yzoomrate / 100, 0);
2549                 break;
2550         case VL_BOTTOM:
2551                 draw_line_itemsize(state, height * yzoomrate / 100, 0, 0);
2552                 break;
2553         case VL_CENTER:
2554                 draw_line_itemsize(state, height * yzoomrate / 200,
2555                         height * yzoomrate / 200, 0);
2556                 break;
2557         }
2558 #endif
2559
2560         if (centerflag)
2561                 image_posx = char_size[caching] / 2 - (width * xzoomrate / 100) / 2;
2562         else
2563                 image_posx = 0;
2564
2565         imimage = search_imdata(filename);
2566         obj_new_image2(state, state->linewidth + image_posx,
2567                 - height * yzoomrate / 100 / 2,
2568                 myimage, xzoomrate, yzoomrate, imimage, zoomonclk);
2569
2570         state->linewidth += (width * xzoomrate / 100);
2571 end:
2572         if (!caching){
2573                 XUndefineCursor(display, state->target);
2574                 XFlush(display);
2575         }
2576 }
2577
2578 static void
2579 image_load_ps(struct render_state *state, char *filename,
2580     int numcolor, int ximagesize, int yimagesize,
2581     int backflag, int zoomflag, int centerflag,
2582     int raise2, int rotate, int zoomonclk)
2583 {
2584         int x1, y1v, x2, y2;
2585         static Cursor curs;
2586         char fullname[MAXPATHLEN];
2587         char *imagefile;
2588         int width, height;
2589         float xzoom, yzoom, zratio;
2590         char *p;
2591
2592         /* wait for a while, please. */
2593         if (!curs)
2594                 curs = XCreateFontCursor(display, XC_watch);
2595         XDefineCursor(display, window, curs);
2596         XFlush(display);
2597
2598         if (findImage(filename, fullname) < 0) {
2599                 fprintf(stderr, "image file %s not found in path\n", filename);
2600                 cleanup(-1);
2601         }
2602         if (ps_boundingbox(fullname, &x1, &y1v, &x2, &y2) < 0) {
2603                 /* error message generated in ps_boundingbox() */
2604                 cleanup(-1);
2605         }
2606
2607         width = x2 - x1 + 1;
2608         height = y2 - y1v + 1;
2609         xzoom = (float) ximagesize;
2610         yzoom = (float) yimagesize;
2611         image_zoomratio(state, &xzoom, &yzoom, zoomflag, width, height);
2612         width = width * xzoom / 100;
2613         height = height * yzoom / 100;
2614
2615         if (zoomonclk)
2616                 zratio = (float) zoomonclk / 100.0 * window_width / width;
2617         else
2618                 zratio = 1.0;
2619         imagefile = epstoimage(fullname, x1, y1v,
2620                 width * zratio, height * zratio, xzoom * zratio, yzoom * zratio);
2621         if (imagefile == NULL) {
2622                 fprintf(stderr, "WARN: cannot generate %s file from %s\n",
2623                         gsdevice, filename);
2624                 XUndefineCursor(display, window);
2625                 XFlush(display);
2626                 return;
2627         }
2628
2629         if (mgp_flag & FL_VERBOSE) {
2630                 fprintf(stderr, "image_load_ps: %s: %s file = %s\n",
2631                         filename, gsdevice, imagefile);
2632         }
2633         image_load(state, imagefile, numcolor, 100.0 /zratio, 100.0/zratio, backflag,
2634                 Z_NORMAL | (Z_NORMAL << Z_YSHIFT), centerflag, raise2, rotate, zoomonclk);
2635         /* XXX: unlink imagefile in /tmp */
2636         if ((p = strrchr(imagefile, '/')) != NULL)
2637                 p++;
2638         else
2639                 p = imagefile;
2640         if (strncmp(p, ".gscache", sizeof(".gscache") - 1) != 0)
2641                 unlink(imagefile);
2642
2643         if (!backflag && numcolor >= 0)
2644                 image_setcolor(state);
2645 }
2646
2647 void
2648 timebar(struct render_state *state)
2649 {
2650         int pos, n, p, barlen;
2651         GC pgc;
2652
2653         if (t_start == 0 || tbar_mode == 0 || caching)
2654                 return;
2655
2656         pos = (window_width - 2) * (state->page - 1) / (maxpage - 1);
2657         p = time(NULL) - t_start;
2658         barlen = window_width - window_width * p / t_fin / 60;
2659
2660         if (window_width / 2 < barlen)
2661                 pgc = gcgreen;
2662         else if (window_width / 3 < barlen)
2663                 pgc = gcyellow;
2664         else
2665                 pgc = gcred;
2666         if (barlen > 0) {
2667                 XClearArea(display, state->target, 0, window_height - 2,
2668                         window_width, 2, 0);
2669                 XFillRectangle(display, state->target, pgc,
2670                         window_width - barlen, window_height - 1, barlen, 1);
2671                 XFillRectangle(display, state->target, pgc,
2672                         pos, window_height - 5, 2, 5);
2673         } else if (barlen < 0) {
2674                 barlen = - barlen;
2675                 n = p / t_fin / 60;
2676                 if (n > window_height - 1)
2677                         n = window_height - 1;
2678                 if (n)
2679                         XFillRectangle(display, state->target, gcred,
2680                                 0, window_height - n,
2681                                 barlen, n);
2682                 XClearArea(display, state->target, 0, window_height - (n + 2),
2683                         window_width, n + 2, 0);
2684                 XFillRectangle(display, state->target, gcred,
2685                         0, window_height - (n + 1),
2686                         barlen % window_width, n + 1);
2687                 XFillRectangle(display, state->target, gcred,
2688                         pos, window_height - (n + 1 + 4),
2689                         2, 5);
2690         }
2691 }
2692
2693 static const struct icon_point {
2694         XPoint xpoint[4];
2695         unsigned int point_num;
2696 } icon_point[] = {{ {{1, 0}, {0, 2}, {2, 2}, {0, 0}}, 3 },
2697                   { {{0, 0}, {2, 0}, {1, 2}, {0, 0}}, 3 },
2698                   { {{0, 0}, {0, 2}, {2, 1}, {0, 0}}, 3 },
2699                   { {{2, 0}, {2, 2}, {0, 1}, {0, 0}}, 3 },
2700                   { {{1, 0}, {0, 1}, {1, 2}, {2, 1}}, 4 }};
2701
2702 static void
2703 process_icon(struct render_state *state, struct ctrl *cp)
2704 {
2705         u_int i, icon_type, icon_size, icon_x, icon_y, index2;
2706         u_long tmp_color;
2707         XPoint xpoint[4];
2708
2709         for (i = 0; icon_words[i].ctl_strlen != 0; i++) {
2710                 if (!strncasecmp(cp->ctic_value, icon_words[i].ctl_string,
2711                         strlen(cp->ctic_value))) {
2712                                 break;
2713                 }
2714         }
2715
2716         icon_type = icon_words[i].ctl_type; /* may be 0 */
2717         icon_size = char_size[caching] * cp->ctic_size / 100;
2718
2719         switch(icon_type){
2720         case 0:
2721                 /* this is image */
2722                 icon_x = icon_size * 100 / state->width;
2723                 icon_y = icon_size * 100 / state->height;
2724                 if (icon_x == 0) icon_x = 1;
2725                 if (icon_y == 0) icon_y = 1;
2726                 tmp_color = fore_color[caching];
2727                 fore_color[caching] = cp->ctic_color;
2728                 image_load(state, cp->ctic_value, 0, icon_x, icon_y, 0, 0, 1, 0, 0, 0);
2729                 fore_color[caching] = tmp_color;
2730                 break;
2731
2732         case 1:
2733                 /* this is box */
2734                 obj_new_icon(state,
2735                         state->linewidth + char_size[caching]/2 - icon_size/2,
2736                         POSY(icon_size), icon_type, icon_size,
2737                         cp->ctic_color, 0, NULL);
2738                 state->linewidth += char_size[caching];
2739                 break;
2740
2741         case 2:
2742                 /* this is arc */
2743                 obj_new_icon(state,
2744                         state->linewidth + char_size[caching]/2 - icon_size/2,
2745                         POSY(icon_size), icon_type, icon_size,
2746                         cp->ctic_color, 0, NULL);
2747                 state->linewidth += char_size[caching];
2748                 break;
2749
2750         case 3:
2751         case 4:
2752         case 5:
2753         case 6:
2754         case 7:
2755                 index2 = icon_type - 3;
2756                 icon_x = state->linewidth + (char_size[caching] - icon_size) / 2;
2757 #if 0
2758                 icon_y = POSY(icon_size);
2759 #else
2760                 icon_y = 0;
2761 #endif
2762                 for (i = 0; i < icon_point[index2].point_num; i ++){
2763                         xpoint[i].x = icon_x +
2764                                 icon_point[index2].xpoint[i].x * icon_size / 2;
2765                         xpoint[i].y = icon_y +
2766                                 icon_point[index2].xpoint[i].y * icon_size / 2;
2767                 }
2768                 obj_new_icon(state, 0, 0, icon_type, icon_size,
2769                         cp->ctic_color, icon_point[index2].point_num, xpoint);
2770                 state->linewidth += char_size[caching];
2771                 break;
2772
2773         default:
2774                 break;
2775         }
2776
2777         cp = NULL;
2778         state->brankline = 0;
2779 }
2780
2781 static void
2782 draw_bar(struct render_state *state, struct ctrl *cp)
2783 {
2784         u_int width, swidth, st, len;
2785         XColor col, scol;
2786         static GC gcbar, gcsbar;
2787         static u_long prevcolor = -1;
2788
2789         if (!gcbar) {
2790                 gcbar = XCreateGC(display, state->target, 0, 0);
2791                 XSetFunction(display, gcbar, GXcopy);
2792                 gcsbar = XCreateGC(display, state->target, 0, 0);
2793                 XSetFunction(display, gcsbar, GXcopy);
2794         }
2795         col.pixel = cp->ctb_color;
2796         if (col.pixel == (unsigned int)-1)
2797                 col.pixel = fore_color[caching];
2798         if (col.pixel != prevcolor) {
2799                 prevcolor = col.pixel;
2800                 col.flags = DoRed|DoGreen|DoBlue;
2801                 XQueryColor(display, colormap, &col);
2802                 scol.red   = col.red   / 2;
2803                 scol.green = col.green / 2;
2804                 scol.blue  = col.blue  / 2;
2805                 if (!XAllocColor(display, colormap, &scol))
2806                         scol.pixel = col.pixel;
2807                 XSetForeground(display, gcbar, col.pixel);
2808                 XSetForeground(display, gcsbar, scol.pixel);
2809         }
2810         width = cp->ctb_width * state->height / 1000;
2811         swidth = width / 2;
2812         width -= swidth;
2813         st = cp->ctb_start * state->width / 100 + state->xoff;
2814         len = cp->ctb_length * state->width / 100;
2815         XFillRectangle(display, state->target, gcbar, st, state->ypos + state->yoff, len, width);
2816         XFillRectangle(display, state->target, gcsbar, st, state->ypos + state->yoff + width, len, swidth);
2817
2818         state->ypos += width + swidth + VERT_GAP(char_size[caching]) / 2;
2819         if (state->maxascent < width + swidth)
2820                 state->maxascent = width + swidth;
2821         state->brankline = 0;
2822 }
2823
2824 static void
2825 process_system(struct render_state *state, struct ctrl *cp)
2826 {
2827         pid_t pid;
2828         unsigned int i;
2829         char **argv;
2830         char buf[BUFSIZ];
2831
2832         if (state->repaint) {
2833                 if (mgp_flag & FL_VERBOSE) {
2834                         fprintf(stderr, "WARN: %%system directive skipping during repaint of same page\n");
2835                 }
2836                 return; /* don't relaunch on repaint */
2837         }
2838
2839         if (mgp_flag & FL_NOFORK) {
2840                 if (mgp_flag & FL_VERBOSE) {
2841                         fprintf(stderr, "WARN: %%system ");
2842                         for (i = 0; i < cp->cta_argc; i++) {
2843                                 fprintf(stderr, "%c%s", (i == 0) ? '"' : ' ',
2844                                         cp->cta_argv[i]);
2845                         }
2846                         fprintf(stderr, "\": directive skipped\n");
2847                 }
2848                 return;
2849         }
2850
2851         if (checkchild(cp) != (pid_t)-1)
2852                 return; /*already running*/
2853
2854         /*
2855          * edit argument.
2856          * if we have X11 geometry string
2857          */
2858         argv = (char **)cp->cta_argv;
2859         for (i = 0; i < cp->cta_argc; i++) {
2860                 if (*(argv[i]) == '%')
2861                         break;
2862         }
2863         if (i < cp->cta_argc) {
2864                 char *p;
2865                 char *q;
2866                 int myxpos, myypos;
2867                 u_int rootxsiz, rootysiz;
2868                 u_int xsiz, ysiz;
2869                 int xloc, yloc;
2870                 int mode;
2871
2872             {
2873                 XWindowAttributes wa;
2874                 Window junkwin;
2875                 int junk;
2876
2877                 XGetWindowAttributes(display, window, &wa);
2878                 XTranslateCoordinates(display, window, wa.root,
2879                         -wa.border_width, -wa.border_width,
2880                         &myxpos, &myypos, &junkwin);
2881                 XGetGeometry(display, wa.root, &junkwin, &junk, &junk,
2882                         &rootxsiz, &rootysiz, (void *)&junk, (void *)&junk);
2883              }
2884
2885                 argv = (char **)malloc((cp->cta_argc + 1) * sizeof(char *));
2886                 memcpy(argv, cp->cta_argv, (cp->cta_argc + 1) * sizeof(char *));
2887                 p = argv[i];
2888                 p++;    /*drop percent char*/
2889                 q = buf;
2890                 *q = '\0';
2891
2892                 mode = XParseGeometry(p, &xloc, &yloc, &xsiz, &ysiz);
2893                 if (mode == 0)
2894                         goto fail;
2895                 if ((mode & WidthValue) && (mode & HeightValue)) {
2896                         sprintf(q, "%dx%d", xsiz * state->width / 100,
2897                                 ysiz * state->height / 100);
2898                         q += strlen(q);
2899                 }
2900                 if ((mode & XValue) && (mode & YValue)) {
2901                         xloc = xloc * state->width / 100;
2902                         yloc = yloc * state->height / 100;
2903                         if (mode & XNegative)
2904                                 xloc = rootxsiz - myxpos + state->width - xloc;
2905                         else
2906                                 xloc += myxpos;
2907                         if (mode & YNegative)
2908                                 yloc = rootysiz - myypos + state->height - yloc;
2909                         else
2910                                 yloc += myypos;
2911                         sprintf(q, "+%d+%d", xloc + state->xoff, yloc + state->yoff);
2912                 }
2913
2914                 if (mgp_flag & FL_VERBOSE) {
2915                         fprintf(stderr, "relative geometry: "
2916                                 "%s (presentation %dx%d+%d+%d)\n",
2917                                 argv[i], state->width, state->height,
2918                                 myxpos, myypos);
2919                         fprintf(stderr, "\t-> %s\n", buf);
2920                 }
2921                 argv[i] = buf;
2922
2923                 if (0) {
2924 fail:
2925                         if (mgp_flag & FL_VERBOSE) {
2926                                 fprintf(stderr,
2927                                         "relative geometry: %s failed\n",
2928                                         argv[i]);
2929                         }
2930                 }
2931         }
2932         pid = fork();
2933         if (pid < 0) {
2934                 perror("fork");
2935                 cleanup(-1);
2936         } else if (pid == 0) {
2937                 execvp(argv[0], argv);
2938                 perror(argv[0]);
2939                 _exit(1);
2940         }
2941
2942         if (!cp->cta_flag)      /*will be purged at the end of page*/
2943                 regchild(pid, cp, -1, state->page);
2944         else
2945                 regchild(pid, cp, -1, cp->cta_flag);
2946 }
2947
2948 static void
2949 process_xsystem(struct render_state *state, struct ctrl *cp)
2950 {
2951         pid_t pid;
2952         unsigned int i;
2953         u_int dumint;
2954         int xloc, yloc;
2955         u_int xsiz, ysiz;
2956         char **argv;
2957         char buf[BUFSIZ];
2958         Window window_id, dumwin;
2959
2960         if (state->repaint) {
2961                 if (mgp_flag & FL_VERBOSE) {
2962                         fprintf(stderr, "WARN: %%system directive skipping during repaint of same page\n");
2963                 }
2964                 return; /* don't relaunch on repaint */
2965         }
2966
2967         if (mgp_flag & FL_NOFORK) {
2968                 if (mgp_flag & FL_VERBOSE) {
2969                         fprintf(stderr, "WARN: %%system ");
2970                         for (i = 0; i < cp->cta_argc; i++) {
2971        &nb