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