rnd-20040816-2-src
[rocksndiamonds.git] / src / libem / init.c
1
2 #if defined(TARGET_X11)
3
4 /* 2000-08-10T18:03:54Z
5  *
6  * open X11 display and sound
7  */
8
9 #if 1
10 #include <X11/Xlib.h>
11 #include <X11/Xutil.h>
12 #include <X11/Xatom.h>
13 #include <X11/Xos.h>
14 #include <X11/Intrinsic.h>
15 #include <X11/keysymdef.h>
16 #endif
17
18 #include <X11/Xlib.h>
19 #include <X11/Xutil.h>
20 #include <X11/Xatom.h>
21 #include <X11/keysym.h>
22 #include <X11/cursorfont.h>
23 #include <X11/xpm.h>
24 #include <sys/types.h>
25 #include <sys/wait.h>
26 #include <unistd.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <signal.h>
30 #include <string.h>
31 #include <errno.h>
32
33 #include "../libgame/platform.h"
34
35 #include "global.h"
36 #include "display.h"
37 #include "sample.h"
38
39 #if 0
40 Display *display;
41 Window xwindow;
42 #endif
43
44 #if 1
45 Bitmap *objBitmap;
46 Bitmap *botBitmap;
47 Bitmap *sprBitmap;
48 Bitmap *ttlBitmap;
49 #endif
50
51 #if 1
52 Bitmap *screenBitmap;
53 Bitmap *scoreBitmap;
54 #endif
55
56 Pixmap screenPixmap;
57 Pixmap scorePixmap;
58 Pixmap spriteBitmap;
59
60 Pixmap objPixmap;
61 Pixmap objmaskBitmap;
62 Pixmap botPixmap;
63 Pixmap botmaskBitmap;
64 Pixmap sprPixmap;
65 Pixmap sprmaskBitmap;
66 Pixmap ttlPixmap;
67 Pixmap ttlmaskBitmap;
68
69 GC screenGC;
70 GC scoreGC;
71 GC spriteGC;
72 GC antsGC;
73
74 Atom deleteAtom;
75
76 KeySym lastKeySym;
77
78 KeyCode northKeyCode[3];
79 KeyCode eastKeyCode[3];
80 KeyCode southKeyCode[3];
81 KeyCode westKeyCode[3];
82 KeyCode fireKeyCode[3];
83 KeyCode escKeyCode[1];
84
85 char play[SAMPLE_MAX];
86
87 static int sound_pid = -1;
88 int sound_pipe[2] = { -1, -1 }; /* for communication */
89 short *sound_data[SAMPLE_MAX]; /* pointer to sound data */
90 long sound_length[SAMPLE_MAX]; /* length of sound data */
91
92 static Screen *defaultScreen;
93 static Visual *defaultVisual;
94 static Colormap defaultColourmap;
95 static Window defaultRootWindow;
96 static unsigned int screenDepth;
97 static unsigned int screenWidth;
98 static unsigned int screenHeight;
99 static unsigned long screenBlackPixel;
100 static unsigned long screenWhitePixel;
101
102 #if 0
103 static XSizeHints sizeHints;
104 static XSetWindowAttributes setWindowAttributes;
105 static XWMHints wmHints;
106 static XVisualInfo visualInfo;
107 #endif
108 static XGCValues gcValues;
109
110 static Colormap privateColourmap;
111 static Cursor cursor;
112 static XColor *privateColours;
113 static unsigned char *privateFlags;
114 static int privateNumColours;
115
116 static XColor redColour;
117 static XColor whiteColour;
118 static int gotRed;
119 static int gotWhite;
120
121 #if 1
122 static Bitmap *pcxBitmaps[4];
123 static Bitmap *pcxBitmapsX2[4];
124 #endif
125
126 #if 0
127 static Pixmap xpmPixmaps[4];
128 static Pixmap xpmBitmaps[4];
129 static XpmAttributes xpmAttributes[4];
130 static int xpmGot[4];
131 #endif
132
133 static int xpmAllocColourFunc(Display *, Colormap, char *, XColor *, void *);
134 static int xpmFreeColoursFunc(Display *, Colormap, unsigned long *, int, void *);
135
136 static KeyCode keycodes[16];
137
138 #if 0
139 static const char *xpmNames[4] = { "object.xpm", "score.xpm", "sprite.xpm", "title.xpm" };
140 #endif
141
142 #if 1
143 static const char *pcxNames[4] = { "object.pcx", "score.pcx", "sprite.pcx", "title.pcx" };
144 #endif
145
146 static const int xpmCloseness[4] = { 10000, 10000, 40000, 50000 };
147 static const KeySym keysyms[16] = {
148         XK_Up, XK_KP_Up, XK_r, /* north */
149         XK_Right, XK_KP_Right, XK_g, /* east */
150         XK_Down, XK_KP_Down, XK_f, /* south */
151         XK_Left, XK_KP_Left, XK_d, /* west */
152         XK_Shift_L, XK_Control_R, XK_space, /* fire */
153         XK_Escape /* escape */
154 };
155 static const char *sound_names[SAMPLE_MAX] = {
156         "00.blank.au","01.roll.au","02.stone.au","03.nut.au","04.crack.au",
157         "05.bug.au","06.tank.au","07.android.au","08.spring.au","09.slurp.au",
158         "10.eater.au","11.alien.au","12.collect.au","13.diamond.au","14.squash.au",
159         "15.drip.au","16.push.au","17.dirt.au","18.acid.au","19.ball.au",
160         "20.grow.au","21.wonder.au","22.door.au","23.exit.au","24.dynamite.au",
161         "25.tick.au","26.press.au","27.wheel.au","28.boom.au","29.time.au",
162         "30.die.au"
163 };
164 static const int sound_volume[SAMPLE_MAX] = {
165         20,100,100,100,100,20,20,100,100,100,
166         50,100,100,100,100,100,100,100,100,100,
167         100,20,100,100,100,100,100,20,100,100,
168         100
169 };
170
171 int open_all(void)
172 {
173         char name[MAXNAME+2];
174 #if 0
175         void *dummyptr;
176         int dummyint;
177 #endif
178         int i;
179
180 #if 0
181         display = XOpenDisplay(arg_display);
182         if(display == 0) {
183                 fprintf(stderr, "%s: \"%s\": %s: %s\n", progname, XDisplayName(arg_display), "failed to open display", strerror(errno));
184                 return(1);
185         }
186 #endif
187
188         defaultScreen = DefaultScreenOfDisplay(display);
189         defaultVisual = DefaultVisualOfScreen(defaultScreen);
190         defaultColourmap = DefaultColormapOfScreen(defaultScreen);
191         defaultRootWindow = RootWindowOfScreen(defaultScreen);
192         screenDepth = DefaultDepthOfScreen(defaultScreen);
193         screenWidth = WidthOfScreen(defaultScreen);
194         screenHeight = HeightOfScreen(defaultScreen);
195         screenBlackPixel = BlackPixelOfScreen(defaultScreen);
196         screenWhitePixel = WhitePixelOfScreen(defaultScreen);
197
198 #if 0
199         if(arg_install) {
200                 visualInfo.visualid = XVisualIDFromVisual(defaultVisual);
201                 dummyptr = XGetVisualInfo(display, VisualIDMask, &visualInfo, &dummyint);
202                 if(dummyptr == 0) {
203                         fprintf(stderr, "%s: \"%s\": %s: %s\n", progname, XDisplayName(arg_display), "failed to get visual info", strerror(errno));
204                         return(1);
205                 }
206                 memcpy(&visualInfo, dummyptr, sizeof(visualInfo));
207                 XFree(dummyptr);
208
209                 if(visualInfo.class != PseudoColor) {
210                         fprintf(stderr, "%s: \"%s\": %s\n", progname, XDisplayName(arg_display), "private colourmap only supported for pseudocolour display");
211                         return(1);
212                 }
213
214                 privateColourmap = XCreateColormap(display, defaultRootWindow, defaultVisual, AllocAll);
215                 if(privateColourmap == 0) {
216                         fprintf(stderr, "%s: \"%s\": %s: %s\n", progname, XDisplayName(arg_display), "failed to create colourmap", strerror(errno));
217                         return(1);
218                 }
219
220                 privateNumColours = visualInfo.colormap_size;
221
222                 privateColours = malloc(privateNumColours * sizeof(XColor));
223                 if(privateColours == 0) {
224                         fprintf(stderr, "%s: %s (%d): %s\n", progname, "malloc failed", privateNumColours * sizeof(XColor), strerror(errno));
225                         return(1);
226                 }
227                 for(dummyint = 0; dummyint < privateNumColours; dummyint++) privateColours[dummyint].pixel = dummyint;
228                 XQueryColors(display, defaultColourmap, privateColours, privateNumColours);
229                 XStoreColors(display, privateColourmap, privateColours, privateNumColours);
230
231                 privateFlags = malloc(privateNumColours);
232                 if(privateFlags == 0) {
233                         fprintf(stderr, "%s: %s (%d): %s\n", progname, "malloc failed", privateNumColours, strerror(errno));
234                         return(1);
235                 }
236                 memset(privateFlags, 0, privateNumColours);
237                 privateFlags[0] = 1; /* first two entries (black and white) are already allocated */
238                 privateFlags[1] = 1;
239         }
240
241         sizeHints.flags = PSize | PMinSize | PMaxSize;
242         sizeHints.width = 20 * TILEX;
243         sizeHints.height = 12 * TILEY + SCOREY;
244         sizeHints.min_width = sizeHints.max_width = sizeHints.width;
245         sizeHints.min_height = sizeHints.max_height = sizeHints.height;
246         if(arg_geometry) {
247                 dummyint = XWMGeometry(display, XScreenNumberOfScreen(defaultScreen), arg_geometry, 0, 2, &sizeHints, &sizeHints.x, &sizeHints.y, &dummyint, &dummyint, &sizeHints.win_gravity);
248                 if(dummyint & (XValue | YValue)) sizeHints.flags |= USPosition | PWinGravity;
249         }
250
251         xwindow = XCreateWindow(display, defaultRootWindow, sizeHints.x, sizeHints.y, sizeHints.width, sizeHints.height, 2, screenDepth, InputOutput, CopyFromParent, 0, 0);
252         if(xwindow == 0) {
253                 fprintf(stderr, "%s: \"%s\": %s: %s\n", progname, XDisplayName(arg_display), "failed to open window", strerror(errno));
254                 return(1);
255         }
256
257         setWindowAttributes.background_pixel = screenBlackPixel;
258         setWindowAttributes.border_pixel = screenWhitePixel;
259         setWindowAttributes.backing_store = NotUseful;
260         setWindowAttributes.override_redirect = False;
261         setWindowAttributes.event_mask = KeyPressMask | EnterWindowMask | LeaveWindowMask | ExposureMask;
262         setWindowAttributes.colormap = privateColourmap ? privateColourmap : defaultColourmap;
263         XChangeWindowAttributes(display, xwindow, CWBackPixel | CWBorderPixel | CWBackingStore | CWOverrideRedirect | CWEventMask | CWColormap, &setWindowAttributes);
264
265         XStoreName(display, xwindow, "Emerald Mine");
266
267         wmHints.flags = InputHint | StateHint;
268         wmHints.input = True;
269         wmHints.initial_state = NormalState;
270         XSetWMHints(display, xwindow, &wmHints);
271
272         XSetWMNormalHints(display, xwindow, &sizeHints);
273
274         deleteAtom = XInternAtom(display, "WM_DELETE_WINDOW", False);
275         XSetWMProtocols(display, xwindow, &deleteAtom, 1);
276
277         cursor = XCreateFontCursor(display, XC_trek);
278         if(cursor) XDefineCursor(display, xwindow, cursor);
279
280         XMapWindow(display, xwindow);
281 #endif
282
283 #if 0
284         for(i = 0; i < 4; i++) {
285                 name[MAXNAME] = 0;
286                 if(arg_basedir) {
287                         snprintf(name, MAXNAME+2, "%s/%s/%s", arg_basedir, EM_GFX_DIR, xpmNames[i]);
288                 } else {
289                         snprintf(name, MAXNAME+2, "%s/%s", EM_GFX_DIR, xpmNames[i]);
290                 }
291                 if(name[MAXNAME]) snprintf_overflow("read graphics/ files");
292
293                 xpmAttributes[i].valuemask = XpmColormap | XpmReturnAllocPixels | XpmExactColors | XpmCloseness | XpmAllocColor | XpmFreeColors;
294                 xpmAttributes[i].colormap = privateColourmap ? privateColourmap : defaultColourmap;
295                 xpmAttributes[i].exactColors = False;
296                 xpmAttributes[i].closeness = xpmCloseness[i];
297                 xpmAttributes[i].alloc_color = xpmAllocColourFunc;
298                 xpmAttributes[i].free_colors = xpmFreeColoursFunc;
299                 dummyint = XpmReadFileToPixmap(display, xwindow, name, &xpmPixmaps[i], &xpmBitmaps[i], &xpmAttributes[i]);
300                 if(dummyint) {
301                         fprintf(stderr, "%s: \"%s\": \"%s\": %s: %s: %s\n", progname, XDisplayName(arg_display), name, "failed to read xpm", XpmGetErrorString(dummyint), strerror(errno));
302                         return(1);
303                 }
304                 xpmGot[i] = 1;
305         }
306 #endif
307
308         for(i = 0; i < 4; i++)
309         {
310           name[MAXNAME] = 0;
311           snprintf(name, MAXNAME+2, "%s/%s", EM_GFX_DIR, pcxNames[i]);
312
313           if (name[MAXNAME])
314             snprintf_overflow("read graphics/ files");
315
316           if ((pcxBitmaps[i] = LoadImage(name)) == NULL)
317           {
318             printf("::: LoadImage() failed for file '%s'\n", name);
319             return 1;
320           }
321         }
322
323         objBitmap = pcxBitmaps[0];
324         botBitmap = pcxBitmaps[1];
325         sprBitmap = pcxBitmaps[2];
326         ttlBitmap = pcxBitmaps[3];
327
328 #if 0
329         objPixmap = xpmPixmaps[0];
330         botPixmap = xpmPixmaps[1];
331         sprPixmap = xpmPixmaps[2];
332         ttlPixmap = xpmPixmaps[3];
333         objmaskBitmap = xpmBitmaps[0];
334         botmaskBitmap = xpmBitmaps[1];
335         sprmaskBitmap = xpmBitmaps[2];
336         ttlmaskBitmap = xpmBitmaps[3];
337 #else
338         objPixmap = pcxBitmaps[0]->drawable;
339         botPixmap = pcxBitmaps[1]->drawable;
340         sprPixmap = pcxBitmaps[2]->drawable;
341         ttlPixmap = pcxBitmaps[3]->drawable;
342         objmaskBitmap = pcxBitmaps[0]->clip_mask;
343         botmaskBitmap = pcxBitmaps[1]->clip_mask;
344         sprmaskBitmap = pcxBitmaps[2]->clip_mask;
345         ttlmaskBitmap = pcxBitmaps[3]->clip_mask;
346 #endif
347
348 #if 0
349         for (i = 0; i < 4; i++)
350           pcxBitmapsX2[i] = ZoomBitmap(pcxBitmaps[i],
351                                        pcxBitmaps[i]->width * 2,
352                                        pcxBitmaps[i]->height * 2);
353
354         objBitmap = pcxBitmapsX2[0];
355         botBitmap = pcxBitmapsX2[1];
356         sprBitmap = pcxBitmapsX2[2];
357         ttlBitmap = pcxBitmapsX2[3];
358
359         objPixmap = pcxBitmapsX2[0]->drawable;
360         botPixmap = pcxBitmapsX2[1]->drawable;
361         sprPixmap = pcxBitmapsX2[2]->drawable;
362         ttlPixmap = pcxBitmapsX2[3]->drawable;
363         objmaskBitmap = pcxBitmapsX2[0]->clip_mask;
364         botmaskBitmap = pcxBitmapsX2[1]->clip_mask;
365         sprmaskBitmap = pcxBitmapsX2[2]->clip_mask;
366         ttlmaskBitmap = pcxBitmapsX2[3]->clip_mask;
367 #endif
368
369 #if 1
370         screenBitmap = CreateBitmap(22 * TILEX, 14 * TILEY, DEFAULT_DEPTH);
371         scoreBitmap = CreateBitmap(20 * TILEX, SCOREY, DEFAULT_DEPTH);
372 #endif
373
374         screenPixmap = XCreatePixmap(display, xwindow, 22 * TILEX, 14 * TILEY, screenDepth);
375         if(screenPixmap == 0) {
376                 fprintf(stderr, "%s: \"%s\": %s: %s\n", progname, XDisplayName(arg_display), "failed to create pixmap", strerror(errno));
377                 return(1);
378         }
379
380         scorePixmap = XCreatePixmap(display, xwindow, 20 * TILEX, SCOREY, screenDepth);
381         if(scorePixmap == 0) {
382                 fprintf(stderr, "%s: \"%s\": %s: %s\n", progname, XDisplayName(arg_display), "failed to create pixmap", strerror(errno));
383                 return(1);
384         }
385
386         spriteBitmap = XCreatePixmap(display, xwindow, TILEX, TILEY, 1);
387         if(spriteBitmap == 0) {
388                 fprintf(stderr, "%s: \"%s\": %s: %s\n", progname, XDisplayName(arg_display), "failed to create pixmap", strerror(errno));
389                 return(1);
390         }
391
392         redColour.pixel = screenWhitePixel;
393         whiteColour.pixel = screenBlackPixel;
394         gotRed = (xpmAllocColourFunc(display, privateColourmap ? privateColourmap : defaultColourmap, "red", &redColour, 0) > 0);
395         gotWhite = (xpmAllocColourFunc(display, privateColourmap ? privateColourmap : defaultColourmap, "white", &whiteColour, 0) > 0);
396
397         gcValues.graphics_exposures = False;
398         screenGC = XCreateGC(display, screenPixmap, GCGraphicsExposures, &gcValues);
399         if(screenGC == 0) {
400                 fprintf(stderr, "%s: \"%s\": %s: %s\n", progname, XDisplayName(arg_display), "failed to create graphics context", strerror(errno));
401                 return(1);
402         }
403
404         gcValues.graphics_exposures = False;
405         scoreGC = XCreateGC(display, scorePixmap, GCGraphicsExposures, &gcValues);
406         if(scoreGC == 0) {
407                 fprintf(stderr, "%s: \"%s\": %s: %s\n", progname, XDisplayName(arg_display), "failed to create graphics context", strerror(errno));
408                 return(1);
409         }
410
411         gcValues.function = objmaskBitmap ? GXcopyInverted : sprmaskBitmap ? GXcopy : GXset;
412         gcValues.graphics_exposures = False;
413         spriteGC = XCreateGC(display, spriteBitmap, GCFunction | GCGraphicsExposures, &gcValues);
414         if(spriteGC == 0) {
415                 fprintf(stderr, "%s: \"%s\": %s: %s\n", progname, XDisplayName(arg_display), "failed to create graphics context", strerror(errno));
416                 return(1);
417         }
418
419         gcValues.foreground = redColour.pixel;
420         gcValues.background = whiteColour.pixel;
421         gcValues.line_style = LineDoubleDash;
422         gcValues.graphics_exposures = False;
423         antsGC = XCreateGC(display, screenPixmap, GCForeground | GCBackground | GCLineStyle | GCGraphicsExposures, &gcValues);
424         if(antsGC == 0) {
425                 fprintf(stderr, "%s: \"%s\": %s: %s\n", progname, XDisplayName(arg_display), "failed to create graphics context", strerror(errno));
426                 return(1);
427         }
428
429         for(i = 0; i < 16; i++) {
430                 keycodes[i] = XKeysymToKeycode(display, keysyms[i]);
431         }
432         for(i = 0; i < 3; i++) northKeyCode[i] = keycodes[i + 0];
433         for(i = 0; i < 3; i++) eastKeyCode[i] = keycodes[i + 3];
434         for(i = 0; i < 3; i++) southKeyCode[i] = keycodes[i + 6];
435         for(i = 0; i < 3; i++) westKeyCode[i] = keycodes[i + 9];
436         for(i = 0; i < 3; i++) fireKeyCode[i] = keycodes[i + 12];
437         for(i = 0; i < 1; i++) escKeyCode[i] = keycodes[i + 15];
438
439 #if defined(PLATFORM_LINUX) || defined(PLATFORM_BSD)
440         if(arg_silence == 0) {
441                 for(i = 0; i < SAMPLE_MAX; i++) {
442                         name[MAXNAME] = 0;
443                         if(arg_basedir) {
444                                 snprintf(name, MAXNAME+2, "%s/%s/%s", arg_basedir, EM_SND_DIR, sound_names[i]);
445                         } else {
446                                 snprintf(name, MAXNAME+2, "%s/%s", EM_SND_DIR, sound_names[i]);
447                         }
448                         if(name[MAXNAME]) snprintf_overflow("read sounds/ directory");
449
450                         if(read_sample(name, &sound_data[i], &sound_length[i])) return(1);
451
452                         {
453                                 short *ptr, *stop;
454                                 int mult = sound_volume[i] * 65536 / (100 * MIXER_MAX);
455                                 stop = sound_data[i] + sound_length[i];
456                                 for(ptr = sound_data[i]; ptr < stop; ptr++) *ptr = (*ptr * mult) / 65536;
457                         }
458                 }
459
460                 if(pipe(sound_pipe) == -1) {
461                         fprintf(stderr, "%s: %s: %s\n", progname, "unable to create sound pipe", strerror(errno));
462                         return(1);
463                 }
464                 sound_pid = fork();
465                 if(sound_pid == -1) {
466                         fprintf(stderr, "%s: %s: %s\n", progname, "unable to fork sound thread", strerror(errno));
467                         return(1);
468                 }
469                 close(sound_pipe[sound_pid == 0]); sound_pipe[sound_pid == 0] = -1;
470                 if(sound_pid == 0) _exit(sound_thread());
471                 signal(SIGPIPE, SIG_IGN); /* dont crash if sound process dies */
472         }
473 #endif /* defined(PLATFORM_LINUX) || defined(PLATFORM_BSD) */
474
475         return(0);
476 }
477
478 void close_all(void)
479 {
480         int i;
481
482         if(sound_pid != -1) {
483                 kill(sound_pid, SIGTERM);
484                 waitpid(sound_pid, 0, 0);
485         }
486         if(sound_pipe[0] != -1) close(sound_pipe[0]);
487         if(sound_pipe[1] != -1) close(sound_pipe[1]);
488         for(i = 0; i < SAMPLE_MAX; i++) if(sound_data[i]) free(sound_data[i]);
489
490 #if 0
491         for(i = 0; i < 4; i++) if(xpmPixmaps[i]) XFreePixmap(display, xpmPixmaps[i]);
492         for(i = 0; i < 4; i++) if(xpmBitmaps[i]) XFreePixmap(display, xpmBitmaps[i]);
493         for(i = 0; i < 4; i++) if(xpmGot[i]) {
494                 xpmFreeColoursFunc(display, xpmAttributes[i].colormap, xpmAttributes[i].alloc_pixels, xpmAttributes[i].nalloc_pixels, 0);
495                 XpmFreeAttributes(&xpmAttributes[i]);
496         }
497 #endif
498
499         if(gotRed) xpmFreeColoursFunc(display, privateColourmap ? privateColourmap : defaultColourmap, &redColour.pixel, 1, 0);
500         if(gotWhite) xpmFreeColoursFunc(display, privateColourmap ? privateColourmap : defaultColourmap, &whiteColour.pixel, 1, 0);
501
502         if(screenGC) XFreeGC(display, screenGC);
503         if(scoreGC) XFreeGC(display, scoreGC);
504         if(spriteGC) XFreeGC(display, spriteGC);
505         if(antsGC) XFreeGC(display, antsGC);
506         if(screenPixmap) XFreePixmap(display, screenPixmap);
507         if(scorePixmap) XFreePixmap(display, scorePixmap);
508         if(spriteBitmap) XFreePixmap(display, spriteBitmap);
509         if(xwindow) XDestroyWindow(display, xwindow);
510         if(cursor) XFreeCursor(display, cursor);
511         if(privateColourmap) XFreeColormap(display, privateColourmap);
512         if(privateColours) free(privateColours);
513         if(privateFlags) free(privateFlags);
514         if(display) XCloseDisplay(display);
515 }
516
517 /* ---------------------------------------------------------------------- */
518
519 void sound_play(void)
520 {
521         if(sound_pipe[1] != -1) {
522                 if(write(sound_pipe[1], &play, sizeof(play)) == -1) {
523                         fprintf(stderr, "%s: %s: %s\n", progname, "write sound", strerror(errno));
524                         if(sound_pipe[0] != -1) { close(sound_pipe[0]); sound_pipe[0] = -1; }
525                         if(sound_pipe[1] != -1) { close(sound_pipe[1]); sound_pipe[1] = -1; }
526                 }
527         }
528         memset(play, 0, sizeof(play));
529 }
530
531 /* ---------------------------------------------------------------------- */
532
533 static int xpmAllocColourFunc(Display *display, Colormap colourmap, char *colourname, XColor *xcolour, void *closure)
534 {
535         int i, match;
536         int r,g,b;
537         long best, sum;
538
539         if(colourname) if(XParseColor(display, colourmap, colourname, xcolour) == 0) return(-1); /* invalid name */
540         if(colourmap != privateColourmap) return(XAllocColor(display, colourmap, xcolour) != 0);
541
542 /* first try to find an exact match */
543         match = -1;
544         for(i = 0; i < privateNumColours; i++) {
545                 if(privateColours[i].red == xcolour->red && privateColours[i].green == xcolour->green && privateColours[i].blue == xcolour->blue) match = i;
546         }
547         if(match != -1) {
548                 privateFlags[match] = 1;
549                 xcolour->pixel = privateColours[match].pixel;
550                 return(1);
551         }
552
553 /* then find an unallocated colour that is close to what we want */
554         match = -1;
555         best = 1000000;
556         for(i = 0; i < privateNumColours; i++) {
557                 if(privateFlags[i]) continue; /* skip if it is already allocated */
558                 r = (privateColours[i].red - xcolour->red) / 256;
559                 g = (privateColours[i].green - xcolour->green) / 256;
560                 b = (privateColours[i].blue - xcolour->blue) / 256;
561                 sum = r * r + g * g + b * b;
562                 if(sum < best) {
563                         best = sum;
564                         match = i;
565                 }
566         }
567         if(match != -1) {
568                 privateFlags[match] = 1;
569                 privateColours[match].red = xcolour->red;
570                 privateColours[match].green = xcolour->green;
571                 privateColours[match].blue = xcolour->blue;
572                 XStoreColor(display, colourmap, &privateColours[match]);
573                 xcolour->pixel = privateColours[match].pixel;
574                 return(1); /* found a close match */
575         }
576
577 /* if all else fails, just find the closest colour and return it */
578         match = -1;
579         best = 1000000;
580         for(i = 0; i < privateNumColours; i++) {
581                 r = (privateColours[i].red - xcolour->red) / 256;
582                 g = (privateColours[i].green - xcolour->green) / 256;
583                 b = (privateColours[i].blue - xcolour->blue) / 256;
584                 sum = r * r + g * g + b * b;
585                 if(sum < best) {
586                         best = sum;
587                         match = i;
588                 }
589         }
590         if(match != -1) {
591                 xcolour->red = privateColours[match].red;
592                 xcolour->green = privateColours[match].green;
593                 xcolour->blue = privateColours[match].blue;
594                 xcolour->pixel = privateColours[match].pixel;
595                 return(1); /* best we could do */
596         }
597         return(0); /* still didnt find one, give up */
598 }
599
600 static int xpmFreeColoursFunc(Display *display, Colormap colourmap, unsigned long *pixels, int npixels, void *closure)
601 {
602         if(colourmap != privateColourmap) XFreeColors(display, colourmap, pixels, npixels, 0);
603         return(1); /* non-zero for success */
604 }
605
606 #endif