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