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