1 /* $Id: record.c,v 5.5 2002/06/15 18:14:01 dik Exp $
2  *
3  * XPilot, a multiplayer gravity war game.  Copyright (C) 1991-2001 by
4  *
5  *      Bj�rn Stabell        <bjoern@xpilot.org>
6  *      Ken Ronny Schouten   <ken@xpilot.org>
7  *      Bert Gijsbers        <bert@xpilot.org>
8  *      Dick Balaska         <dick@xpilot.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23  */
24 
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <ctype.h>
30 #include <time.h>
31 
32 #ifndef _WINDOWS
33 # include <unistd.h>
34 # include <X11/X.h>
35 # include <X11/Xlib.h>
36 # include <X11/Xutil.h>
37 # include <X11/Xos.h>
38 #else
39 # include <fcntl.h>
40 #endif
41 
42 #ifdef _WINDOWS
43 # include "winX.h"
44 # include "winX_.h"
45 # include <io.h>
46 #endif
47 
48 #include "version.h"
49 #include "config.h"
50 #include "error.h"
51 #include "const.h"
52 #include "client.h"
53 #include "paint.h"
54 #include "setup.h"
55 #include "record.h"
56 #include "recordfmt.h"
57 #include "xpmread.h"
58 #include "commonproto.h"
59 #include "xinit.h"
60 
61 char record_version[] = VERSION;
62 
63 /*
64  * GC elements for line drawing operations.
65  */
66 #define RSTROKEGC	(GCForeground | GCBackground | \
67 			 GCLineWidth | GCLineStyle | \
68 			 GCDashOffset | GCFunction)
69 /*
70  * GC elements for polygon filling (except GCForeground).
71  */
72 #define RTILEGC		(GCFillStyle | GCTile | \
73 			 GCTileStipXOrigin | GCTileStipYOrigin)
74 
75 
76 /*
77  * Functions and variables for recording
78  */
79 static char		*record_filename = NULL;/* Name of recordfile. */
80 static FILE		*recordFP = NULL;	/* File handle for writing
81 						 * recording frames to. */
82 int			recording = False;	/* Are we recording or not. */
83 static int		record_start = False;	/* Should we start recording
84 						 * at the next frame. */
85 static int		record_frame_count = 0;	/* How many recorded frames. */
86 static const char	*record_dashes;		/* Which dash list to use. */
87 static int		record_num_dashes;	/* How big is dashes list. */
88 static int		record_dash_dirty = 0;	/* Has dashes list changed? */
89 
90 /*
91  * Dummy functions for "recordable drawing" interface, when not recording.
92  */
Dummy_newFrame(void)93 static void Dummy_newFrame(void) {}
Dummy_endFrame(void)94 static void Dummy_endFrame(void) {}
95 
96 #ifdef _WINDOWS
97 extern void paintItemSymbol(unsigned char type, Drawable drawable, GC mygc, int x, int y, int color);
98 #else
Dummy_paintItemSymbol(unsigned char type,Drawable drawable,GC mygc,int x,int y,int color)99 static void Dummy_paintItemSymbol(unsigned char type, Drawable drawable,
100 				  GC mygc, int x, int y, int color) {}
101 #endif
102 
103 extern char	hostname[];
104 
105 
106 /*
107  * Miscellaneous recording functions.
108  * These are only called when (recording == True).
109  */
RWriteByte(unsigned char i)110 static void RWriteByte(unsigned char i)
111 {
112     putc(i, recordFP);
113 }
114 
RWriteShort(short i)115 static void RWriteShort(short i)
116 {
117     putc(i, recordFP);
118     i >>= 8;
119     putc(i, recordFP);
120 }
121 
RWriteUShort(unsigned short i)122 static void RWriteUShort(unsigned short i)
123 {
124     putc(i, recordFP);
125     i >>= 8;
126     putc(i, recordFP);
127 }
128 
RWriteLong(long i)129 static void RWriteLong(long i)
130 {
131     putc(i, recordFP);
132     i >>= 8;
133     putc(i, recordFP);
134     i >>= 8;
135     putc(i, recordFP);
136     i >>= 8;
137     putc(i, recordFP);
138 }
139 
RWriteULong(unsigned long i)140 static void RWriteULong(unsigned long i)
141 {
142     putc(i, recordFP);
143     i >>= 8;
144     putc(i, recordFP);
145     i >>= 8;
146     putc(i, recordFP);
147     i >>= 8;
148     putc(i, recordFP);
149 }
150 
RWriteString(char * str)151 static void RWriteString(char *str)
152 {
153     int				len = strlen(str);
154     int				i;
155 
156     RWriteUShort(len);
157     for (i = 0; i < len; i++) {
158 	putc(str[i], recordFP);
159     }
160 }
161 
162 /*
163  * Output the XPilot Recording Header at the beginning
164  * of the recording file.
165  */
RWriteHeader(void)166 static void RWriteHeader(void)
167 {
168     time_t			t;
169     char			buf[256];
170     char			*ptr;
171     int				i;
172 
173     rewind(recordFP);
174 
175     /* First write out magic 4 letter word */
176     putc('X', recordFP);
177     putc('P', recordFP);
178     putc('R', recordFP);
179     putc('C', recordFP);
180 
181     /* Write which version of the XPilot Record Protocol this is. */
182     putc(RC_MAJORVERSION, recordFP);
183     putc('.', recordFP);
184     putc(RC_MINORVERSION, recordFP);
185     putc('\n', recordFP);
186 
187     /* Write player's nick, login, host, server, FPS and the date. */
188     RWriteString(name);
189     RWriteString(realname);
190     RWriteString(hostname);
191     RWriteString(servername);
192     RWriteByte(FPS);
193     time(&t);
194     strlcpy(buf, ctime(&t), sizeof(buf));
195     if ((ptr = strchr(buf, '\n')) != NULL) {
196 	*ptr = '\0';
197     }
198     RWriteString(buf);
199 
200     /* Write info about graphics setup. */
201     putc(maxColors, recordFP);
202     for (i = 0; i < maxColors; i++) {
203 	RWriteULong(colors[i].pixel);
204 #ifdef _WINDOWS
205 	RWriteUShort(256*GetRValue(objs[i].color));
206 	RWriteUShort(256*GetGValue(objs[i].color));
207 	RWriteUShort(256*GetBValue(objs[i].color));
208 #else
209 	RWriteUShort(colors[i].red);
210 	RWriteUShort(colors[i].green);
211 	RWriteUShort(colors[i].blue);
212 #endif
213     }
214     RWriteString(gameFontName);
215     RWriteString(messageFontName);
216 
217     RWriteUShort(draw_width);
218     RWriteUShort(draw_height);
219 
220     record_dashes = dashes;
221     record_num_dashes = NUM_DASHES;
222     record_dash_dirty = True;
223 }
224 
RGetPixelIndex(unsigned long pixel)225 static int RGetPixelIndex(unsigned long pixel)
226 {
227     int			i;
228 
229     for (i = 0; i < maxColors; i++) {
230 	if (pixel == colors[i].pixel) {
231 	    return i;
232 	}
233     }
234     for (i = 1; i < maxColors; i++) {
235 	if (pixel == (colors[BLACK].pixel ^ colors[i].pixel)) {
236 	    return i + maxColors;
237 	}
238     }
239 
240     return WHITE;
241 }
242 
RWriteTile(Pixmap tile)243 static void RWriteTile(Pixmap tile)
244 {
245 #ifndef _WINDOWS
246     typedef struct tile_list {
247 	struct tile_list	*next;
248 	Pixmap			tile;
249 	unsigned char		tile_id;
250     } tile_list_t;
251     static tile_list_t		*list = NULL;
252     tile_list_t			*lptr;
253     static int			next_tile_id = 1;
254     unsigned			x, y;
255     int				i;
256     XImage			*img;
257 
258     for (lptr = list; lptr != NULL; lptr = lptr->next) {
259 	if (lptr->tile == tile) {
260 	    /* tile already sent before. */
261 	    RWriteByte(RC_TILE);
262 	    RWriteByte(lptr->tile_id);
263 	    return;
264 	}
265     }
266 
267     /* a first time tile. */
268 
269     if (!(lptr = (tile_list_t *)malloc(sizeof(tile_list_t)))) {
270 	error("Not enough memory");
271 	RWriteByte(RC_TILE);
272 	RWriteByte(0);
273 	return;
274     }
275     lptr->next = list;
276     lptr->tile = tile;
277     lptr->tile_id = next_tile_id;
278     list = lptr;
279 
280     if (!(img = xpm_image_from_pixmap(tile))) {
281 	RWriteByte(RC_TILE);
282 	RWriteByte(0);
283 	lptr->tile_id = 0;
284 	return;
285     }
286     RWriteByte(RC_NEW_TILE);
287     RWriteByte(lptr->tile_id);
288     RWriteUShort(img->width);
289     RWriteUShort(img->height);
290     for (y = 0; y < img->height; y++) {
291 	for (x = 0; x < img->width; x++) {
292 	    unsigned long pixel = XGetPixel(img, x, y);
293 	    for (i = 0; i < maxColors - 1; i++) {
294 		if (pixel == colors[i].pixel) {
295 		    break;
296 		}
297 	    }
298 	    RWriteByte(i);
299 	}
300     }
301 
302     XDestroyImage(img);
303 
304     next_tile_id++;
305 #endif
306 }
307 
RWriteGC(GC gc,unsigned long req_mask)308 static void RWriteGC(GC gc, unsigned long req_mask)
309 {
310     XGCValues			values;
311     unsigned long		write_mask;
312     static unsigned long	prev_mask;
313     static XGCValues		prev_values;
314     static int			prev_frame_count = -1;
315     unsigned short		gc_mask;
316 
317     if (prev_frame_count != record_frame_count) {
318 	prev_frame_count = record_frame_count;
319 	write_mask = RSTROKEGC | RTILEGC;
320 	XGetGCValues(dpy, gc, write_mask, &values);
321 	if (values.fill_style != FillTiled) {
322 	    write_mask &= ~(GCTileStipXOrigin | GCTileStipYOrigin
323 			    | GCTile);
324 	}
325 	prev_mask = write_mask;
326 	prev_values = values;
327     }
328     else {
329 	write_mask = req_mask | GCFunction;
330 	XGetGCValues(dpy, gc, write_mask, &values);
331 
332 	if ((write_mask & prev_mask & GCForeground) != 0) {
333 	    if (prev_values.foreground == values.foreground) {
334 		write_mask &= ~GCForeground;
335 	    } else {
336 		prev_values.foreground = values.foreground;
337 	    }
338 	}
339 	if ((write_mask & prev_mask & GCBackground) != 0) {
340 	    if (prev_values.background == values.background) {
341 		write_mask &= ~GCBackground;
342 	    } else {
343 		prev_values.background = values.background;
344 	    }
345 	}
346 	if ((write_mask & prev_mask & GCLineWidth) != 0) {
347 	    if (prev_values.line_width == values.line_width) {
348 		write_mask &= ~GCLineWidth;
349 	    } else {
350 		prev_values.line_width = values.line_width;
351 	    }
352 	}
353 	if ((write_mask & prev_mask & GCLineStyle) != 0) {
354 	    if (prev_values.line_style == values.line_style) {
355 		write_mask &= ~GCLineStyle;
356 	    } else {
357 		prev_values.line_style = values.line_style;
358 	    }
359 	}
360 	if ((write_mask & prev_mask & GCDashOffset) != 0) {
361 	    if (prev_values.dash_offset == values.dash_offset) {
362 		write_mask &= ~GCDashOffset;
363 	    } else {
364 		prev_values.dash_offset = values.dash_offset;
365 	    }
366 	}
367 	if ((write_mask & prev_mask & GCFunction) != 0) {
368 	    if (prev_values.function == values.function) {
369 		write_mask &= ~GCFunction;
370 	    } else {
371 		prev_values.function = values.function;
372 	    }
373 	}
374 	if ((write_mask & prev_mask & GCFillStyle) != 0) {
375 	    if (prev_values.fill_style == values.fill_style) {
376 		write_mask &= ~GCFillStyle;
377 	    } else {
378 		prev_values.fill_style = values.fill_style;
379 	    }
380 	    /*
381 	     * We only update some values if they
382 	     * are going to be used.
383 	     * e.g., no use for tile origins and tiles
384 	     * if fill style is not tiled.
385 	     */
386 	    if (values.fill_style == FillTiled) {
387 		if ((write_mask & prev_mask & GCTileStipXOrigin) != 0) {
388 		    if (prev_values.ts_x_origin == values.ts_x_origin) {
389 			write_mask &= ~GCTileStipXOrigin;
390 		    } else {
391 			prev_values.ts_x_origin = values.ts_x_origin;
392 		    }
393 		}
394 		if ((write_mask & prev_mask & GCTileStipYOrigin) != 0) {
395 		    if (prev_values.ts_y_origin == values.ts_y_origin) {
396 			write_mask &= ~GCTileStipYOrigin;
397 		    } else {
398 			prev_values.ts_y_origin = values.ts_y_origin;
399 		    }
400 		}
401 		if ((write_mask & prev_mask & GCTile) != 0) {
402 		    if (prev_values.tile == values.tile) {
403 			write_mask &= ~GCTile;
404 		    } else {
405 			prev_values.tile = values.tile;
406 		    }
407 		}
408 	    }
409 	    else {
410 		write_mask &= ~(GCTileStipXOrigin | GCTileStipYOrigin
411 				| GCTile);
412 	    }
413 	}
414 
415 	if (!write_mask && !record_dash_dirty) {
416 	    putc(RC_NOGC, recordFP);
417 	    return;
418 	}
419 
420 	prev_mask |= write_mask;
421     }
422 
423     putc(RC_GC, recordFP);
424 
425     gc_mask = 0;
426     if (write_mask & GCForeground)
427 	gc_mask |= RC_GC_FG;
428     if (write_mask & GCBackground)
429 	gc_mask |= RC_GC_BG;
430     if (write_mask & GCLineWidth)
431 	gc_mask |= RC_GC_LW;
432     if (write_mask & GCLineStyle)
433 	gc_mask |= RC_GC_LS;
434     if (write_mask & GCDashOffset)
435 	gc_mask |= RC_GC_DO;
436     if (write_mask & GCFunction)
437 	gc_mask |= RC_GC_FU;
438     if (record_dash_dirty) {
439 	gc_mask |= RC_GC_DA;
440 	if ((write_mask & GCDashOffset) == 0) {
441 	    write_mask |= GCDashOffset;
442 	    values.dash_offset = prev_values.dash_offset;
443 	    gc_mask |= RC_GC_DO;
444 	}
445     }
446     if (write_mask & RTILEGC) {
447 	gc_mask |= RC_GC_B2;
448 	if (write_mask & GCFillStyle)
449 	    gc_mask |= RC_GC_FS;
450 	if (write_mask & GCTileStipXOrigin)
451 	    gc_mask |= RC_GC_XO;
452 	if (write_mask & GCTileStipYOrigin)
453 	    gc_mask |= RC_GC_YO;
454 	if (write_mask & GCTile)
455 	    gc_mask |= RC_GC_TI;
456     }
457 
458     RWriteByte(gc_mask);
459     if (gc_mask & RC_GC_B2) {
460 	RWriteByte(gc_mask >> 8);
461     }
462 
463     if (write_mask & GCForeground)
464 	RWriteByte(RGetPixelIndex(values.foreground));
465     if (write_mask & GCBackground)
466 	RWriteByte(RGetPixelIndex(values.background));
467     if (write_mask & GCLineWidth)
468 	RWriteByte(values.line_width);
469     if (write_mask & GCLineStyle)
470 	RWriteByte(values.line_style);
471     if (write_mask & GCDashOffset)
472 	RWriteByte(values.dash_offset);
473     if (write_mask & GCFunction)
474 	RWriteByte(values.function);
475     if (record_dash_dirty) {
476 	int i;
477 	RWriteByte(record_num_dashes);
478 	for (i = 0; i < record_num_dashes; i++) {
479 	    RWriteByte(record_dashes[i]);
480 	}
481     }
482     if (write_mask & RTILEGC) {
483 	if (write_mask & GCFillStyle)
484 	    RWriteByte(values.fill_style);
485 	if (write_mask & GCTileStipXOrigin)
486 	    RWriteLong(values.ts_x_origin);
487 	if (write_mask & GCTileStipYOrigin)
488 	    RWriteLong(values.ts_y_origin);
489 	if (write_mask & GCTile) {
490 	    RWriteTile(values.tile);
491 	}
492     }
493 }
494 
RNewFrame(void)495 static void RNewFrame(void)
496 {
497     static int		before;
498 
499     if (!before++) {
500 	RWriteHeader();
501     }
502 
503     recording = True;
504 
505     putc(RC_NEWFRAME, recordFP);
506     RWriteUShort(draw_width);
507     RWriteUShort(draw_height);
508 }
509 
REndFrame(void)510 static void REndFrame(void)
511 {
512     if (damaged) {
513 	XGCValues			values;
514 
515 	XGetGCValues(dpy, gc, GCForeground, &values);
516 
517 	RWriteByte(RC_DAMAGED);
518 	if ((damaged & 1) != 0) {
519 	    XSetForeground(dpy, gc, colors[BLUE].pixel);
520 	} else {
521 	    XSetForeground(dpy, gc, colors[BLACK].pixel);
522 	}
523 	RWriteGC(gc, GCForeground | RTILEGC);
524 	RWriteByte(damaged);
525 
526 	XSetForeground(dpy, gc, values.foreground);
527     }
528 
529     putc(RC_ENDFRAME, recordFP);
530 
531     fflush(recordFP);
532 
533     recording = False;
534 
535     record_frame_count++;	/* Number of frames written sofar. */
536 }
537 
RDrawArc(Display * display,Drawable drawable,GC gc,int x,int y,unsigned width,unsigned height,int angle1,int angle2)538 static int RDrawArc(Display *display, Drawable drawable, GC gc,
539 		    int x, int y,
540 		    unsigned width, unsigned height,
541 		    int angle1, int angle2)
542 {
543     XDrawArc(display, drawable, gc, x, y, width, height, angle1, angle2);
544     if (drawable == p_draw) {
545 	putc(RC_DRAWARC, recordFP);
546 	RWriteGC(gc, RSTROKEGC | RTILEGC);
547 	RWriteShort(x);
548 	RWriteShort(y);
549 	RWriteByte(width);
550 	RWriteByte(height);
551 	RWriteShort(angle1);
552 	RWriteShort(angle2);
553     }
554     return 0;
555 }
556 
RDrawLines(Display * display,Drawable drawable,GC gc,XPoint * points,int npoints,int mode)557 static int RDrawLines(Display *display, Drawable drawable, GC gc,
558 		      XPoint *points, int npoints, int mode)
559 {
560     XDrawLines(display, drawable, gc, points, npoints, mode);
561     if (drawable == p_draw) {
562 	int i;
563 	XPoint *xp = points;
564 
565 	putc(RC_DRAWLINES, recordFP);
566 	RWriteGC(gc, RSTROKEGC | RTILEGC);
567 	RWriteUShort(npoints);
568 	for (i = 0; i < npoints; i++, xp++) {
569 	    RWriteShort(xp->x);
570 	    RWriteShort(xp->y);
571 	}
572 	RWriteByte(mode);
573     }
574     return 0;
575 }
576 
RDrawLine(Display * display,Drawable drawable,GC gc,int x1,int y1,int x2,int y2)577 static int RDrawLine(Display *display, Drawable drawable, GC gc,
578 		     int x1, int y1,
579 		     int x2, int y2)
580 {
581     XDrawLine(display, drawable, gc, x1, y1, x2, y2);
582     if (drawable == p_draw) {
583 	putc(RC_DRAWLINE, recordFP);
584 	RWriteGC(gc, RSTROKEGC | RTILEGC);
585 	RWriteShort(x1);
586 	RWriteShort(y1);
587 	RWriteShort(x2);
588 	RWriteShort(y2);
589     }
590     return 0;
591 }
592 
RDrawRectangle(Display * display,Drawable drawable,GC gc,int x,int y,unsigned width,unsigned height)593 static int RDrawRectangle(Display *display, Drawable drawable, GC gc,
594 			  int x, int y,
595 			  unsigned width, unsigned height)
596 {
597     XDrawRectangle(display, drawable, gc, x, y, width, height);
598     if (drawable == p_draw) {
599 	putc(RC_DRAWRECTANGLE, recordFP);
600 	RWriteGC(gc, RSTROKEGC | RTILEGC);
601 	RWriteShort(x);
602 	RWriteShort(y);
603 	RWriteByte(width);
604 	RWriteByte(height);
605     }
606     return 0;
607 }
608 
RDrawString(Display * display,Drawable drawable,GC gc,int x,int y,const char * string,int length)609 static int RDrawString(Display *display, Drawable drawable, GC gc,
610 		       int x, int y,
611 		       const char *string, int length)
612 {
613     XDrawString(display, drawable, gc, x, y, string, length);
614     if (drawable == p_draw) {
615 	int i;
616 	XGCValues values;
617 
618 	putc(RC_DRAWSTRING, recordFP);
619 	RWriteGC(gc, GCForeground | RTILEGC);
620 	RWriteShort(x);
621 	RWriteShort(y);
622 	XGetGCValues(display, gc, GCFont, &values);
623 	RWriteByte((values.font == messageFont->fid) ? 1 : 0);
624 	RWriteUShort(length);
625 	for (i = 0; i < length; i++)
626 	    putc(string[i], recordFP);
627     }
628     return 0;
629 }
630 
RFillArc(Display * display,Drawable drawable,GC gc,int x,int y,unsigned height,unsigned width,int angle1,int angle2)631 static int RFillArc(Display *display, Drawable drawable, GC gc,
632 		    int x, int y,
633 		    unsigned height, unsigned width,
634 		    int angle1, int angle2)
635 {
636     XFillArc(display, drawable, gc, x, y, width, height, angle1, angle2);
637     if (drawable == p_draw) {
638 	putc(RC_FILLARC, recordFP);
639 	RWriteGC(gc, GCForeground | RTILEGC);
640 	RWriteShort(x);
641 	RWriteShort(y);
642 	RWriteByte(width);
643 	RWriteByte(height);
644 	RWriteShort(angle1);
645 	RWriteShort(angle2);
646     }
647     return 0;
648 }
649 
RFillPolygon(Display * display,Drawable drawable,GC gc,XPoint * points,int npoints,int shape,int mode)650 static int RFillPolygon(Display *display, Drawable drawable, GC gc,
651 		        XPoint *points, int npoints,
652 			int shape, int mode)
653 {
654     XFillPolygon(display, drawable, gc, points, npoints, shape, mode);
655     if (drawable == p_draw) {
656 	int i;
657 	XPoint *xp = points;
658 
659 	putc(RC_FILLPOLYGON, recordFP);
660 	RWriteGC(gc, GCForeground | RTILEGC);
661 	RWriteUShort(npoints);
662 	for (i = 0; i < npoints; i++, xp++) {
663 	    RWriteShort(xp->x);
664 	    RWriteShort(xp->y);
665 	}
666 	RWriteByte(shape);
667 	RWriteByte(mode);
668     }
669     return 0;
670 }
671 
RPaintItemSymbol(unsigned char type,Drawable drawable,GC mygc,int x,int y,int color)672 static void RPaintItemSymbol(unsigned char type, Drawable drawable, GC mygc,
673 			     int x, int y, int color)
674 {
675 #ifdef _WINDOWS
676 	paintItemSymbol(type, drawable, mygc, x, y, color);
677 #endif
678     if (drawable == p_draw) {
679 	putc(RC_PAINTITEMSYMBOL, recordFP);
680 	RWriteGC(gc, GCForeground | GCBackground);
681 	putc(type, recordFP);
682 	RWriteShort(x);
683 	RWriteShort(y);
684     }
685 }
686 
RFillRectangle(Display * display,Drawable drawable,GC gc,int x,int y,unsigned width,unsigned height)687 static int RFillRectangle(Display *display, Drawable drawable, GC gc,
688 			  int x, int y,
689 			  unsigned width, unsigned height)
690 {
691     XFillRectangle(display, drawable, gc, x, y, width, height);
692     if (drawable == p_draw) {
693 	putc(RC_FILLRECTANGLE, recordFP);
694 	RWriteGC(gc, GCForeground | RTILEGC);
695 	RWriteShort(x);
696 	RWriteShort(y);
697 	RWriteByte(width);
698 	RWriteByte(height);
699     }
700     return 0;
701 }
702 
RFillRectangles(Display * display,Drawable drawable,GC gc,XRectangle * rectangles,int nrectangles)703 static int RFillRectangles(Display *display, Drawable drawable, GC gc,
704 			   XRectangle *rectangles, int nrectangles)
705 {
706     XFillRectangles(display, drawable, gc, rectangles, nrectangles);
707     if (drawable == p_draw) {
708 	int i;
709 
710 	putc(RC_FILLRECTANGLES, recordFP);
711 	RWriteGC(gc, GCForeground | RTILEGC);
712 	RWriteUShort(nrectangles);
713 	for (i = 0; i < nrectangles; i++) {
714 	    RWriteShort(rectangles[i].x);
715 	    RWriteShort(rectangles[i].y);
716 	    RWriteByte(rectangles[i].width);
717 	    RWriteByte(rectangles[i].height);
718 	}
719     }
720     return 0;
721 }
722 
RDrawArcs(Display * display,Drawable drawable,GC gc,XArc * arcs,int narcs)723 static int RDrawArcs(Display *display, Drawable drawable, GC gc,
724 		     XArc *arcs, int narcs)
725 {
726     XDrawArcs(display, drawable, gc, arcs, narcs);
727     if (drawable == p_draw) {
728 	int i;
729 
730 	putc(RC_DRAWARCS, recordFP);
731 	RWriteGC(gc, RSTROKEGC | RTILEGC);
732 	RWriteUShort(narcs);
733 	for (i = 0; i < narcs; i++) {
734 	    RWriteShort(arcs[i].x);
735 	    RWriteShort(arcs[i].y);
736 	    RWriteByte(arcs[i].width);
737 	    RWriteByte(arcs[i].height);
738 	    RWriteShort(arcs[i].angle1);
739 	    RWriteShort(arcs[i].angle2);
740 	}
741     }
742     return 0;
743 }
744 
RDrawSegments(Display * display,Drawable drawable,GC gc,XSegment * segments,int nsegments)745 static int RDrawSegments(Display *display, Drawable drawable, GC gc,
746 			 XSegment *segments, int nsegments)
747 {
748     XDrawSegments(display, drawable, gc, segments, nsegments);
749     if (drawable == p_draw) {
750 	int i;
751 
752 	putc(RC_DRAWSEGMENTS, recordFP);
753 	RWriteGC(gc, RSTROKEGC | RTILEGC);
754 	RWriteUShort(nsegments);
755 	for (i = 0; i < nsegments; i++) {
756 	    RWriteShort(segments[i].x1);
757 	    RWriteShort(segments[i].y1);
758 	    RWriteShort(segments[i].x2);
759 	    RWriteShort(segments[i].y2);
760 	}
761     }
762     return 0;
763 }
764 
RSetDashes(Display * display,GC gc,int dash_offset,const char * dash_list,int n)765 static int RSetDashes(Display *display, GC gc,
766 		      int dash_offset, const char *dash_list, int n)
767 {
768     XSetDashes(display, gc, dash_offset, dash_list, n);
769     record_dashes = dash_list;	/* supposedly static memory */
770     record_num_dashes = n;
771     record_dash_dirty = True;
772     return 0;
773 }
774 
775 
776 /*
777  * The `_Xconst' trick from <X11/Xfuncproto.h> doesn't work
778  * on Suns when not compiling under full ANSI mode.
779  * So we force the prototypes to use `const' instead of `_Xconst'
780  * by means of defining function types and casting with them.
781  */
782 typedef int (*draw_string_proto_t)(Display *, Drawable, GC,
783 				   int, int, const char *, int);
784 typedef int (*set_dashes_proto_t)(Display *, GC, int, const char *, int);
785 
786 /*
787  * X windows drawing
788  */
789 static struct recordable_drawing Xdrawing = {
790     Dummy_newFrame,
791     Dummy_endFrame,
792     XDrawArc,
793     XDrawLines,
794     XDrawLine,
795     XDrawRectangle,
796     (draw_string_proto_t)XDrawString,
797     XFillArc,
798     XFillPolygon,
799 #ifdef _WINDOWS
800     paintItemSymbol,
801 #else
802     Dummy_paintItemSymbol,
803 #endif
804     XFillRectangle,
805     XFillRectangles,
806     XDrawArcs,
807     XDrawSegments,
808     (set_dashes_proto_t)XSetDashes,
809 };
810 
811 /*
812  * Recording + X windows drawing
813  */
814 static struct recordable_drawing Rdrawing = {
815     RNewFrame,
816     REndFrame,
817     RDrawArc,
818     RDrawLines,
819     RDrawLine,
820     RDrawRectangle,
821     RDrawString,
822     RFillArc,
823     RFillPolygon,
824     RPaintItemSymbol,
825     RFillRectangle,
826     RFillRectangles,
827     RDrawArcs,
828     RDrawSegments,
829     RSetDashes,
830 };
831 
832 /*
833  * Publicly accessible drawing routines.
834  * This is either a copy of Xdrawing or of Rdrawing.
835  */
836 struct recordable_drawing rd;
837 
838 /*
839  * Return the number of bytes written sofar to
840  * the record file.  This way the user can monitor
841  * that she ain't filling up all of her diskspace.
842  */
Record_size(void)843 long Record_size(void)
844 {
845     return (recordFP != NULL) ? ftell(recordFP) : 0L;
846 }
847 
848 /*
849  * Toggle the recording of frames.
850  * This only makes sense if there has been defined
851  * a filename to write the recordings to.
852  * When recording is turned on for the first time
853  * then we have to open the file to write to.
854  */
Record_toggle(void)855 void Record_toggle(void)
856 {
857 #if !(defined(_WINDOWS) && defined(PENS_OF_PLENTY))
858 /* No recording available with PEN_OF_PLENTY under Windows.
859 */
860     if (record_filename != NULL) {
861 	if (!record_start) {
862 	    record_start = True;
863 	    if (!recordFP) {
864 		if ((recordFP = fopen(record_filename, "w")) == NULL) {
865 		    perror("Unable to open record file");
866 		    free(record_filename);
867 		    record_filename = NULL;
868 		    record_start = False;
869 		} else {
870 		    setvbuf(recordFP, NULL, _IOFBF, (size_t)(8 * 1024));
871 # ifdef _WINDOWS
872 			setmode(fileno(recordFP), O_BINARY);
873 # endif
874 		}
875 	    }
876 	} else {
877 	    record_start = False;
878 	}
879 	if (record_start) {
880 	    rd = Rdrawing;
881 	} else {
882 	    rd = Xdrawing;
883 	    recording = False;
884 	}
885     }
886 #endif
887 }
888 
889 /*
890  * Inform the user how many frames have been
891  * written and remind her to which file.
892  */
Record_cleanup(void)893 void Record_cleanup(void)
894 {
895     if (record_filename != NULL && record_frame_count > 0) {
896 	fflush(recordFP);
897 	printf("Recorded %d frames to %s\n",
898 	       record_frame_count, record_filename);
899     }
900 }
901 
902 /*
903  * Store the name of the file where the user
904  * wants recordings to be written to.
905  */
Record_init(char * filename)906 void Record_init(char *filename)
907 {
908     rd = Xdrawing;
909     if (filename != NULL && filename[0] != '\0') {
910 	record_filename = xp_strdup(filename);
911     }
912 }
913 
914