1 /* Lower-level drawing routines for the X11 interface to Xconq.
2    Copyright (C) 1987-1989, 1991-1999 Stanley T. Shebs.
3 
4 Xconq is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.  See the file COPYING.  */
8 
9 /* The routines in this file are for drawing in maps, but only work
10    at the Xlib level, and would be the same no matter what higher-level
11    toolkit was in use. */
12 
13 /* At the moment, there are three subwindows of a map that use raw Xlib;
14    the unit info, the map view itself, and the list of sides. */
15 
16 #include "conq.h"
17 #include "xtconq.h"
18 
19 int tmpdrawlighting;
20 int tmpdrawcoverage;
21 
22 Map *tmpmap;
23 
24 #define bords_to_draw(m) (numbordtypes > 0 && bwid[(m)->vp->power] > 0)
25 
26 #define conns_to_draw(m) (numconntypes > 0 && cwid[(m)->vp->power] > 0)
27 
28 Unit *x_find_unit_or_occ(Side *side, Map *map, Unit *unit,
29 			 int usx, int usy, int usw, int ush, int sx, int sy);
30 Unit *x_find_unit_at(Side *side, Map *map, int x, int y, int sx, int sy);
31 
32 static void xform_fractional(Side *side, Map *map,
33 			     int x, int y, int xf, int yf, int *sxp, int *syp);
34 
35 static void draw_feature_name(Side *side, Map *map, int fid);
36 static void draw_border_line_mult(Side *side, Map *map, Window win,
37 				  int sx, int sy, int bitmask, int power,
38 				  int t);
39 static void draw_connection_line_mult(Side *side, Window win,
40 				      int sx, int sy, int bitmask,
41 				      int power, int c);
42 static void draw_hex_polygon(Side *side, Map *map, Window win,
43 			     GC gc, int sx, int sy,
44 			     int power, int over, int dogrid);
45 static void draw_area_background(Side *side, Map *map);
46 static void meridian_line_callback(int x1, int y1, int x1f, int y1f,
47 				   int x2, int y2, int x2f, int y2f);
48 static void meridian_text_callback(int x1, int y1, int x1f, int y1f,
49 				   char *str);
50 static int cell_drawing_info(Side *side, int x, int y, int power,
51 			     int seeall, Pixmap *patp, long *colorp);
52 static void set_terrain_gc_for_image(Side *side, Image *timg);
53 static void draw_terrain_row(Side *side, Map *map,
54 			     int x0, int y0, int len, int force);
55 static void draw_contours(Side *side, Map *map, int x0, int y0, int len);
56 #if 0
57 static void draw_elevations(Side *side, Map *map, int x0, int y0, int len);
58 #endif
59 static void draw_clouds_row(Side *side, Map *map, int x0, int y0, int len);
60 static void draw_temperature_row(Side *side, Map *map, int x0, int y0,
61 				 int len);
62 static void draw_winds_row(Side *side, Map *map, int x0, int y0, int len);
63 static void draw_units(Side *side, Map *map, int x, int y);
64 static void draw_unit_and_occs(Side *side, Map *map, Unit *unit,
65 			       int sx, int sy, int sw, int sh);
66 static void draw_unit_view_and_occs(Side *side, Map *map, UnitView *uview,
67 			       int sx, int sy, int sw, int sh);
68 static void draw_unit_name(Side *side, Map *map, Unit *unit,
69 			   int sx, int sy, int sw, int sh);
70 static void draw_people(Side *side, Map *map, int x, int y);
71 static void draw_borders(Side *side, Map *map, int x, int y, int b);
72 static void draw_connections(Side *side, Map *map, int x, int y, int c);
73 static void draw_legend(Side *side, Map *map, int x, int y);
74 static void draw_country_border_line(Side *side, Window win,
75 				     int sx, int sy, int dir, int power);
76 static void draw_legend_text(Side *side, Window win,
77 			     int sx, int sy, int power, char *str,
78 			     int color, int maskit);
79 static void draw_feature_boundary(Side *side, Map *map, int x, int y, int fid);
80 static void draw_info_text(Side *side, Map *map, int x, int y,
81 			   int len, char *buf);
82 
83 static void draw_meridians(Side *side, Map *map);
84 
85 #define xtconq_cell_overlay(side, x, y)  \
86   ((side->designer) \
87    ? ((terrain_view(side, x, y) == UNSEEN) ? -2 : 0)  \
88    : (tmpdrawlighting  \
89       ? (night_at(x, y)  \
90          ? -2  \
91          : (tmpdrawcoverage ? (cover(side, x, y) == 0 ? -1 : 0) : 0))  \
92       : (tmpdrawcoverage  \
93           ? (cover(side, x, y) == 0 ? -1 : 0)  \
94           : 0)))
95 
96 /* Put the point x, y in the center of the map, or at least as close
97    as possible. */
98 
99 void
recenter(Side * side,Map * map,int x,int y)100 recenter(Side *side, Map *map, int x, int y)
101 {
102     int oldsx = map->vp->sx, oldsy = map->vp->sy;
103 
104     set_view_focus(map->vp, x, y);
105     x_center_on_focus(side, map);
106     if (map->vp->sx != oldsx || map->vp->sy != oldsy) {
107 	draw_map(side, map);
108     }
109 }
110 
111 /* Ensure that given location is visible on the front map.  We
112    (should) also flush the input because any input relating to a
113    different screen is probably worthless. */
114 
115 void
put_on_screen(Side * side,Map * map,int x,int y)116 put_on_screen(Side *side, Map *map, int x, int y)
117 {
118 
119     /* Ugly hack to prevent extra boxes being drawn during init - don't ask!*/
120     if (x == 0 && y == 0)
121       return;
122     if (!in_middle(side, map, x, y))
123       recenter(side, map, x, y);
124 }
125 
126 /* Decide whether given location is not too close to edge of screen.
127    We do this because it's a pain to move units when half the adjacent
128    places aren't even visible.  This routine effectively places a
129    lower limit of 5x5 for the map window. (I think) */
130 
131 int
in_middle(Side * side,Map * map,int x,int y)132 in_middle(Side *side, Map *map, int x, int y)
133 {
134     int sx, sy, insetx1, insety1, insetx2, insety2;
135 
136     xform(side, map, x, y, &sx, &sy);
137     /* Adjust to be the center of the cell, more reasonable if large. */
138     sx += map->vp->hw / 2;  sy += map->vp->hh / 2;
139     insetx1 = min(map->vp->pxw / 4, 1 * map->vp->hw);
140     insety1 = min(map->vp->pxh / 4, 1 * map->vp->hch);
141     insetx2 = min(map->vp->pxw / 4, 2 * map->vp->hw);
142     insety2 = min(map->vp->pxh / 4, 2 * map->vp->hch);
143     if (sx < insetx2)
144       return FALSE;
145     if (sx > map->vp->pxw - insetx2)
146       return FALSE;
147     if (sy < (between(2, y, area.height-3) ? insety2 : insety1))
148       return FALSE;
149     if (sy > map->vp->pxh - (between(2, y, area.height-3) ? insety2 : insety1))
150       return FALSE;
151     return TRUE;
152 }
153 
154 /* Transform map coordinates into screen coordinates, relative to the
155    given side. */
156 
157 void
xform(Side * side,Map * map,int x,int y,int * sxp,int * syp)158 xform(Side *side, Map *map, int x, int y, int *sxp, int *syp)
159 {
160     xform_cell(map->vp, x, y, sxp, syp);
161 }
162 
163 static void
xform_fractional(Side * side,Map * map,int x,int y,int xf,int yf,int * sxp,int * syp)164 xform_fractional(Side *side, Map *map, int x, int y, int xf, int yf, int *sxp, int *syp)
165 {
166     xform_cell_fractional(map->vp, x, y, xf, yf, sxp, syp);
167 }
168 
169 void
x_xform_unit(Side * side,Map * map,Unit * unit,int * sxp,int * syp,int * swp,int * shp)170 x_xform_unit(Side *side, Map *map, Unit *unit, int *sxp, int *syp, int *swp, int *shp)
171 {
172     xform_unit(map->vp, unit, sxp, syp, swp, shp);
173 }
174 
175 void
x_xform_unit_view(Side * side,Map * map,UnitView * uview,int * sxp,int * syp,int * swp,int * shp)176 x_xform_unit_view(Side *side, Map *map, UnitView *uview, int *sxp, int *syp, int *swp, int *shp)
177 {
178     xform_unit_view(side, map->vp, uview, sxp, syp, swp, shp);
179 }
180 
181 void
x_xform_unit_self(Side * side,Map * map,Unit * unit,int * sxp,int * syp,int * swp,int * shp)182 x_xform_unit_self(Side *side, Map *map, Unit *unit, int *sxp, int *syp, int *swp, int *shp)
183 {
184     xform_unit_self(map->vp, unit, sxp, syp, swp, shp);
185 }
186 
187 void
x_xform_unit_self_view(Side * side,Map * map,UnitView * uview,int * sxp,int * syp,int * swp,int * shp)188 x_xform_unit_self_view(Side *side, Map *map, UnitView *uview, int *sxp, int *syp, int *swp, int *shp)
189 {
190     xform_unit_self_view(side, map->vp, uview, sxp, syp, swp, shp);
191 }
192 
193 void
x_xform_occupant(Side * side,Map * map,Unit * transport,Unit * unit,int sx,int sy,int sw,int sh,int * sxp,int * syp,int * swp,int * shp)194 x_xform_occupant(Side *side, Map *map, Unit *transport, Unit *unit, int sx, int sy, int sw, int sh, int *sxp, int *syp, int *swp, int *shp)
195 {
196     xform_occupant(map->vp, transport, unit, sx, sy, sw, sh, sxp, syp, swp, shp);
197 }
198 
199 void
x_xform_occupant_view(Side * side,Map * map,UnitView * transport,UnitView * uview,int sx,int sy,int sw,int sh,int * sxp,int * syp,int * swp,int * shp)200 x_xform_occupant_view(Side *side, Map *map, UnitView *transport, UnitView *uview, int sx, int sy, int sw, int sh, int *sxp, int *syp, int *swp, int *shp)
201 {
202     xform_occupant_view(map->vp, transport, uview, sx, sy, sw, sh, sxp, syp, swp, shp);
203 }
204 
205 int
x_nearest_cell(Side * side,Map * map,int sx,int sy,int * xp,int * yp)206 x_nearest_cell(Side *side, Map *map, int sx, int sy, int *xp, int *yp)
207 {
208     return nearest_cell(map->vp, sx, sy, xp, yp, NULL, NULL);
209 }
210 
211 /* Find the closest direction of the closest boundary. */
212 
213 int
x_nearest_boundary(Side * side,Map * map,int sx,int sy,int * xp,int * yp,int * dirp)214 x_nearest_boundary(Side *side, Map *map, int sx, int sy, int *xp, int *yp, int *dirp)
215 {
216     return nearest_boundary(map->vp, sx, sy, xp, yp, dirp);
217 }
218 
219 Unit *
x_find_unit_or_occ(Side * side,Map * map,Unit * unit,int usx,int usy,int usw,int ush,int sx,int sy)220 x_find_unit_or_occ(Side *side, Map *map, Unit *unit, int usx, int usy, int usw, int ush, int sx, int sy)
221 {
222     int usx1, usy1, usw1, ush1;
223     Unit *occ, *rslt;
224 
225     /* See if the point might be over an occupant. */
226     if (unit->occupant != NULL) {
227 	for_all_occupants(unit, occ) {
228 	    x_xform_unit(side, map, occ, &usx1, &usy1, &usw1, &ush1);
229 	    rslt =
230 	      x_find_unit_or_occ(side, map, occ, usx1, usy1, usw1, ush1, sx, sy);
231 	    if (rslt) {
232 		return rslt;
233 	    }
234 	}
235     }
236     /* Otherwise see if it could be the unit itself.  This has the effect of
237        "giving" the transport everything in its box that is not in an occ. */
238     x_xform_unit(side, map, unit, &usx1, &usy1, &usw1, &ush1);
239     if (between(usx1, sx, usx1 + usw1) && between(usy1, sy, usy1 + ush1)) {
240 	return unit;
241     }
242     return NULL;
243 }
244 
245 Unit *
x_find_unit_at(Side * side,Map * map,int x,int y,int sx,int sy)246 x_find_unit_at(Side *side, Map *map, int x, int y, int sx, int sy)
247 {
248     int usx, usy, usw, ush;
249     Unit *unit, *rslt;
250 
251     for_all_stack(x, y, unit) {
252 	x_xform_unit(side, map, unit, &usx, &usy, &usw, &ush);
253 	rslt = x_find_unit_or_occ(side, map, unit, usx, usy, usw, ush, sx, sy);
254 	if (rslt)
255 	  return rslt;
256     }
257     return NULL;
258 }
259 
260 int
x_nearest_unit(Side * side,Map * map,int sx,int sy,Unit ** unitp)261 x_nearest_unit(Side *side, Map *map, int sx, int sy, Unit **unitp)
262 {
263     int x, y;
264 
265     if (!x_nearest_cell(side, map, sx, sy, &x, &y)) {
266 	*unitp = NULL;
267     } else if (map->vp->power > 4) {
268 	*unitp = x_find_unit_at(side, map, x, y, sx, sy);
269     } else {
270 	*unitp = unit_at(x, y);
271     }
272     DGprintf("Pixel %d,%d -> unit %s\n", sx, sy, unit_desig(*unitp));
273     return TRUE;
274 }
275 
276 /* Draw all the maps that are currently up. */
277 
278 void
draw_all_maps(Side * side)279 draw_all_maps(Side *side)
280 {
281     Map *map;
282 
283     for_all_maps(side, map) {
284 	draw_map(side, map);
285     }
286 }
287 
288 /* Display a map and all of its paraphernalia. */
289 
290 void
draw_map(Side * side,Map * map)291 draw_map(Side *side, Map *map)
292 {
293     /* Redraw only the panes that are managed manually. */
294     draw_map_sides(side, map);
295     draw_map_info(side, map);
296     draw_map_view(side, map);
297     flush_output(side);
298 }
299 
300 /* Draw the background area for the map. */
301 
302 static void
draw_area_background(Side * side,Map * map)303 draw_area_background(Side *side, Map *map)
304 {
305     int sx, sy, sx2, sy2, sw, sh, aw, ah, i;
306     int llx, lly, lrx, lry, rx, ry, urx, ury, ulx, uly, lx, ly;
307     XPoint points[7];
308     Display *dpy = side->ui->dpy;
309     GC gc = side->ui->terrgc;
310 
311     XSetClipMask(dpy, gc, None);
312     XSetFillStyle(dpy, gc, FillSolid);
313     XSetForeground(dpy, gc, side->ui->fgcolor);
314     XFillRectangle(dpy, map->viewwin, gc,
315 		   0, 0, map->vp->pxw, map->vp->pxh);
316     if (1 /* grid color matches unseen color */) {
317 	XSetForeground(dpy, gc, side->ui->gridcolor);
318     }
319     XSetLineAttributes(dpy, gc, 1, LineSolid, CapButt, JoinMiter);
320 
321     if (area.xwrap) {
322 	/* Area is cylinder; draw a rectangle. */
323 	xform(side, map, 0, area.height - 1, &sx, &sy);
324 	xform(side, map, area.width - 1, 0, &sx2, &sy2);
325 	sw = sx2 - sx;
326 	sh = sy2 - sy;
327 	XFillRectangle(dpy, map->viewwin, gc,
328 		       sx, sy + map->vp->hh / 2, sw, sh);
329 	XSetForeground(dpy, gc, side->ui->whitecolor);
330 	XDrawRectangle(dpy, map->viewwin, gc,
331 		       sx, sy + map->vp->hh / 2, sw, sh);
332     } else {
333 	/* Area is hexagon; draw a hexagon. */
334 	aw = area.width;  ah = area.height;
335 	xform(side, map, 0 + ah / 2, 0, &llx, &lly);
336 	points[0].x = llx;  points[0].y = lly;
337 	xform(side, map, aw - 1, 0, &lrx, &lry);
338 	points[1].x = lrx;  points[1].y = lry;
339 	xform(side, map, aw - 1, ah / 2, &rx, &ry);
340 	points[2].x = rx;   points[2].y = ry;
341 	xform(side, map, aw - 1 - ah / 2, ah - 1, &urx, &ury);
342 	points[3].x = urx;  points[3].y = ury;
343 	xform(side, map, 0, ah - 1, &ulx, &uly);
344 	points[4].x = ulx;  points[4].y = uly;
345 	xform(side, map, 0, ah / 2, &lx, &ly);
346 	points[5].x = lx;   points[5].y = ly;
347 	/* Offset so polygon edges run through middles of cells. */
348 	for (i = 0; i < 6; ++i) {
349 	    points[i].x += map->vp->hw / 2;  points[i].y += map->vp->hh / 2;
350 	}
351 	/* End up where we started. */
352 	points[6].x = points[0].x;  points[6].y = points[0].y;
353 	XFillPolygon(dpy, map->viewwin, gc,
354 		     points, 6, Convex, CoordModeOrigin);
355 	XSetForeground(dpy, gc, side->ui->whitecolor);
356 	XDrawLines(dpy, map->viewwin, gc, points, 7, CoordModeOrigin);
357     }
358     /* This probably drew a large area; make sure it's all out there. */
359     XFlush(dpy);
360     /* Restore the foreground color. */
361     XSetForeground(dpy, gc, side->ui->fgcolor);
362 }
363 
364 /* Draw the view proper. */
365 
366 void
draw_map_view(Side * side,Map * map)367 draw_map_view(Side *side, Map *map)
368 {
369     int y1, y2, y, x1, x2, adj, vx, vy, vw, vh;
370     int halfheight = area.height / 2;
371 
372     draw_area_background(side, map);
373 
374     if (map->vp->vcx < 0 || map->vp->vcy < 0) {
375     	run_warning("doing a nasty hack");
376 	map->vp->vcx = map->vp->vcy = 2;
377     }
378     /* Compute the width and height. */
379     vw = min(area.width, map->pxw / map->vp->hw + 2);
380     vh = min(area.height, map->pxh / map->vp->hch + 2);
381 
382     vy = ((map->vp->totsh - map->vp->sy) / map->vp->hch) - vh;
383     /* Now adjust the bottom row so it doesn't go outside the area. */
384     if (vy < 0)
385       vy = 0;
386     if (vy > area.height - vh)
387       vy = area.height - vh;
388     /* Compute the leftmost "column". */
389     vx = map->vp->sx / map->vp->hw - vy / 2 - 1;
390     DGprintf("Set map %x viewport to be %dx%d @ %d,%d (nom center %d,%d)\n",
391 	     map, vw, vh, vx, vy,
392 	     map->vp->vcx, map->vp->vcy);
393     /* Compute top and bottom rows to be displayed. */
394     /* Include rows that will only be partly drawn. */
395     y1 = min(vy + vh, area.height - 1);
396     y2 = vy;
397     for (y = y1; y >= y2; --y) {
398 	/* Adjust the right and left bounds to fill the viewport as
399 	   much as possible, without going too far (the drawing code
400 	   will clip, but clipped drawing is still expensive). */
401 	/* could test by drawing viewport rect as lines... */
402 	adj = (y - vy) / 2;
403 	/* If the area doesn't wrap, then we might have to stop
404 	   drawing before we reach the edge of the viewport. */
405 	/* (is this really reliable?) */
406 	x1 = vx - (y - vy) / 2;
407 	x2 = x1 + vw + 2 /* bleah, shouldn't be necessary */;
408 	if (area.xwrap) {
409 	} else {
410 	    /* Truncate x's to stay within the area. */
411 	    x1 = max(0, min(x1, area.width-1));
412 	    x2 = max(0, min(x2, area.width));
413 	    /* If this row is entirely in the NE corner, don't draw
414                anything. */
415 	    if (x1 + y > area.width + halfheight)
416 	      continue;
417 	    /* If this row is entirely in the SW corner, don't draw
418                anything. */
419 	    if (x2 + y < halfheight)
420 	      continue;
421 	    /* If the row ends up in the NE corner, shorten it. */
422 	    if (x2 + y > area.width + halfheight)
423 	      x2 = area.width + halfheight - y;
424 	    /* If the row starts out in the SW corner, shorten it. */
425 	    if (x1 + y < halfheight)
426 	      x1 = halfheight - y;
427 	}
428 #if 0
429 	/* I don't understand the wrapx below, but Massimo says it
430 	   fixes bugs. -sts */
431 	draw_row(side, map, wrapx(x1), y, x2 - x1, FALSE);
432 #endif
433 	draw_row(side, map, x1, y, x2 - x1, FALSE);
434 	/* (should test for input events here, would respond better) */
435     }
436     /* Now draw the lat-long grid if asked to do so. */
437     if (map->vp->draw_meridians && map->vp->meridian_interval > 0)
438       draw_meridians(side, map);
439     draw_current(side, map);
440 }
441 
442 static void
draw_meridians(Side * side,Map * map)443 draw_meridians(Side *side,Map * map)
444 {
445     Display *dpy = side->ui->dpy;
446     GC gc = side->ui->gc;
447 
448     tmpside = side;
449     tmpmap = map;
450     XSetForeground(dpy, gc, side->ui->meridian_color);
451     XSetBackground(dpy, gc, side->ui->bgcolor);
452     XSetClipMask(dpy, gc, None);
453     XSetLineAttributes(dpy, gc, 1, LineSolid, CapButt, JoinMiter);
454     plot_meridians(map->vp, meridian_line_callback, meridian_text_callback);
455 }
456 
457 static void
meridian_line_callback(int x1,int y1,int x1f,int y1f,int x2,int y2,int x2f,int y2f)458 meridian_line_callback(int x1, int y1, int x1f, int y1f, int x2, int y2, int x2f, int y2f)
459 {
460     int sx1, sy1, sx2, sy2;
461 
462     xform_fractional(tmpside, tmpmap, x1, y1, x1f, y1f, &sx1, &sy1);
463     xform_fractional(tmpside, tmpmap, x2, y2, x2f, y2f, &sx2, &sy2);
464     XDrawLine(tmpside->ui->dpy, tmpmap->viewwin, tmpside->ui->gc,
465 	      sx1, sy1, sx2, sy2);
466 }
467 
468 static void
meridian_text_callback(int x1,int y1,int x1f,int y1f,char * str)469 meridian_text_callback(int x1, int y1, int x1f, int y1f, char *str)
470 {
471     int sx1, sy1;
472 
473     xform_fractional(tmpside, tmpmap, x1, y1, x1f, y1f, &sx1, &sy1);
474     draw_text(tmpside, tmpmap->viewwin, sx1 + 1, sy1 + 1, str,
475 	      tmpside->ui->fgcolor);
476 }
477 
478 /* The basic map drawing routine does an entire row at a time, which yields
479    order-of-magnitude speedups. */
480 
481 void
draw_row(Side * side,Map * map,int x0,int y0,int len,int clearit)482 draw_row(Side *side, Map *map, int x0, int y0, int len, int clearit)
483 {
484     int x, b, c, i;
485 
486     if (map->vp->draw_terrain) {
487 	draw_terrain_row(side, map, x0, y0, len, (clearit == -1));
488 	/* The relative ordering of these is quite important.  Note that
489 	   each should be prepared to run independently also, since the
490 	   other displays might have been turned off. */
491 	if (elevations_defined()
492 	    && map->vp->draw_elevations
493 	    && map->vp->angle == 90) {
494 	    draw_contours(side, map, x0, y0, len);
495 	}
496 	if (any_aux_terrain_defined()) {
497 	    if (bords_to_draw(map)) {
498 		for_all_terrain_types(b) {
499 		    if (t_is_border(b) && aux_terrain_defined(b)) {
500 			for (x = x0; x < x0 + len; ++x) {
501 			    draw_borders(side, map, x, y0, b);
502 			}
503 		    }
504 		}
505 	    }
506 	    /* Draw the connections on top of the borders. */
507 	    if (conns_to_draw(map)) {
508 		for_all_terrain_types(c) {
509 		    if (t_is_connection(c) && aux_terrain_defined(c)) {
510 			for (x = x0; x < x0 + len; ++x) {
511 			    draw_connections(side, map, x, y0, c);
512 			}
513 		    }
514 		}
515 	    }
516 	}
517     }
518     if (clouds_defined() && map->vp->draw_clouds) {
519 	draw_clouds_row(side, map, x0, y0, len);
520     }
521     if (temperatures_defined() && map->vp->draw_temperature && map->vp->hw > 10) {
522 	draw_temperature_row(side, map, x0, y0, len);
523     }
524     if (winds_defined() && map->vp->draw_winds && map->vp->hw > 10) {
525 	draw_winds_row(side, map, x0, y0, len);
526     }
527     /* Skip the top and bottom rows if they are edge rows. */
528     if (!between(1, y0, area.height - 2))
529       return;
530     /* Skip the rightmost and leftmost cells if on the edge. */
531     if (!inside_area(x0 + len - 1, y0))
532       --len;
533     if (!inside_area(x0, y0)) {
534 	++x0;
535 	--len;
536     }
537     if (len <= 0)
538       return;
539     if (features_defined() && map->vp->draw_feature_boundaries) {
540 	for (x = x0; x < x0 + len; ++x) {
541 	    draw_feature_boundary(side, map, x, y0, raw_feature_at(x, y0));
542 	}
543     }
544     if (features_defined() && map->vp->draw_feature_names && side->ui->legends) {
545 	for (i = 0; i < numfeatures; ++i) {
546 	    if (side->ui->legends[i].oy == y0) {
547 		draw_feature_name(side, map, i);
548 	    }
549 	}
550     }
551     /* Draw sparse things on top of the basic row. */
552     if (people_sides_defined() && map->vp->draw_people && map->vp->hw >= 8) {
553 	for (x = x0; x < x0 + len; ++x) {
554 	    draw_people(side, map, x, y0);
555 	}
556     }
557     /* Draw units. */
558     /* (Do names first, so that unit images don't disappear behind names
559        when densely packed) */
560     if (map->vp->draw_names && map->vp->hh >= 8) {
561 	for (x = x0; x < x0 + len; ++x) {
562 	    draw_legend(side, map, x, y0);
563 	}
564     }
565     if (map->vp->draw_units && map->vp->hw > 2) {
566 	for (x = x0; x < x0 + len; ++x) {
567 	    draw_units(side, map, x, y0);
568 	}
569     }
570 }
571 
572 char buffer[BUFSIZE];
573 
574 static void
draw_feature_name(Side * side,Map * map,int f)575 draw_feature_name(Side *side, Map *map, int f)
576 {
577     Legend *leg = &side->ui->legends[f];
578     int y = leg->oy;
579     int x = leg->ox;
580     int d = ((leg->dx + 1) * map->vp->hw * 9) / 10;
581     int i, b, l, lnew, sx0, sy0, sxc2, syc;
582     char *pad, *name;
583     XCharStruct *bounds;
584 
585 #if 0 /* uses slots that no longer exist */
586     if (leg->dist < 0 || y < map->vy || y > map->vy + map->vh)
587       return;
588 #endif
589 
590     name = feature_desc(find_feature(f + 1), buffer);
591     if (empty_string(name))
592       return;
593 
594     XSetFillStyle(side->ui->dpy, side->ui->gc, FillSolid);
595     XSetForeground(side->ui->dpy, side->ui->gc, side->ui->feature_color);
596     xform(side, map, x, y, &sx0, &sy0);
597     /* xform returns coordinates of the upper-left corner of the cell */
598     sxc2 = 2 * sx0 + (leg->dx + 1) * map->vp->hw; /* twice center x */
599     syc  = sy0 + map->vp->hh / 2;		  /* center y */
600     if (sxc2 + 2 * d < 0)
601       return;
602 
603     l = XTextWidth(side->ui->flegendfonts[0], name, strlen(name));
604     for (i = 1; i < 5; i++) {
605 	/* Check if the font is too tall for the current magnification. */
606 	bounds = &side->ui->flegendfonts[i]->max_bounds;
607 	if (bounds->ascent+bounds->descent > map->vp->hch)
608 	  break;
609 	lnew = XTextWidth(side->ui->flegendfonts[i], name, strlen(name));
610 	if (lnew > d) {
611 	    XSetFont(side->ui->dpy, side->ui->gc, side->ui->flegendfids[i-1]);
612 	    bounds = &side->ui->flegendfonts[i-1]->max_bounds;
613 	    XDrawString(side->ui->dpy, map->viewwin, side->ui->gc,
614 			(sxc2 - l) / 2, syc + (bounds->ascent - bounds->descent) / 2,
615 			name, strlen(name));
616 	    return;
617 	}
618 	l = lnew;
619     }
620     /* Retain the biggest usable font. */
621     if (i)
622       --i;
623 
624     XSetFont(side->ui->dpy, side->ui->gc, side->ui->flegendfids[i]);
625     bounds = &side->ui->flegendfonts[i]->max_bounds;
626     for (b = 1; b < 21; b++) {
627 	pad = pad_blanks(name, b);
628 	lnew = XTextWidth(side->ui->flegendfonts[i], pad, strlen(pad));
629 	if (lnew > d || b == 20) {
630 	    if (b == 20) {
631 		l = lnew;
632 		b++;
633 	    }
634 	    pad = pad_blanks(name, b - 1);
635  	    /* map->pxw is the window width */
636  	    if (sxc2 - l > 2 * map->pxw)
637 	      return;
638 	    XDrawString(side->ui->dpy, map->viewwin, side->ui->gc,
639 			(sxc2 - l) / 2,
640 			syc + (bounds->ascent - bounds->descent) / 2,
641 			pad, strlen(pad));
642 	    return;
643 	}
644 	l = lnew;
645     }
646 }
647 
648 static int
cell_drawing_info(Side * side,int x,int y,int power,int seeall,Pixmap * patp,long * colorp)649 cell_drawing_info(Side *side, int x, int y, int power, int seeall, Pixmap *patp, long *colorp)
650 {
651     int t;
652     enum whattouse rslt;
653 
654     *patp = None;
655     *colorp = side->ui->whitecolor;
656     if (seeall || terrain_visible(side, x, y)) {
657 	t = terrain_at(x, y);
658 	*patp = side->ui->terrpics[power][t];
659 	*colorp = side->ui->cellcolor[t];
660 	if (*colorp < 0)
661 	  *colorp = side->ui->blackcolor;
662 	rslt = side->ui->usewhat[power][t];
663     } else {
664 	rslt = dontdraw;
665     }
666     return rslt;
667 }
668 
669 static void
set_terrain_gc_for_image(Side * side,Image * timg)670 set_terrain_gc_for_image(Side *side, Image *timg)
671 {
672     X11Image *ximg;
673     Display *dpy = side->ui->dpy;
674     GC gc = side->ui->terrgc;
675 
676     if (timg != NULL) {
677 	ximg = (X11Image *) timg->hook;
678 	if (ximg != NULL) {
679 	    if (!side->ui->monochrome) {
680 		if (side->ui->dflt_color_terr_images) {
681 		    if (ximg->colr != None) {
682 			XSetFillStyle(dpy, gc, FillTiled);
683 			XSetTile(dpy, gc, ximg->colr);
684 		    } else if (ximg->mono != None) {
685 			XSetFillStyle(dpy, gc, FillOpaqueStippled);
686 			XSetStipple(dpy, gc, ximg->mono);
687 		    } else {
688 			XSetFillStyle(dpy, gc, FillSolid);
689 		    }
690 		} else {
691 		    XSetFillStyle(dpy, gc, FillSolid);
692 		}
693 	    } else if (ximg->mono != None) {
694 		XSetFillStyle(dpy, gc, FillOpaqueStippled);
695 		XSetStipple(dpy, gc, ximg->mono);
696 	    } else {
697 		/* No pattern, no color - what to do? */
698 		XSetFillStyle(dpy, gc, FillSolid);
699 	    }
700 	} else {
701 	    XSetFillStyle(dpy, gc, FillSolid);
702 	}
703     } else {
704 	XSetFillStyle(dpy, gc, FillSolid);
705     }
706 }
707 
708 /* This interfaces higher-level drawing decisions to the rendition of
709    individual pieces of display.  The rendering technique chosen
710    depends on what the init code has decided is appropriate given what
711    it found during init and what magnification the display is at.
712 
713    This routine is performance-critical; any improvements will
714    probably have a noticeable effect on the display.  But also note
715    that X's main bottleneck is the network connection, so it's more
716    useful to eliminate roundtrips to the server than anything else. */
717 
718 /* (should cache best images, never have to look up in here) */
719 
720 static void
draw_terrain_row(Side * side,Map * map,int x0,int y0,int len,int force)721 draw_terrain_row(Side *side, Map *map, int x0, int y0, int len, int force)
722 {
723     int x1, x, t, sx, sy, i = 0, j;
724     int w = map->vp->hw, h = map->vp->hh, p = map->vp->power;
725     int seeall = map->seeall, dogrid = map->vp->draw_grid;
726     int over, segover;
727     long color, segcolor;
728     enum whattouse drawmethod, segdrawmethod;
729     Pixmap pat, segpat;
730     Image *timg;
731     Display *dpy = side->ui->dpy;
732     GC gc = side->ui->terrgc;
733 
734     tmpdrawlighting = map->vp->draw_lighting;
735     tmpdrawcoverage = (!side->see_all && map->vp->draw_cover);
736 
737     x1 = x0;
738     segdrawmethod = (enum whattouse)cell_drawing_info(side, x0, y0, p, seeall,
739 				      &segpat, &segcolor);
740     segover = xtconq_cell_overlay(side, x0, y0);
741     for (x = x0; x < x0 + len + 1; ++x) {
742 	t = terrain_at(x, y0);
743 	drawmethod = (enum whattouse)cell_drawing_info(side, x, y0, p, seeall, &pat, &color);
744 	over = xtconq_cell_overlay(side, x, y0);
745 	/* Decide if the run is over and we need to dump some output. */
746 	if (x == x0 + len
747 	    || x == area.width
748 	    || drawmethod != segdrawmethod
749 	    || color != segcolor
750 	    || pat != segpat
751 	    || over != segover
752 	    || segdrawmethod == usepolygons
753 	    || force) {
754 	    /* Note: we might end up drawing something that matches
755 	       the background color, which wastes time, but apparently
756 	       the test "(segdrawmethod != dontdraw && segcolor !=
757 	       side->ui->bgcolor)" is not completely sufficient.
758 	       (should figure this one out sometime) */
759 	    t = terrain_at(x1, y0);
760 	    timg = best_image(side->ui->timages[t], w, h);
761 	    xform(side, map, x1, y0, &sx, &sy);
762 	    /* Last-minute fixup for when we're erasing a cell. */
763 	    if (force && segdrawmethod == dontdraw) {
764 		segdrawmethod = side->ui->usewhat[p][t];
765 		/* this must match the bg set in draw_area_background */
766 		segcolor = side->ui->gridcolor /* bgcolor */;
767 		timg = NULL;
768 	    }
769 	    XSetForeground(dpy, gc, segcolor);
770 	    XSetBackground(dpy, gc, side->ui->whitecolor);
771 	    switch (segdrawmethod) {
772 	    case dontdraw:
773 	      /* Don't do anything. */
774 	      break;
775 	    case useblocks:
776 	      XSetClipMask(dpy, gc, None);
777 	      set_terrain_gc_for_image(side, timg);
778 	      XFillRectangle(dpy, map->viewwin, gc, sx, sy, i * w, h);
779 	      if (segover < 0) {
780 		  enum grayshade shade = gray;
781 
782 		  if (segover == -2)
783 		    shade = darkgray;
784 		  XSetFillStyle(dpy, gc, FillStippled);
785 		  XSetStipple(dpy, gc, side->ui->grays[shade]);
786 		  XSetForeground(dpy, gc, side->ui->blackcolor);
787 		  XFillRectangle(dpy, map->viewwin, gc, sx, sy, i * w, h);
788 		  XSetFillStyle(dpy, gc, FillSolid);
789 	      }
790 	      break;
791 	    case usepictures:
792 	      if (dogrid) {
793 		  XSetClipMask(dpy, gc, side->ui->bhexpics[p]);
794 	      } else {
795 		  XSetClipMask(dpy, gc, side->ui->hexpics[p]);
796 	      }
797 	      set_terrain_gc_for_image(side, timg);
798 	      for (j = 0; j < i; ++j) {
799 		  xform(side, map, x1 + j, y0, &sx, &sy);
800 		  XSetClipOrigin(dpy, gc, sx, sy);
801 
802 		  /* In tcltk, this is actually a user preference.
803 		     For now, we'll hardcode images (which is the tcltk
804 		     default) - does that increase X server traffic or
805 		     have some other subtle reason for being a
806 		     preference?  */
807 		  /* Upon startup (not the very first call but a few
808 		     in), the XCopyArea was crashing with a Bad
809 		     Drawable, but only if there were two players on
810 		     different X displays.  I guess we were passing
811 		     the window for the wrong player (?).  Anyway, until
812 		     that is fixed, turn off terrain images. */
813 		  if (/* we should display images */ 0) {
814 		      int quasirand = abs(((x1 + j) * (x1 + j) + y0 * y0 ) % 101);
815 		      X11Image *ximg;
816 		      Image *subimg;
817 
818 		      subimg = timg;
819 		      if (timg->numsubimages > 0 && timg->subimages) {
820 			  subimg =
821 			      timg->subimages[quasirand % timg->numsubimages];
822 		      }
823 		      ximg = (X11Image*) subimg->hook;
824 		      if (subimg
825 			  && ximg
826 			  && ximg->colr != None
827 			  && !timg->istile) {
828 			  XCopyArea(dpy, ximg->colr, map->viewwin, gc,
829 				    0, 0, w, h, sx, sy);
830 		      } else {
831 			  XFillRectangle(dpy, map->viewwin, gc, sx, sy, w, h);
832 		      }
833 		  } else {
834 		      /* User doesn't want to see images. */
835 		      XFillRectangle(dpy, map->viewwin, gc, sx, sy, w, h);
836 		  }
837 
838 		  if (segover < 0) {
839 		      enum grayshade shade = gray;
840 
841 		      if (segover == -2)
842 			shade = darkgray;
843 		      XSetFillStyle(dpy, gc, FillStippled);
844 		      XSetStipple(dpy, gc, side->ui->grays[shade]);
845 		      XSetForeground(dpy, gc, side->ui->blackcolor);
846 		      XFillRectangle(dpy, map->viewwin, gc, sx, sy, i * w, h);
847 		      XSetFillStyle(dpy, gc, FillSolid);
848 		      XSetForeground(dpy, gc, segcolor);
849 		  }
850 	      }
851 	      break;
852 	    case usepolygons:
853 	      /* No clipping when we're drawing big polygons. */
854 	      XSetClipMask(dpy, gc, None);
855 	      set_terrain_gc_for_image(side, timg);
856 	      draw_hex_polygon(side, map, map->viewwin, gc, sx, sy, p, segover, dogrid);
857 	    }
858 	    /* Reset everything for the next run. */
859 	    i = 0;
860 	    x1 = x;
861 	    segdrawmethod = drawmethod;
862 	    segpat = pat;
863 	    segcolor = color;
864 	    segover = over;
865 	}
866 	++i;
867     }
868 }
869 
870 static void
draw_contours(Side * side,Map * map,int x0,int y0,int len)871 draw_contours(Side *side, Map *map, int x0, int y0, int len)
872 {
873     int x, y;
874     int i, sx, sy, numlines;
875     LineSegment *lines;
876     Display *dpy = side->ui->dpy;
877     Window win = map->viewwin;
878     GC gc = side->ui->gc;
879 
880     if (map->vp->contour_interval < 1)
881       return;
882     XSetForeground(dpy, gc, side->ui->contour_color);
883     XSetClipMask(dpy, gc, None);
884     XSetLineAttributes(dpy, gc, 1, LineSolid, CapButt, JoinMiter);
885     y = y0;
886     for (x = x0; x < x0 + len; ++x) {
887 	if (terrain_visible(side, x, y0)) {
888 	    xform(side, map, x, y, &sx, &sy);
889 	    contour_lines_at(map->vp, x, y, sx, sy, &lines, &numlines);
890 	    for (i = 0; i < numlines; ++i) {
891 		XDrawLine(dpy, win, gc,
892 			  lines[i].sx1, lines[i].sy1, lines[i].sx2, lines[i].sy2);
893 	    }
894 	}
895     }
896 }
897 
898 #if 0
899 static void
900 draw_elevations(Side *side, Map *map, int x0, int y0, int len)
901 {
902     int x, sx, sy;
903 
904     if (map->vp->hw < 20)
905       return;
906     for (x = x0; x < x0 + len - 1; ++x) {
907 	if (terrain_visible(side, x, y0)) {
908 	    sprintf(spbuf, "%d", elev_at(x, y0));
909 	    xform(side, map, x, y0, &sx, &sy);
910 	    draw_text(side, map->viewwin, sx + 5, sy + map->vp->uh / 2,
911 		      spbuf, side->ui->fgcolor);
912 	}
913     }
914 }
915 #endif
916 
917 static void
draw_clouds_row(Side * side,Map * map,int x0,int y0,int len)918 draw_clouds_row(Side *side, Map *map, int x0, int y0, int len)
919 {
920   int x, sx, sy;
921 
922     for (x = x0; x < x0 + len - 1; ++x) {
923 	if (map->seeall || terrain_visible(side, x, y0)) {
924 	    sprintf(spbuf, "%d", cloud_view(side, x, y0));
925 	    xform(side, map, x, y0, &sx, &sy);
926 	    draw_text(side, map->viewwin, sx + 5, sy + map->vp->uh / 2,
927 		      spbuf, side->ui->fgcolor);
928 	}
929     }
930 }
931 
932 static void
draw_temperature_row(Side * side,Map * map,int x0,int y0,int len)933 draw_temperature_row(Side *side, Map *map, int x0, int y0, int len)
934 {
935     int x, sx, sy;
936 
937     for (x = x0; x < x0 + len - 1; ++x) {
938 	if (map->seeall || terrain_visible(side, x, y0)) {
939 	    sprintf(spbuf, "%d", temperature_view(side, x, y0));
940 	    xform(side, map, x, y0, &sx, &sy);
941 	    draw_text(side, map->viewwin, sx + 5, sy + map->vp->uh / 2,
942 		      spbuf, side->ui->fgcolor);
943 	}
944     }
945 }
946 
947 static void
draw_winds_row(Side * side,Map * map,int x0,int y0,int len)948 draw_winds_row(Side *side, Map *map, int x0, int y0, int len)
949 {
950     int x, sx, sy;
951 
952     for (x = x0; x < x0 + len - 1; ++x) {
953 	if (map->seeall || terrain_visible(side, x, y0)) {
954 	    sprintf(spbuf, "%d", wind_view(side, x, y0));
955 	    xform(side, map, x, y0, &sx, &sy);
956 	    draw_text(side, map->viewwin, sx + 5, sy + map->vp->uh / 2,
957 		      spbuf, side->ui->fgcolor);
958 	}
959     }
960 }
961 
962 /* Draw a single unit icon as appropriate.  This *also* has a bunch of
963    details to worry about: centering of icon in cell, clearing a rectangular
964    area for the icon, picking a color for the unit, using either a bitmap
965    or font char, and adding a side emblem. */
966 
967 static void
draw_units(Side * side,Map * map,int x,int y)968 draw_units(Side *side, Map *map, int x, int y)
969 {
970     int sx, sy, sw, sh, u, s, osx, osy, numvis = 0;
971     int uw = map->vp->uw, uh = map->vp->uh, didone;
972     Unit *unit, *unit2;
973     UnitView *uview, *uview2;
974     Display *dpy = side->ui->dpy;
975     GC gc = side->ui->unitgc;
976 
977     /* This case is for the display of all units. */
978     if (map->vp->show_all) {
979 	if (uw <= 16) {
980 	    /* At smaller mags we choose a single unit to display. */
981 	    unit = unit_at(x, y);
982 	    if (unit == NULL)
983 	      return;
984 	    /* Prefer to display one of our own units. */
985 	    for_all_stack(x, y, unit2) {
986 		if (unit2->transport)
987 		    continue;
988 		if (unit2->side == side) {
989 		    unit = unit2;
990 		    break;
991 		}
992 	    }
993 	    s = side_number(unit->side);
994 	    xform(side, map, x, y, &sx, &sy);
995 	    if (big_unit_images) {
996 		sw = map->vp->hw;
997 		sh = map->vp->hw;
998 	    } else {
999 		sw = map->vp->uw;
1000 		sh = map->vp->uh;
1001 	    }
1002 	    /* Adjust to unit part of cell. */
1003 	    sx += (map->vp->hw - sw) / 2;
1004 	    sy += (map->vp->hh - sh) / 2;
1005 	    if (sw >= 8
1006 	        /* Always draw advanced units in boxes at low magnifications. */
1007 	        && (
1008 #if 0
1009 	        	(u_advanced(unit->type) && sw < 16)
1010 		/* Always draw boxes if there are visible occupants. */
1011 	            ||
1012 #endif
1013 	            (unit->occupant
1014 		       && map->vp->draw_occupants))) {
1015 			/* Draw a "grouping box", in white, but with no occs
1016 			   actually drawn. */
1017 			XSetClipMask(dpy, gc, None);
1018 			XSetForeground(dpy, gc, side->ui->whitecolor);
1019 			XFillRectangle(dpy, map->viewwin, gc,
1020 				       sx + 1, sy + 1, uw - 2, uh - 2);
1021 			/* Put a black border around it, for better contrast. */
1022 			XSetForeground(dpy, gc, side->ui->blackcolor);
1023 			XDrawRectangle(dpy, map->viewwin, gc,
1024 				       sx + 1, sy + 1, uw - 2, uh - 2);
1025 		    }
1026 		    draw_unit_image(unit->imf, side, map->viewwin, sx, sy, uw, uh,
1027 				    s, -1, -1, !completed(unit));
1028 		    /* Indicate that other units are stacked here also. */
1029 		    if (unit->nexthere != NULL && uw > 8) {
1030 			osx = sx + uw / 2 - 6;
1031 			osy = sy + uh - 2;
1032 			/* (should be able to do with one fill?) */
1033 			XSetClipOrigin(dpy, gc, osx, osy - 1);
1034 			XSetForeground(dpy, gc, side->ui->whitecolor);
1035 			XSetClipMask(dpy, gc, None);
1036 			XFillRectangle(dpy, map->viewwin, gc, osx, osy - 1, 12, 4);
1037 			XSetForeground(dpy, gc, side->ui->blackcolor);
1038 			XSetClipMask(dpy, gc, side->ui->dots);
1039 			XFillRectangle(dpy, map->viewwin, gc, osx, osy - 1, 12, 4);
1040 		    }
1041 		    if (map->vp->draw_names) {
1042 			draw_unit_name(side, map, unit, sx, sy, uw, uh);
1043 		    }
1044 	} else {
1045 	    /* At 32x32 and up, we can display several units in the stack. */
1046 	    for_all_stack(x, y, unit) {
1047 		    x_xform_unit(side, map, unit, &sx, &sy, &sw, &sh);
1048 		    draw_unit_and_occs(side, map, unit, sx, sy, sw, sh);
1049 	    }
1050 	}
1051     } else {
1052 	/* Count the visible units at the top level. */
1053 	numvis = 0;
1054 	for_all_view_stack(side, x, y, uview) {
1055 		if (uview->transport == NULL)
1056 	      	    ++numvis;
1057 	}
1058 	    if (uw <= 16) {
1059 		/* At smaller mags we choose a single unit to display. */
1060 		uview = unit_view_at(side, x, y);
1061 		if (uview == NULL)
1062 		  return;
1063 		/* Prefer to display one of our own units. */
1064 		for_all_view_stack(side, x, y, uview2) {
1065 			if (uview2->transport)
1066 			    continue;
1067 			if (uview2->siden == side->id) {
1068 		    		uview = uview2;
1069 				break;
1070 			}
1071 		}
1072 		s = uview->siden;
1073 		xform(side, map, x, y, &sx, &sy);
1074 		if (big_unit_images) {
1075 			sw = map->vp->hw;
1076 			sh = map->vp->hw;
1077 		} else {
1078 			sw = map->vp->uw;
1079 			sh = map->vp->uh;
1080 		}
1081 		/* Adjust to unit part of cell. */
1082 		    sx += (map->vp->hw - sw) / 2;
1083 		    sy += (map->vp->hh - sh) / 2;
1084 		if (sw >= 8
1085     		    /* Always draw advanced units in boxes at low magnifications. */
1086 		    && (
1087 #if 0
1088 		    (u_advanced(uview->type) && sw < 16)
1089 		    /* Always draw boxes if there are visible occupants. */
1090 		    	||
1091 #endif
1092 		    	(uview->occupant
1093 			    && map->vp->draw_occupants))) {
1094 			/* Draw a "grouping box", in white, but with no occs
1095 			   actually drawn. */
1096 			XSetClipMask(dpy, gc, None);
1097 			XSetForeground(dpy, gc, side->ui->whitecolor);
1098 			XFillRectangle(dpy, map->viewwin, gc,
1099 				       sx + 1, sy + 1, uw - 2, uh - 2);
1100 			/* Put a black border around it, for better contrast. */
1101 			XSetForeground(dpy, gc, side->ui->blackcolor);
1102 			XDrawRectangle(dpy, map->viewwin, gc,
1103 				       sx + 1, sy + 1, uw - 2, uh - 2);
1104 		    }
1105 		    draw_unit_image(uview->imf, side, map->viewwin, sx, sy, uw, uh,
1106 				    s, -1, -1, !uview->complete);
1107 		/* Indicate that other units are stacked here also. */
1108 		if (numvis > 1 && sw > 8) {
1109 			osx = sx + uw / 2 - 6;
1110 			osy = sy + uh - 2;
1111 			/* (should be able to do with one fill?) */
1112 			XSetClipOrigin(dpy, gc, osx, osy - 1);
1113 			XSetForeground(dpy, gc, side->ui->whitecolor);
1114 			XSetClipMask(dpy, gc, None);
1115 			XFillRectangle(dpy, map->viewwin, gc, osx, osy - 1, 12, 4);
1116 			XSetForeground(dpy, gc, side->ui->blackcolor);
1117 			XSetClipMask(dpy, gc, side->ui->dots);
1118 			XFillRectangle(dpy, map->viewwin, gc, osx, osy - 1, 12, 4);
1119 		    }
1120 		    if (map->vp->draw_names && uview->unit) {
1121 			draw_unit_name(side, map, uview->unit, sx, sy, uw, uh);
1122 		    }
1123 	} else {
1124 	    /* At 32x32 and up, we can display several units in the stack. */
1125 	    for_all_view_stack(side, x, y, uview) {
1126 		    x_xform_unit_view(side, map, uview, &sx, &sy, &sw, &sh);
1127 		    draw_unit_view_and_occs(side, map, uview, sx, sy, sw, sh);
1128 	    }
1129 	}
1130     }
1131 }
1132 
1133 static void
draw_unit_and_occs(Side * side,Map * map,Unit * unit,int sx,int sy,int sw,int sh)1134 draw_unit_and_occs(Side *side, Map *map, Unit *unit, int sx, int sy, int sw, int sh)
1135 {
1136     int u = unit->type, s = side_number(unit->side), sx2, sy2, sw2, sh2;
1137     Unit *occ;
1138     Display *dpy = side->ui->dpy;
1139     GC gc = side->ui->unitgc;
1140 
1141     /* If an occupant's side is the same as its transport's, then there's
1142        really no need to draw its side emblem, since the transport's emblem
1143        will also be visible. */
1144     if (unit->transport && unit->side == unit->transport->side)
1145       s = -1;
1146     if (sw > 8
1147 	    && unit->occupant
1148 	    && map->vp->draw_occupants) {
1149 	/* Draw a white box to indicate the grouping. */
1150 	XSetClipMask(dpy, gc, None);
1151 	XSetForeground(dpy, gc, side->ui->whitecolor);
1152 	XFillRectangle(dpy, map->viewwin, gc,
1153 		       sx + 1, sy + 1, sw - 2, sh - 2);
1154 	/* Put a black border around it, for better contrast. */
1155 	XSetForeground(dpy, gc, side->ui->blackcolor);
1156 	XDrawRectangle(dpy, map->viewwin, gc,
1157 		       sx + 1, sy + 1, sw - 2, sh - 2);
1158 	/* Draw the transport's image. */
1159 	x_xform_occupant(side, map, unit, unit, sx, sy, sw, sh,
1160 		       &sx2, &sy2, &sw2, &sh2);
1161 	draw_unit_image(unit->imf, side, map->viewwin, sx2, sy2, sw2, sh2, s,
1162 								-1, -1, !completed(unit));
1163 	if (map->vp->draw_names) {
1164 		draw_unit_name(side, map, unit, sx2, sy2, sw2, sh2);
1165 	}
1166 	    for_all_occupants(unit, occ) {
1167 		x_xform_occupant(side, map, unit, occ, sx, sy, sw, sh,
1168 				 &sx2, &sy2, &sw2, &sh2);
1169 		draw_unit_and_occs(side, map, occ, sx2, sy2, sw2, sh2);
1170 	    }
1171     } else {
1172 	draw_unit_image(unit->imf, side, map->viewwin, sx, sy, sw, sh, s,
1173 								-1, -1, !completed(unit));
1174 	if (map->vp->draw_names) {
1175 	  draw_unit_name(side, map, unit, sx, sy, sw, sh);
1176     	}
1177     }
1178 }
1179 
1180 static void
draw_unit_view_and_occs(Side * side,Map * map,UnitView * uview,int sx,int sy,int sw,int sh)1181 draw_unit_view_and_occs(Side *side, Map *map, UnitView *uview, int sx, int sy, int sw, int sh)
1182 {
1183     int u = uview->type, s = uview->siden, sx2, sy2, sw2, sh2;
1184     UnitView *occview;
1185     Display *dpy = side->ui->dpy;
1186     GC gc = side->ui->unitgc;
1187 
1188     /* If an occupant's side is the same as its transport's, then there's
1189        really no need to draw its side emblem, since the transport's emblem
1190        will also be visible. */
1191     if (uview->transport && uview->siden == uview->transport->siden)
1192       s = -1;
1193     if (sw > 8
1194 	    && uview->occupant
1195 	    && map->vp->draw_occupants) {
1196 	/* Draw a white box to indicate the grouping. */
1197 	XSetClipMask(dpy, gc, None);
1198 	XSetForeground(dpy, gc, side->ui->whitecolor);
1199 	XFillRectangle(dpy, map->viewwin, gc,
1200 		       sx + 1, sy + 1, sw - 2, sh - 2);
1201 	/* Put a black border around it, for better contrast. */
1202 	XSetForeground(dpy, gc, side->ui->blackcolor);
1203 	XDrawRectangle(dpy, map->viewwin, gc,
1204 		       sx + 1, sy + 1, sw - 2, sh - 2);
1205 	/* Draw the transport's image. */
1206 	x_xform_occupant_view(side, map, uview, uview, sx, sy, sw, sh,
1207 		       &sx2, &sy2, &sw2, &sh2);
1208 	draw_unit_image(uview->imf, side, map->viewwin, sx2, sy2, sw2, sh2, s,
1209 								-1, -1, !uview->complete);
1210 	if (map->vp->draw_names && uview->unit) {
1211 		draw_unit_name(side, map, uview->unit, sx2, sy2, sw2, sh2);
1212 	}
1213 	    for_all_occupant_views(uview, occview) {
1214 		x_xform_occupant_view(side, map, uview, occview, sx, sy, sw, sh,
1215 				 					&sx2, &sy2, &sw2, &sh2);
1216 		draw_unit_view_and_occs(side, map, occview, sx2, sy2, sw2, sh2);
1217 	    }
1218     } else {
1219 	draw_unit_image(uview->imf, side, map->viewwin, sx, sy, sw, sh, s,
1220 								-1, -1, !uview->complete);
1221 	if (map->vp->draw_names && uview->unit) {
1222 	  draw_unit_name(side, map, uview->unit, sx, sy, sw, sh);
1223     	}
1224     }
1225 }
1226 
1227 static void
draw_unit_name(Side * side,Map * map,Unit * unit,int sx,int sy,int sw,int sh)1228 draw_unit_name(Side *side, Map *map, Unit *unit, int sx, int sy, int sw, int sh)
1229 {
1230     if (!empty_string(unit->name)) {
1231 	draw_legend_text(side, map->viewwin,
1232 			 sx + sw,
1233 			 sy + sh / 2 - 5,
1234 			 map->vp->power, unit->name, side->ui->fgcolor, TRUE);
1235     }
1236 }
1237 
1238 
1239 /* Indicate what kind of people are living in the given cell. */
1240 
1241 static void
draw_people(Side * side,Map * map,int x,int y)1242 draw_people(Side *side, Map *map, int x, int y)
1243 {
1244     int pop, sx, sy, sw, sh, ex, ey, ew, eh, dir, x1, y1, pop1;
1245     int bordercell = FALSE;
1246     Side *side2;
1247 
1248     if (!terrain_visible(side, x, y))
1249       return;
1250     pop = people_side_at(x, y);
1251     side2 = side_n(pop);
1252     if (!side2)
1253       return;
1254     xform(side, map, x, y, &sx, &sy);
1255     /* Decide which edges are borders of the country. */
1256     for_all_directions(dir) {
1257 	if (point_in_dir(x, y, dir, &x1, &y1)) {
1258 	    pop1 = people_side_at(x1, y1);
1259 	    if (pop != pop1) {
1260 		if (pop1 != NOBODY) {
1261 		    draw_country_border_line(side, map->viewwin,
1262 					     sx, sy, dir, map->vp->power);
1263 		} else {
1264 		    /* should draw in gray instead? */
1265 		    draw_country_border_line(side, map->viewwin,
1266 					     sx, sy, dir, map->vp->power);
1267 		}
1268 		bordercell = TRUE;
1269 	    }
1270 	}
1271     }
1272     /* Draw an emblem for the people in the cell. */
1273     if (map->vp->draw_people && bordercell) {
1274 	sw = map->vp->hw;  sh = map->vp->hh;
1275 	ew = min(sw, max(8, sw / 4));  eh = min(sh, max(8, sh / 4));
1276 	ex = sx + sw / 2 - ew / 2;  ey = sy + sh / 2 - eh / 2;
1277 	draw_side_emblem(side, map->viewwin, ex, ey, ew, eh,
1278 			 side_number(side2), 0);
1279     }
1280 }
1281 
1282 /* Draw three borders of the given cell. */
1283 
1284 /* (do we need another routine to repair all six borders?) */
1285 
1286 static void
draw_borders(Side * side,Map * map,int x,int y,int b)1287 draw_borders(Side *side, Map *map, int x, int y, int b)
1288 {
1289     int dir, bitmask = 0, sx, sy;
1290 
1291     if (!terrain_visible(side, x, y)
1292 	|| !any_borders_at(x, y, b))
1293       return;
1294     for_all_directions(dir) {
1295 	if (border_at(x, y, dir, b) && seen_border(side, x, y, dir)) {
1296 	    bitmask |= 1 << dir;
1297 	}
1298     }
1299     if (bitmask != 0) {
1300 	xform(side, map, x, y, &sx, &sy);
1301 	draw_border_line_mult(side, map, map->viewwin, sx, sy,
1302 				  bitmask, map->vp->power, b);
1303     }
1304 }
1305 
1306 /* Draw three connections of the given cell. */
1307 
1308 /* Actually this draws all six half-connections.  It also only draws the
1309    connection if the underlying terrain is different. */
1310 
1311 static void
draw_connections(Side * side,Map * map,int x,int y,int t)1312 draw_connections(Side *side, Map *map, int x, int y, int t)
1313 {
1314     int dir, bitmask = 0, sx, sy;
1315 
1316     if (bwid[map->vp->power] == 0)
1317       return;
1318     if (!terrain_visible(side, x, y))
1319       return;
1320     xform(side, map, x, y, &sx, &sy);
1321     for_all_directions(dir) {
1322 	if (connection_at(x, y, dir, t)) {
1323 	    bitmask |= 1 << dir;
1324 	}
1325     }
1326     if (bitmask != 0) {
1327 	draw_connection_line_mult(side, map->viewwin,
1328 				      sx, sy, bitmask,
1329 				      map->vp->power, t);
1330     }
1331 }
1332 
1333 /* Draw any text that should be associated with this cell. */
1334 
1335 /* (could precompute what the string will lap over and move or truncate str),
1336    should be deterministic for each mag, so redraw doesn't scramble */
1337 
1338 /* do features, label at a cell with nothing else, and declared as the
1339    feature's "center" */
1340 
1341 /* Black/white text should be consistent for each period, use mask only
1342    to fix difficulties. */
1343 
1344 static void
draw_legend(Side * side,Map * map,int x,int y)1345 draw_legend(Side *side, Map *map, int x, int y)
1346 {
1347     int sx, sy, pixlen;
1348     char *featname;
1349     Feature *feature;
1350 
1351     if (!inside_area(x, y))
1352       return;
1353     if (!terrain_visible(side, x, y))
1354       return;
1355     /* feature object should specify legend's position */
1356     feature = feature_at(x, y);
1357     if (feature != NULL) {
1358 	if (feature->size == 1) {
1359 	    featname = feature_name_at(x, y);
1360 	    if (featname != NULL) {
1361 		pixlen = strlen(featname) * 8;
1362 		xform(side, map, x, y, &sx, &sy);
1363 		draw_legend_text(side, map->viewwin,
1364 				 sx + 1 + (pixlen > map->vp->hw ? 2 :
1365 					   (map->vp->hw/2 - pixlen / 2)),
1366 				 sy - 6 + map->vp->hh / 2,
1367 				 map->vp->power, featname,
1368 				 side->ui->fgcolor, TRUE);
1369 	    }
1370 	}
1371     }
1372 }
1373 
1374 /* Cursor drawing also draws the unit in some other color if it's not the
1375    "top-level" unit in a cell. */
1376 
1377 /* "color unders" here are not correct */
1378 
1379 void
draw_current(Side * side,Map * map)1380 draw_current(Side *side, Map *map)
1381 {
1382     int sx, sy, sw, sh;
1383     int uw = map->vp->uw, uh = map->vp->uh;
1384     enum grayshade shade = black;
1385     Unit *unit = NULL;
1386     Display *dpy = side->ui->dpy;
1387     GC gc = side->ui->unitgc;
1388     Window win;
1389 
1390     if (in_play(map->curunit)) {
1391 	unit = map->curunit;
1392 	/* Compute the bounding box we're going to hilite. */
1393 	if (map->vp->power >= 5) { /* not ideal test */
1394 	    x_xform_unit_self(side, map, unit, &sx, &sy, &sw, &sh);
1395 	} else {
1396 	    xform(side, map, unit->x, unit->y, &sx, &sy);
1397 	    /* Adjust to unit part of cell. */
1398 	    sx += (map->vp->hw - uw) / 2;  sy += (map->vp->hh - uh) / 2;
1399 	    sw = uw;  sh = uh;
1400 	}
1401 	/* Maybe redraw the unit that the cursor is showing. */
1402 	if (unit->transport != NULL) {
1403 	    if (side->ui->monochrome) {
1404 		draw_unit_image(unit->imf, side, map->viewwin, sx, sy, sw, sh,
1405 				-1, -1, -1, !completed(unit));
1406 	    } else {
1407 		/* Leave any underlying image alone, but draw over it
1408 		   in a different color. */
1409 		draw_unit_image(unit->imf, side, map->viewwin, sx, sy, sw, sh,
1410 				-1, side->ui->diffcolor, -1, !completed(unit));
1411 	    }
1412 	}
1413     } else if (inside_area(map->curx, map->cury)) {
1414 	xform(side, map, map->curx, map->cury, &sx, &sy);
1415 	/* Adjust to unit part of cell. */
1416 	sx += (map->vp->hw - uw) / 2;  sy += (map->vp->hh - uh) / 2;
1417 	sw = uw;  sh = uh;
1418     } else {
1419 	/* Nothing to draw, get out of here. */
1420 	return;
1421     }
1422     /* Draw the cursor icon proper. */
1423     win = map->viewwin;
1424     /* Black is for units that can still act, dark gray for actors
1425        that used all acp, gray if the unit can't do anything. */
1426     shade = (unit ?
1427 	     ((unit->act && unit->act->initacp > 0) ?
1428 	      (has_acp_left(unit) ? black : darkgray) :
1429 	      gray) :
1430 	     black);
1431     /* Box at larger sizes need to come in slightly, otherwise we
1432 	   get "dirt" pixels in the hex grid. */
1433     if (sw > 16 && sh > 16)
1434       sh -= 1;
1435     XSetClipMask(dpy, gc, None);
1436     XSetForeground(dpy, gc, (map->curtool == movetool && map->anim_state % 2 == 1) ? side->ui->blackcolor : side->ui->whitecolor);
1437     if (sw >= 2 && sh >= 2) {
1438 	XDrawRectangle(dpy, win, gc, sx, sy, sw - 1, sh - 1);
1439     } else {
1440 	XFillRectangle(dpy, win, gc, sx, sy, sw, sh);
1441     }
1442     if (sw >= 4 && sh >= 4) {
1443 	if (shade != black) {
1444 	    XSetFillStyle(dpy, gc, FillOpaqueStippled);
1445 	    XSetStipple(dpy, gc, side->ui->grays[shade]);
1446 	}
1447 	XSetForeground(dpy, gc, side->ui->blackcolor);
1448 	XDrawRectangle(dpy, win, gc, sx + 1, sy + 1, sw - 3, sh - 3);
1449 	/* If the box is not too small, thicken the inner rectangle. */
1450 	if (sw >= 8 && sh >= 8
1451 	    && (unit ?
1452 		!(unit->plan && (unit->plan->asleep || unit->plan->reserve))
1453 		: TRUE)) {
1454 	    if (sw > 8 && sh > 8 && unit == NULL)
1455 	      XSetForeground(dpy, gc, side->ui->whitecolor);
1456 	    XDrawRectangle(dpy, win, gc, sx + 2, sy + 2, sw - 5, sh - 5);
1457 	}
1458     }
1459     if (shade != black) {
1460 	XSetFillStyle(dpy, gc, FillSolid);
1461     }
1462 }
1463 
1464 /* Get rid of curunit indicator by redrawing the cell. */
1465 
1466 void
erase_current(Side * side,Map * map,int x,int y,Unit * unit)1467 erase_current(Side *side, Map *map, int x, int y, Unit *unit)
1468 {
1469     /* (should use unit to decide whether to redraw only its part of
1470        the cell, instead of doing whole cell) */
1471     if (in_area(x, y)) {
1472 	draw_row(side, map, x, y, 1, -1);
1473     } else if (unit != NULL && in_area(unit->x, unit->y)) {
1474 	draw_row(side, map, unit->x, unit->y, 1, -1);
1475     }
1476 }
1477 
1478 void
draw_blast_image(Side * side,Map * map,int sx,int sy,int sw,int sh,int blasttype)1479 draw_blast_image(Side *side, Map *map, int sx, int sy, int sw, int sh, int blasttype)
1480 {
1481     int sx2, sy2;
1482     Display *dpy = side->ui->dpy;
1483     GC gc = side->ui->gc;
1484 
1485     if (sw >= 16 && sh >= 16) {
1486 	sx2 = sx + (sw - 16) / 2;  sy2 = sy + (sh - 16) / 2;
1487 	XSetClipMask(dpy, gc, side->ui->hitpics[blasttype]);
1488 	XSetClipOrigin(dpy, gc, sx2 + 1, sy2 + 1);
1489 	XSetForeground(dpy, gc, side->ui->blackcolor);
1490 	XFillRectangle(dpy, map->viewwin, gc, sx2 + 1, sy2 + 1, 16, 16);
1491 	flush_output(side);
1492 	XSetClipOrigin(dpy, gc, sx2, sy2);
1493 	XSetForeground(dpy, gc, side->ui->badcolor);
1494 	XFillRectangle(dpy, map->viewwin, gc, sx2, sy2, 16, 16);
1495 	flush_output(side);
1496     } else {
1497 	XSetForeground(dpy, gc, side->ui->badcolor);
1498 	XFillRectangle(dpy, map->viewwin, gc, sx, sy, sw, sh);
1499 	flush_output(side);
1500 	XSetForeground(dpy, gc, side->ui->blackcolor);
1501 	XFillRectangle(dpy, map->viewwin, gc, sx, sy, sw, sh);
1502 	flush_output(side);
1503 	XSetFunction(dpy, gc, GXcopy);
1504     }
1505 }
1506 
1507 /* Flash an area of the screen. */
1508 
1509 void
invert_unit_subarea(Side * side,Map * map,int x,int y)1510 invert_unit_subarea(Side *side, Map *map, int x, int y)
1511 {
1512     int sx, sy;
1513 
1514     xform(side, map, x, y, &sx, &sy);
1515     sx += (map->vp->hw - map->vp->uw) / 2;
1516     sy += (map->vp->hh - map->vp->uh) / 2;
1517     XSetFunction(side->ui->dpy, side->ui->gc, GXinvert);
1518     XFillRectangle(side->ui->dpy, map->viewwin, side->ui->gc,
1519 		   sx, sy, map->vp->uw, map->vp->uh);
1520     flush_output(side);
1521     XSetFunction(side->ui->dpy, side->ui->gc, GXcopy);
1522 }
1523 
1524 static void
draw_feature_boundary(Side * side,Map * map,int x,int y,int fid)1525 draw_feature_boundary(Side *side, Map *map, int x, int y, int fid)
1526 {
1527     int wid, p, wid2, d, color, fid0, x1, y1, sx, sy;
1528     Display *dpy = side->ui->dpy;
1529     GC gc = side->ui->bdrygc;
1530     Pixmap graylev;
1531 
1532     if (!terrain_visible(side, x, y))
1533       return;
1534     p = map->vp->power;
1535     wid = bwid[p];
1536     fid0 = raw_feature_at(x, y);
1537     if (fid0 == 0)
1538       return;
1539     xform(side, map, x, y, &sx, &sy);
1540     if (wid == 0)
1541       return;
1542     wid2 = wid / 2;
1543 
1544     /* for now: */
1545     if (fid0 == side->ui->curfid) {
1546 	color = side->ui->badcolor;
1547     } else {
1548 	color = side->ui->graycolor;
1549     }
1550     XSetForeground(dpy, gc, color);
1551     if (side->ui->monochrome) {
1552 	if (fid0 == side->ui->curfid) {
1553 	    graylev = side->ui->grays[darkgray];
1554 	} else {
1555 	    graylev = side->ui->grays[gray];
1556 	}
1557 	XSetFillStyle(dpy, gc, FillStippled);
1558 	XSetStipple(dpy, gc, graylev);
1559     } else {
1560 	XSetFillStyle(dpy, gc, FillSolid);
1561     }
1562     XSetClipMask(dpy, gc, None);
1563     XSetLineAttributes(dpy, gc, bwid[p], LineSolid, CapButt, JoinMiter);
1564     for_all_directions(d) {
1565 	if (point_in_dir(x, y, d, &x1, &y1)) {
1566 	    if (raw_feature_at(x1,y1) != fid0) {
1567 		XDrawLine(dpy, map->viewwin, gc,
1568 			  sx + qx[p][d], sy + qy[p][d],
1569 			  sx + qx[p][d+1], sy + qy[p][d+1]);
1570 	    }
1571 	}
1572     }
1573 }
1574 
1575 /* Do the grody work of drawing very large polygons accurately. */
1576 
1577 static void
draw_hex_polygon(Side * side,Map * map,Window win,GC gc,int sx,int sy,int power,int over,int dogrid)1578 draw_hex_polygon(Side *side, Map *map, Window win, GC gc, int sx, int sy, int power, int over, int dogrid)
1579 {
1580     XPoint points[6];
1581     int hw = hws[power], hh = hhs[power], delt = (hhs[power] - hcs[power]);
1582     int ew = (dogrid ? 1 : 0);
1583     enum grayshade shade = gray;
1584     Display *dpy = side->ui->dpy;
1585 
1586     points[0].x = sx + hw / 2;        points[0].y = sy;
1587     points[1].x = hw / 2 - ew;        points[1].y = delt /*- ew*/;
1588     points[2].x = 0;                  points[2].y = hh - 2 * delt - ew;
1589     points[3].x = 0 - (hw / 2 - ew);  points[3].y = delt - ew;
1590     points[4].x = 0 - (hw / 2 - ew);  points[4].y = 0 - (delt - ew);
1591     points[5].x = 0;                  points[5].y = 0 - (hh - 2 * delt - ew);
1592     XFillPolygon(dpy, win, gc, points, 6, Convex, CoordModePrevious);
1593     XFlush(dpy);
1594     if (over < 0) {
1595 	if (over == -2)
1596 	  shade = darkgray;
1597 	XSetFillStyle(dpy, gc, FillStippled);
1598 	XSetStipple(dpy, gc, side->ui->grays[shade]);
1599 	XSetClipMask(dpy, gc, None);
1600 	XSetForeground(dpy, gc, side->ui->blackcolor);
1601 	XFillPolygon(dpy, win, gc, points, 6, Convex, CoordModePrevious);
1602 	XFlush(dpy);
1603 	XSetFillStyle(dpy, gc, FillSolid);
1604     }
1605 }
1606 
1607 /* Draw a mask of borders for the given location. */
1608 
1609 static void
draw_border_line_mult(Side * side,Map * map,Window win,int sx,int sy,int bitmask,int power,int t)1610 draw_border_line_mult(Side *side, Map *map, Window win, int sx, int sy, int bitmask, int power, int t)
1611 {
1612     int wid = bwid[power], wid2, dir, color, sx1, sy1, sx2, sy2;
1613     Image *timg;
1614     Display *dpy = side->ui->dpy;
1615     GC gc = side->ui->terrgc;
1616 
1617     if (wid == 0)
1618       return;
1619     wid2 = wid / 2;
1620     color = side->ui->cellcolor[t];
1621     if (color < 0)
1622       color = side->ui->blackcolor;
1623     XSetForeground(dpy, gc, color);
1624     XSetBackground(dpy, gc, side->ui->whitecolor);
1625     timg = best_image(side->ui->timages[t], wid, wid);
1626     set_terrain_gc_for_image(side, timg);
1627     XSetClipMask(dpy, gc, None);
1628     XSetLineAttributes(dpy, gc, bwid[power], LineSolid, CapButt, JoinMiter);
1629 
1630     for_all_directions(dir) {
1631 	if (bitmask & (1 << dir)) {
1632 	    sx1 = bsx[power][dir];  sy1 = bsy[power][dir];
1633 	    sx2 = bsx[power][dir+1];  sy2 = bsy[power][dir+1];
1634 	    XDrawLine(dpy, win, gc,
1635 		      sx + sx1 - wid2, sy + sy1 - wid2,
1636 		      sx + sx2 - wid2, sy + sy2 - wid2);
1637 	}
1638     }
1639 }
1640 
1641 /* Draw a mask of connection terrain at the given location. */
1642 
1643 static void
draw_connection_line_mult(Side * side,Window win,int sx,int sy,int bitmask,int power,int t)1644 draw_connection_line_mult(Side *side, Window win, int sx, int sy, int bitmask, int power, int t)
1645 {
1646     int wid = cwid[power], wid2, cx = hws[power] / 2, cy = hhs[power] / 2;
1647     int color, dir;
1648     Image *timg;
1649     Display *dpy = side->ui->dpy;
1650     GC gc = side->ui->terrgc;
1651 
1652     if (wid == 0 || lsx[power][0] == 0)
1653       return;
1654     wid2 = wid / 2;
1655     color = side->ui->cellcolor[t];
1656     if (color < 0)
1657       color = side->ui->blackcolor;
1658     XSetForeground(dpy, gc, color);
1659     XSetBackground(dpy, gc, side->ui->whitecolor);
1660     timg = best_image(side->ui->timages[t], wid, wid);
1661     set_terrain_gc_for_image(side, timg);
1662     XSetClipMask(dpy, gc, None);
1663     XSetLineAttributes(dpy, gc, wid, LineSolid, CapButt, JoinMiter);
1664 
1665     for_all_directions(dir) {
1666 	if (bitmask & (1 << dir)) {
1667 	    XDrawLine(dpy, win, gc,
1668 		      sx + cx - wid2, sy + cy - wid2,
1669 		      sx + cx + lsx[power][dir] - wid2,
1670 		      sy + cy + lsy[power][dir] - wid2);
1671 	}
1672     }
1673 }
1674 
1675 /* Map legends are stencils usually. */
1676 
1677 static void
draw_legend_text(Side * side,Window win,int sx,int sy,int power,char * str,int color,int maskit)1678 draw_legend_text(Side *side, Window win, int sx, int sy, int power, char *str, int color, int maskit)
1679 {
1680     sy += (side->ui->ulegendfonts[power][0])->max_bounds.ascent;
1681     XSetFont(side->ui->dpy, side->ui->ltextgc,
1682 	     (side->ui->ulegendfonts[power][0])->fid);
1683     XSetForeground(side->ui->dpy, side->ui->ltextgc, color);
1684     if (maskit) {
1685 	XSetBackground(side->ui->dpy, side->ui->ltextgc,
1686 		       (color == side->ui->bgcolor ? side->ui->fgcolor :
1687 			side->ui->bgcolor));
1688 	XDrawImageString(side->ui->dpy, win, side->ui->ltextgc,
1689 			 sx, sy, str, strlen(str));
1690     } else {
1691 	XDrawString(side->ui->dpy, win, side->ui->ltextgc,
1692 		    sx, sy, str, strlen(str));
1693     }
1694 }
1695 
1696 /* Splash a unit image (either bitmap or font char) onto some window. */
1697 
1698 void
draw_unit_image(ImageFamily * imf,Side * side,Window win,int sx,int sy,int sw,int sh,int s2,int fg,int bg,int mod)1699 draw_unit_image(ImageFamily *imf, Side *side, Window win, int sx, int sy, int sw, int sh, int s2, int fg, int bg, int mod)
1700 {
1701     char *ename;
1702     int sx2, sy2, ex, ey, ew, eh, desperate = FALSE;
1703     long imagecolor = side->ui->blackcolor, maskcolor = side->ui->whitecolor;
1704     Image *uimg;
1705     X11Image *ximg;
1706     Display *dpy = side->ui->dpy;
1707     GC gc = side->ui->unitgc;
1708 
1709     /* Filter out very small images. */
1710     if (sw <= 1)
1711       return;
1712     /* Just draw a small box at 4x4 and below. */
1713     if (sw <= 4) {
1714 	XSetClipMask(dpy, gc, None);
1715 	/* (should draw with a side color if possible) */
1716 	XSetForeground(dpy, gc, side->ui->blackcolor);
1717 	XFillRectangle(dpy, win, gc, sx, sy, sw, sh);
1718 	return;
1719     }
1720     uimg = best_image(imf, sw, sh);
1721     if (uimg != NULL) {
1722 	/* Offset the image to draw in the middle of its area,
1723 	   whether larger or smaller than the given area. */
1724 	sx2 = sx + (sw - uimg->w) / 2;  sy2 = sy + (sh - uimg->h) / 2;
1725 	/* Only change the size of the rectangle being drawn if it's
1726 	   smaller than what was passed in. */
1727 	if (uimg->w < sw) {
1728 	    sx = sx2;
1729 	    sw = uimg->w;
1730 	}
1731 	if (uimg->h < sh) {
1732 	    sy = sy2;
1733 	    sh = uimg->h;
1734 	}
1735 	/* Figure out what colors to use. */
1736 	imagecolor = ((fg != -1) ? fg : side->ui->blackcolor);
1737 	if (side->ui->unitcolors) {
1738 #if 0
1739 	  (side->ui->numcolors[s2] > 2) ? side->ui->colors[s2][2] :
1740 	    (side->ui->numcolors[s2] > 0) ? side->ui->colors[s2][0] :
1741 #endif
1742 	}
1743 	maskcolor = ((bg != -1) ? bg : side->ui->whitecolor);
1744 	if (side->ui->unitcolors) {
1745 #if 0
1746 	  (side->ui->numcolors[s2] > 1) ? side->ui->colors[s2][1] :
1747 	    side->ui->whitecolor;
1748 #endif
1749 	}
1750 	ximg = (X11Image *) uimg->hook;
1751 	if (ximg != NULL) {
1752 	    if (!side->ui->monochrome
1753 		&& side->ui->dflt_color_unit_images
1754 		&& ximg->colr != None) {
1755 		if (ximg->mask != None) {
1756 		    /* set the clip mask */
1757 		    XSetClipOrigin(dpy, gc, sx2, sy2);
1758 		    XSetClipMask(dpy, gc, ximg->mask);
1759 		}
1760 		/* Draw the color image. */
1761 		XCopyArea(dpy, ximg->colr, win, gc, 0, 0, sw, sh, sx, sy);
1762 	    } else if (ximg->mono != None || ximg->mask != None) {
1763 		/* Set the origin for any subsequent clipping. */
1764 		XSetClipOrigin(dpy, gc, sx2, sy2);
1765 		/* Set the color we're going to use for the mask; use
1766 		   the imagecolor if we'll be using the mask as the
1767 		   only image. */
1768 		XSetForeground(dpy, gc,
1769 			       (ximg->mono == None ? imagecolor : maskcolor));
1770 		/* Set the clip mask to be explicit mask or unit's image. */
1771 		if (ximg->mask)
1772 		  XSetClipMask(dpy, gc, ximg->mask);
1773 		else
1774 		  XSetClipMask(dpy, gc, ximg->mono);
1775 		/* Draw the mask. */
1776 		XFillRectangle(dpy, win, gc, sx, sy, sw, sh);
1777 		/* Draw the image proper. */
1778 		if (ximg->mono != None) {
1779 		    XSetForeground(dpy, gc, imagecolor);
1780 		    XSetClipMask(dpy, gc, ximg->mono);
1781 		    XFillRectangle(dpy, win, gc, sx, sy, sw, sh);
1782 		}
1783 	    }
1784 	} else {
1785 	    desperate = TRUE;
1786 	}
1787     } else {
1788 	desperate = TRUE;
1789     }
1790     if (desperate) {
1791 	/* For some reason, we have no useful image; draw a blank box
1792 	   instead. */
1793 	XSetClipOrigin(dpy, gc, sx, sy);
1794 	XSetClipMask(dpy, gc, None);
1795 	/* Draw an edge, at least for larger mags. */
1796 	if (sw >= 16) {
1797 	    XSetForeground(dpy, gc, maskcolor);
1798 	    XFillRectangle(dpy, win, gc, sx, sy, sw, sh);
1799 	}
1800 	/* Draw a filled box. */
1801 	XSetForeground(dpy, gc, imagecolor);
1802 	XFillRectangle(dpy, win, gc, sx + 1, sy + 1, sw - 2, sh - 2);
1803     }
1804     if (mod != 0) {
1805 	XSetFillStyle(dpy, gc, FillStippled);
1806 	XSetStipple(dpy, gc, side->ui->grays[gray]);
1807 	XSetClipMask(dpy, gc, None);
1808 	XSetForeground(dpy, gc, side->ui->whitecolor);
1809 	XFillRectangle(dpy, win, gc, sx, sy, sw, sh);
1810 	XSetFillStyle(dpy, gc, FillSolid);
1811     }
1812     /* Draw a side emblem if one was requested. */
1813     if (between(0, s2, numsides)) {
1814 	ename = (side_n(s2) ? side_n(s2)->emblemname : NULL);
1815 	if (emblem_position(uimg, ename, NULL /* eimg */, sw, sh,
1816 			    &ex, &ey, &ew, &eh)) {
1817 	    draw_side_emblem(side, win, sx + ex, sy + ey, ew, eh, s2, 0);
1818 	}
1819     }
1820 }
1821 
1822 /* Draw an emblem identifying the given side.  If a side does not have a
1823    distinguishing emblem, fall back on some defaults. */
1824 
1825 void
draw_side_emblem(Side * side,Window win,int ex,int ey,int ew,int eh,int s2,int style)1826 draw_side_emblem(Side *side, Window win, int ex, int ey, int ew, int eh, int s2, int style)
1827 {
1828     int ex2, ey2, offset;
1829     long imagecolor, maskcolor;
1830     Image *eimg;
1831     X11Image *ximg;
1832     Display *dpy = side->ui->dpy;
1833     GC gc = side->ui->emblgc;
1834 
1835     /* Draw the emblem's mask, or else an enclosing box. */
1836     eimg = best_image(side->ui->eimages[s2], ew, eh);
1837     if (eimg != NULL) {
1838 	/* Offset the image to draw in the middle of its area,
1839 	   whether larger or smaller than the given area. */
1840 	ex2 = ex + (ew - eimg->w) / 2;  ey2 = ey + (eh - eimg->h) / 2;
1841 	/* Only change the size of the rectangle being drawn if it's
1842 	   smaller than what was passed in. */
1843 	if (eimg->w < ew) {
1844 	    ex = ex2;
1845 	    ew = eimg->w;
1846 	}
1847 	if (eimg->h < eh) {
1848 	    ey = ey2;
1849 	    eh = eimg->h;
1850 	}
1851 	ximg = (X11Image *) eimg->hook;
1852 	/* Maybe draw with a gray shadow. */
1853 	if (style == 1) {
1854 	    offset = (ew >= 16 ? 2 : 1);
1855 	    XSetForeground(dpy, gc, side->ui->graycolor);
1856 	    XSetClipOrigin(dpy, gc, ex + offset, ey + offset);
1857 	    XSetClipMask(dpy, gc, (ximg ? ximg->mask : None));
1858 	    XFillRectangle(dpy, win, gc, ex + offset, ey + offset, ew, eh);
1859 	}
1860 	/* Decide on the colors to use with the emblem. */
1861 	if (side->ui->numcolors[s2] > 0) {
1862 	    imagecolor = side->ui->colors[s2][0];
1863 	} else {
1864 	    imagecolor = side->ui->blackcolor;
1865 	}
1866 	if (side->ui->numcolors[s2] > 1) {
1867 	    maskcolor = side->ui->colors[s2][1];
1868 	} else {
1869 	    maskcolor = side->ui->whitecolor;
1870 	}
1871 	/* Draw the mask. */
1872 	XSetForeground(dpy, gc, maskcolor);
1873 	XSetClipOrigin(dpy, gc, ex, ey);
1874 	if (ximg != NULL && ximg->mask != None) {
1875 	    XSetClipMask(dpy, gc, ximg->mask);
1876 	    XFillRectangle(dpy, win, gc, ex, ey, ew, eh);
1877 	} else {
1878 	    XSetClipMask(dpy, gc, None);
1879 	    XFillRectangle(dpy, win, gc, ex - 1, ey, ew + 1, eh + 1);
1880 	}
1881 	/* Now draw the emblem proper. */
1882 	/* (should fix so some color emblems can be used with mono displays) */
1883 	if (!side->ui->monochrome
1884 	    && side->ui->dflt_color_embl_images
1885 	    && ximg != NULL
1886 	    && ximg->colr != None) {
1887 	    /* Draw the color image. */
1888 	    XCopyArea(dpy, ximg->colr, win, gc, 0, 0, ew, eh, ex, ey);
1889 	} else {
1890 	    XSetForeground(dpy, gc, imagecolor);
1891 	    XSetClipMask(dpy, gc, (ximg != NULL ? ximg->mono : None));
1892 	    XFillRectangle(dpy, win, gc, ex, ey, ew, eh);
1893 	}
1894     }
1895 }
1896 
1897 static void
draw_country_border_line(Side * side,Window win,int sx,int sy,int dir,int power)1898 draw_country_border_line(Side *side, Window win, int sx, int sy, int dir, int power)
1899 {
1900     int wid = bwid[power];
1901     GC gc = side->ui->bdrygc;
1902     Display *dpy = side->ui->dpy;
1903 
1904     if (wid == 0)
1905       return;
1906     wid = max(1, wid / 2);
1907     XSetForeground(dpy, gc, side->ui->country_border_color);
1908     XSetClipMask(dpy, gc, None);
1909     XSetLineAttributes(dpy, gc, wid, LineSolid, CapButt, JoinMiter);
1910     XDrawLine(dpy, win, gc,
1911 	      sx + bsx[power][dir], sy + bsy[power][dir],
1912 	      sx + bsx[power][dir+1], sy + bsy[power][dir+1]);
1913 }
1914 
1915 /* Describe the state of the given unit, in maximal detail. */
1916 
1917 void
draw_map_info(Side * side,Map * map)1918 draw_map_info(Side *side, Map *map)
1919 {
1920     char infobuf[BUFSIZE];
1921     int u, s, mrow, x = map->curx, y = map->cury, len;
1922     Unit *unit, *unit2;
1923     Side *side2 = NULL; /* init to keep GCC quiet */
1924     UnitView *uview;
1925 
1926     len = 40;
1927     XClearWindow(side->ui->dpy, map->infowin);
1928     unit = map->curunit;
1929     if (!in_play(unit)) {
1930 	if (inside_area(x, y)) {
1931 	    /* (should pick the "most visible" unit at x,y) */
1932 	    unit2 = unit_at(x, y);
1933 	    if (unit2 != NULL
1934 		&& unit_visible(side, map->vp, unit2)) {
1935 		sprintf(infobuf, "%s", unit_handle(side, unit2));
1936 		draw_info_text(side, map, 0, 0, len, infobuf);
1937 		location_desc(infobuf, side, unit2, unit2->type, x, y);
1938 		draw_info_text(side, map, 0, 1, len, infobuf);
1939 	    } else {
1940 		u = NONUTYPE;
1941 		uview = unit_view_at(side, x, y);
1942 		if (uview != NULL) {
1943 		    u = uview->type;
1944 		    side2 = side_n(uview->siden);
1945 		}
1946 		if (u != NONUTYPE) {
1947 		    sprintf(infobuf, "%s %s",
1948 			    side_adjective(side2), u_type_name(u));
1949 		    draw_info_text(side, map, 0, 0, len, infobuf);
1950 		}
1951 		location_desc(infobuf, side, NULL, u, x, y);
1952 		draw_info_text(side, map, 0, 1, len, infobuf);
1953 	    }
1954 	}
1955 	return;
1956     }
1957     u = unit->type;
1958     /* Say which unit this is. */
1959     sprintf(infobuf, "%s", unit_handle(side, unit));
1960     draw_info_text(side, map, 0, 0, len, infobuf);
1961     location_desc(infobuf, side, unit, u, x, y);
1962     draw_info_text(side, map, 0, 1, len, infobuf);
1963     /* Very briefly list the numbers and types of the occupants. */
1964     occupants_desc(infobuf, unit);
1965     draw_info_text(side, map, 0, 2, len, infobuf);
1966     /* Display the "important" parameters. */
1967     /* (should say something about parts?) */
1968     hp_desc(infobuf, unit, TRUE);
1969     strcat(infobuf, "   ");
1970     acp_desc(tmpbuf, unit, TRUE);
1971     strcat(infobuf, tmpbuf);
1972     cxp_desc(tmpbuf, unit, TRUE);
1973     strcat(infobuf, tmpbuf);
1974     morale_desc(tmpbuf, unit, TRUE);
1975     strcat(infobuf, tmpbuf);
1976     /* Crude hack, should be replaced with something better */
1977     if (supply_system_in_use())
1978       sprintf(&infobuf[strlen(infobuf)],
1979 	      "   Supply inflow=%d%% connectedness=%d%%",
1980 	      supply_inflow(unit), supply_connectedness(unit));
1981     draw_info_text(side, map, 50, 0, -1, infobuf);
1982     /* List other stack members here. */
1983     others_here_desc(infobuf, unit);
1984     if (strlen(infobuf) > 0)
1985       draw_info_text(side, map, 50, 1, -1, infobuf);
1986     /* Describe the state of all the supplies. */
1987     mrow = 0;
1988     while (supply_desc(infobuf, unit, mrow)) {
1989 	draw_info_text(side, map, 50, mrow + 2, -1, infobuf);
1990 	++mrow;
1991     }
1992     /* Describe the current plan and task agenda. */
1993     if (unit->plan) {
1994 	int row = 4;
1995 	Task *task;
1996 
1997 	plan_desc(infobuf, unit);
1998 	draw_info_text(side, map, 0, 3, len, infobuf);
1999 	for (task = unit->plan->tasks; task != NULL; task = task->next) {
2000 	    task_desc(infobuf, unit->side, unit, task);
2001 	    draw_info_text(side, map, 0, row++, len, infobuf);
2002 	}
2003     }
2004 }
2005 
2006 /* Display improvement can be achieved by padding out lines with blanks,
2007    then the lines need not be cleared before redrawing. */
2008 
2009 static void
draw_info_text(Side * side,Map * map,int x,int y,int len,char * buf)2010 draw_info_text(Side *side, Map *map, int x, int y, int len, char *buf)
2011 {
2012     int sx, sy;
2013 
2014     /* Translate a 0-100 value for x to pixels. */
2015     if (x == 50)
2016       sx = map->pxw / 2;
2017     else
2018       sx = 2;
2019     sy = y * side->ui->fh;
2020     if (len > 0 && strlen(buf) > len)
2021       buf[len-1] = '\0';
2022     if (buf[0] == '\0')
2023       return;
2024     draw_text(side, map->infowin, sx, sy, buf, side->ui->fgcolor);
2025 }
2026 
2027 /* Write onto the list of sides. */
2028 
2029 void
draw_map_sides(Side * side,Map * map)2030 draw_map_sides(Side *side, Map *map)
2031 {
2032     Side *side2;
2033 
2034     if (map == NULL)
2035       return;
2036     for_all_sides(side2) {
2037 	draw_side_info(side, map, side2);
2038     }
2039 }
2040 
2041 /* Show info about a single side to some other side. */
2042 
2043 void
draw_side_info(Side * side,Map * map,Side * side2)2044 draw_side_info(Side *side, Map *map, Side *side2)
2045 {
2046     char sidebuf[BUFSIZE];
2047     int sx, sy, i, fh = side->ui->fh;
2048     Display *dpy = side->ui->dpy;
2049     Window win;
2050     GC gc = side->ui->gc;
2051 
2052     if (map == NULL || side2 == NULL)
2053       return;
2054     win = map->sideswin;
2055     sx = 2 + 16 + 2;
2056     sy = (side_number(side2) - 1) * map->sidespacing;
2057     draw_side_emblem(side, win, 1, sy + 2, 16, 16, side_number(side2), 1);
2058     /* Build up and write the textual description of the side. */
2059     sidebuf[0] = '\0';
2060 #ifdef DESIGNERS
2061     if (side2->designer)
2062       strcat(sidebuf, "(designer)");
2063 #endif /* DESIGNERS */
2064     strcat(sidebuf, short_side_title(side2));
2065     if (side2->willingtodraw) {
2066 	strcat(sidebuf, "[draw]");
2067     }
2068     if (side2->player) {
2069 	strcat(sidebuf, "(");
2070 	short_player_title(sidebuf+strlen(sidebuf), side2->player, NULL);
2071 	strcat(sidebuf, ")");
2072     }
2073     draw_text(side, win, sx, sy, sidebuf,
2074 	      (side2 == side ? side->ui->bgcolor : side->ui->fgcolor));
2075     if (keeping_score() && side2->everingame) {
2076 	Scorekeeper *sk;
2077 	char *scoredesc;
2078 
2079 	XSetForeground(dpy, gc, side->ui->bgcolor);
2080 	XFillRectangle(dpy, win, gc,
2081 		       0, sy + 2 * fh, 500, map->sidespacing - 2 * fh);
2082 	i = 0;
2083 	for_all_scorekeepers(sk) {
2084 	    scoredesc = side_score_desc(spbuf, side2, sk);
2085 	    sx = (((i & 1) == 1) ? 50 : 0) + 2 + 16 + 2;
2086 	    draw_text(side, win, sx, sy + 2 * fh + (i / 2) * 15, scoredesc,
2087 		      side->ui->fgcolor);
2088 	    ++i;
2089 	}
2090     }
2091     if (side_won(side2)) {
2092 	/* (should do something more interesting than this) */
2093 	XSetForeground(dpy, gc, side->ui->fgcolor);
2094 	XDrawRectangle(dpy, win, gc, 0, sy, 500, map->sidespacing);
2095 	XDrawRectangle(dpy, win, gc, 1, sy + 1, 500 - 2, map->sidespacing - 2);
2096 	XDrawRectangle(dpy, win, gc, 2, sy + 2, 500 - 4, map->sidespacing - 4);
2097     } else if (side_lost(side2)) {
2098 	/* Draw the line of ignominy through sides that have lost. */
2099 	XSetForeground(dpy, gc, side->ui->fgcolor);
2100 	XDrawLine(dpy, win, gc, 0, sy + fh / 2, 500, sy + fh / 2);
2101     } else if (side2->ingame) {
2102 	draw_side_progress(side, map, side2);
2103     } else {
2104 	/* what to do here? */
2105     }
2106 }
2107 
2108 /* Display how far along the side is with moving its units. */
2109 
2110 void
draw_side_progress(Side * side,Map * map,Side * side2)2111 draw_side_progress(Side *side, Map *map, Side *side2)
2112 {
2113     int sx, sy, totacp, percentleft, percentresv;
2114     int barhgt = 7;
2115     Display *dpy = side->ui->dpy;
2116     Window win;
2117     GC gc = side->ui->gc;
2118 
2119     if (map == NULL || side2 == NULL)
2120       return;
2121     win = map->sideswin;
2122     sx = 2 + 16 + 2;
2123     sy = (side_number(side2) - 1) * map->sidespacing + side->ui->fh + 2;
2124     XSetClipMask(dpy, gc, None);
2125     /* (should make generic) */
2126     percentresv = percentleft = 0;
2127     if (side2->ingame && !endofgame) {
2128 	totacp = side_initacp(side2);
2129 	if (totacp > 0) {
2130 	    percentleft = (100 * side_acp(side2)) / totacp;
2131 	    percentleft = limitn(0, percentleft, 100);
2132 	    percentresv = (100 * side_acp_reserved(side2)) / totacp;
2133 	    percentresv = limitn(0, percentresv, percentleft);
2134 	}
2135     }
2136     /* Clear the bar's area. */
2137     XSetForeground(dpy, gc, side->ui->bgcolor);
2138     XFillRectangle(dpy, win, gc, sx, sy, 100 + 2, barhgt + 2);
2139 
2140     /* And show the acp we haven't used yet. */
2141     if (percentleft > 0) {
2142 	XSetForeground(dpy, gc, side->ui->fgcolor);
2143 	if (side2->finishedturn
2144 	    || !(side_has_ai(side2) || side_has_display(side2))) {
2145 	    XSetFillStyle(dpy, gc, FillOpaqueStippled);
2146 	    XSetStipple(dpy, gc, side->ui->grays[gray]);
2147 	}
2148 	XFillRectangle(dpy, win, gc, sx + 1, sy + 1, percentleft, barhgt);
2149 	/* Display the acp in reserve using dark gray. */
2150 	if (percentresv > 0) {
2151 	    XSetFillStyle(dpy, gc, FillOpaqueStippled);
2152 	    XSetStipple(dpy, gc, side->ui->grays[darkgray]);
2153 	    XFillRectangle(dpy, win, gc, sx + 1, sy + 1, percentresv, barhgt);
2154 	    XSetFillStyle(dpy, gc, FillSolid);
2155 	}
2156 	if (side2->finishedturn
2157 	    || !(side_has_ai(side2) || side_has_display(side2))) {
2158 	    XSetFillStyle(dpy, gc, FillSolid);
2159 	}
2160     }
2161     /* Frame the progress bar if progress is meaningful. */
2162     if (side2->ingame && !endofgame) {
2163 	XSetForeground(dpy, gc, side->ui->fgcolor);
2164 	XDrawRectangle(dpy, win, gc, sx, sy, 100 + 1, barhgt + 1);
2165     }
2166 }
2167