rnd-20100421-1-src
[rocksndiamonds.git] / src / libgame / system.c
1 /***********************************************************
2 * Artsoft Retro-Game Library                               *
3 *----------------------------------------------------------*
4 * (c) 1994-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * system.c                                                 *
12 ***********************************************************/
13
14 #include <string.h>
15 #include <signal.h>
16
17 #include "platform.h"
18
19 #if defined(PLATFORM_MSDOS)
20 #include <fcntl.h>
21 #endif
22
23 #include "system.h"
24 #include "image.h"
25 #include "sound.h"
26 #include "setup.h"
27 #include "joystick.h"
28 #include "misc.h"
29
30
31 /* ========================================================================= */
32 /* exported variables                                                        */
33 /* ========================================================================= */
34
35 struct ProgramInfo      program;
36 struct OptionInfo       options;
37 struct VideoSystemInfo  video;
38 struct AudioSystemInfo  audio;
39 struct GfxInfo          gfx;
40 struct ArtworkInfo      artwork;
41 struct JoystickInfo     joystick;
42 struct SetupInfo        setup;
43
44 LevelDirTree           *leveldir_first_all = NULL;
45 LevelDirTree           *leveldir_first = NULL;
46 LevelDirTree           *leveldir_current = NULL;
47 int                     level_nr;
48
49 Display                *display = NULL;
50 Visual                 *visual = NULL;
51 int                     screen = 0;
52 Colormap                cmap = None;
53
54 DrawWindow             *window = NULL;
55 DrawBuffer             *backbuffer = NULL;
56 DrawBuffer             *drawto = NULL;
57
58 int                     button_status = MB_NOT_PRESSED;
59 boolean                 motion_status = FALSE;
60
61 int                     redraw_mask = REDRAW_NONE;
62 int                     redraw_tiles = 0;
63
64 int                     FrameCounter = 0;
65
66
67 /* ========================================================================= */
68 /* init/close functions                                                      */
69 /* ========================================================================= */
70
71 void InitProgramInfo(char *argv0,
72                      char *userdata_subdir, char *userdata_subdir_unix,
73                      char *program_title, char *window_title, char *icon_title,
74                      char *x11_icon_filename, char *x11_iconmask_filename,
75                      char *sdl_icon_filename, char *msdos_cursor_filename,
76                      char *cookie_prefix, char *filename_prefix,
77                      int program_version)
78 {
79   program.command_basepath = getBasePath(argv0);
80   program.command_basename = getBaseName(argv0);
81
82   program.userdata_subdir = userdata_subdir;
83   program.userdata_subdir_unix = userdata_subdir_unix;
84   program.userdata_path = getUserGameDataDir();
85
86   program.program_title = program_title;
87   program.window_title = window_title;
88   program.icon_title = icon_title;
89
90   program.x11_icon_filename = x11_icon_filename;
91   program.x11_iconmask_filename = x11_iconmask_filename;
92   program.sdl_icon_filename = sdl_icon_filename;
93   program.msdos_cursor_filename = msdos_cursor_filename;
94
95   program.cookie_prefix = cookie_prefix;
96   program.filename_prefix = filename_prefix;
97
98   program.version_major = VERSION_MAJOR(program_version);
99   program.version_minor = VERSION_MINOR(program_version);
100   program.version_patch = VERSION_PATCH(program_version);
101
102   program.error_filename = getErrorFilename(ERROR_BASENAME);
103   program.error_file = stderr;
104 }
105
106 void InitExitFunction(void (*exit_function)(int))
107 {
108   program.exit_function = exit_function;
109
110   /* set signal handlers to custom exit function */
111   signal(SIGINT, exit_function);
112   signal(SIGTERM, exit_function);
113
114 #if defined(TARGET_SDL)
115   /* set exit function to automatically cleanup SDL stuff after exit() */
116   atexit(SDL_Quit);
117 #endif
118 }
119
120 void InitPlatformDependentStuff(void)
121 {
122 #if defined(PLATFORM_MSDOS)
123   _fmode = O_BINARY;
124 #endif
125
126 #if defined(PLATFORM_MACOSX)
127   updateUserGameDataDir();
128 #endif
129
130 #if !defined(PLATFORM_UNIX) || defined(PLATFORM_MACOSX)
131   openErrorFile();
132 #endif
133
134 #if defined(TARGET_SDL)
135   if (SDL_Init(SDL_INIT_EVENTTHREAD | SDL_INIT_NOPARACHUTE) < 0)
136     Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
137
138   SDLNet_Init();
139 #endif
140 }
141
142 void ClosePlatformDependentStuff(void)
143 {
144 #if defined(PLATFORM_WIN32) || defined(PLATFORM_MSDOS)
145   closeErrorFile();
146 #endif
147
148 #if defined(PLATFORM_MSDOS)
149   dumpErrorFile();
150 #endif
151 }
152
153 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
154                       int real_sx, int real_sy,
155                       int full_sxsize, int full_sysize,
156                       Bitmap *field_save_buffer)
157 {
158   gfx.sx = sx;
159   gfx.sy = sy;
160   gfx.sxsize = sxsize;
161   gfx.sysize = sysize;
162   gfx.real_sx = real_sx;
163   gfx.real_sy = real_sy;
164   gfx.full_sxsize = full_sxsize;
165   gfx.full_sysize = full_sysize;
166
167   gfx.field_save_buffer = field_save_buffer;
168
169 #if 0
170   gfx.background_bitmap = NULL;
171   gfx.background_bitmap_mask = REDRAW_NONE;
172 #endif
173
174   SetDrawDeactivationMask(REDRAW_NONE);         /* do not deactivate drawing */
175   SetDrawBackgroundMask(REDRAW_NONE);           /* deactivate masked drawing */
176 }
177
178 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
179 {
180   gfx.dx = dx;
181   gfx.dy = dy;
182   gfx.dxsize = dxsize;
183   gfx.dysize = dysize;
184 }
185
186 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
187 {
188   gfx.vx = vx;
189   gfx.vy = vy;
190   gfx.vxsize = vxsize;
191   gfx.vysize = vysize;
192 }
193
194 void InitGfxWindowInfo(int win_xsize, int win_ysize)
195 {
196   gfx.win_xsize = win_xsize;
197   gfx.win_ysize = win_ysize;
198
199 #if 1
200   gfx.background_bitmap_mask = REDRAW_NONE;
201
202   ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize, DEFAULT_DEPTH);
203 #endif
204 }
205
206 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
207 {
208   /* currently only used by MSDOS code to alloc VRAM buffer, if available */
209   /* 2009-03-24: also (temporarily?) used for overlapping blit workaround */
210   gfx.scrollbuffer_width = scrollbuffer_width;
211   gfx.scrollbuffer_height = scrollbuffer_height;
212 }
213
214 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
215 {
216   gfx.clipping_enabled = enabled;
217   gfx.clip_x = x;
218   gfx.clip_y = y;
219   gfx.clip_width = width;
220   gfx.clip_height = height;
221 }
222
223 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
224 {
225   gfx.draw_busy_anim_function = draw_busy_anim_function;
226 }
227
228 void InitGfxCustomArtworkInfo()
229 {
230   gfx.override_level_graphics = FALSE;
231   gfx.override_level_sounds = FALSE;
232   gfx.override_level_music = FALSE;
233
234   gfx.draw_init_text = TRUE;
235 }
236
237 void SetDrawDeactivationMask(int draw_deactivation_mask)
238 {
239   gfx.draw_deactivation_mask = draw_deactivation_mask;
240 }
241
242 void SetDrawBackgroundMask(int draw_background_mask)
243 {
244   gfx.draw_background_mask = draw_background_mask;
245 }
246
247 static void DrawBitmapFromTile(Bitmap *bitmap, Bitmap *tile,
248                                int dest_x, int dest_y, int width, int height)
249 {
250   int bitmap_xsize = width;
251   int bitmap_ysize = height;
252   int tile_xsize = tile->width;
253   int tile_ysize = tile->height;
254   int tile_xsteps = (bitmap_xsize + tile_xsize - 1) / tile_xsize;
255   int tile_ysteps = (bitmap_ysize + tile_ysize - 1) / tile_ysize;
256   int x, y;
257
258   for (y = 0; y < tile_ysteps; y++)
259   {
260     for (x = 0; x < tile_xsteps; x++)
261     {
262       int draw_x = dest_x + x * tile_xsize;
263       int draw_y = dest_y + y * tile_ysize;
264       int draw_xsize = MIN(tile_xsize, bitmap_xsize - x * tile_xsize);
265       int draw_ysize = MIN(tile_ysize, bitmap_ysize - y * tile_ysize);
266
267       BlitBitmap(tile, bitmap, 0, 0, draw_xsize, draw_ysize, draw_x, draw_y);
268     }
269   }
270 }
271
272 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
273 {
274   if (background_bitmap_tile != NULL)
275     gfx.background_bitmap_mask |= mask;
276   else
277     gfx.background_bitmap_mask &= ~mask;
278
279 #if 0
280   if (gfx.background_bitmap == NULL)
281     gfx.background_bitmap = CreateBitmap(video.width, video.height,
282                                          DEFAULT_DEPTH);
283 #endif
284
285   if (background_bitmap_tile == NULL)   /* empty background requested */
286     return;
287
288   if (mask == REDRAW_ALL)
289     DrawBitmapFromTile(gfx.background_bitmap, background_bitmap_tile,
290                        0, 0, video.width, video.height);
291   else if (mask == REDRAW_FIELD)
292     DrawBitmapFromTile(gfx.background_bitmap, background_bitmap_tile,
293                        gfx.real_sx, gfx.real_sy,
294                        gfx.full_sxsize, gfx.full_sysize);
295   else if (mask == REDRAW_DOOR_1)
296   {
297     DrawBitmapFromTile(gfx.background_bitmap, background_bitmap_tile,
298                        gfx.dx, gfx.dy,
299                        gfx.dxsize, gfx.dysize);
300   }
301 }
302
303 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
304 {
305   /* remove every mask before setting mask for window */
306   /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
307   SetBackgroundBitmap(NULL, 0xffff);            /* !!! FIX THIS !!! */
308   SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
309 }
310
311 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
312 {
313   /* remove window area mask before setting mask for main area */
314   /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
315   SetBackgroundBitmap(NULL, REDRAW_ALL);        /* !!! FIX THIS !!! */
316   SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
317 }
318
319 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
320 {
321   /* remove window area mask before setting mask for door area */
322   /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
323   SetBackgroundBitmap(NULL, REDRAW_ALL);        /* !!! FIX THIS !!! */
324   SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
325 }
326
327
328 /* ========================================================================= */
329 /* video functions                                                           */
330 /* ========================================================================= */
331
332 inline static int GetRealDepth(int depth)
333 {
334   return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
335 }
336
337 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
338                                int width, int height, Pixel color)
339 {
340 #if defined(TARGET_SDL)
341   SDLFillRectangle(bitmap, x, y, width, height, color);
342 #else
343   X11FillRectangle(bitmap, x, y, width, height, color);
344 #endif
345 }
346
347 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
348                                int src_x, int src_y, int width, int height,
349                                int dst_x, int dst_y, int mask_mode)
350 {
351 #if defined(TARGET_SDL)
352   SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
353               dst_x, dst_y, mask_mode);
354 #else
355   X11CopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
356               dst_x, dst_y, mask_mode);
357 #endif
358 }
359
360 void InitVideoDisplay(void)
361 {
362 #if defined(TARGET_SDL)
363   SDLInitVideoDisplay();
364 #else
365   X11InitVideoDisplay();
366 #endif
367 }
368
369 void CloseVideoDisplay(void)
370 {
371   KeyboardAutoRepeatOn();
372
373 #if defined(TARGET_SDL)
374   SDL_QuitSubSystem(SDL_INIT_VIDEO);
375 #else
376   if (display)
377     XCloseDisplay(display);
378 #endif
379 }
380
381 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
382 {
383   video.width = width;
384   video.height = height;
385   video.depth = GetRealDepth(depth);
386
387   video.fullscreen_available = FULLSCREEN_STATUS;
388   video.fullscreen_enabled = FALSE;
389 #if 0
390   video.fullscreen_mode_current = NULL;
391   video.fullscreen_modes = NULL;
392 #endif
393
394 #if defined(TARGET_SDL)
395   SDLInitVideoBuffer(&backbuffer, &window, fullscreen);
396 #else
397   X11InitVideoBuffer(&backbuffer, &window);
398 #endif
399
400   drawto = backbuffer;
401 }
402
403 inline static void FreeBitmapPointers(Bitmap *bitmap)
404 {
405   if (bitmap == NULL)
406     return;
407
408 #if defined(TARGET_SDL)
409   SDLFreeBitmapPointers(bitmap);
410 #else
411   X11FreeBitmapPointers(bitmap);
412 #endif
413
414   checked_free(bitmap->source_filename);
415   bitmap->source_filename = NULL;
416 }
417
418 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
419                                           Bitmap *dst_bitmap)
420 {
421   if (src_bitmap == NULL || dst_bitmap == NULL)
422     return;
423
424   FreeBitmapPointers(dst_bitmap);
425
426   *dst_bitmap = *src_bitmap;
427 }
428
429 void FreeBitmap(Bitmap *bitmap)
430 {
431   if (bitmap == NULL)
432     return;
433
434   FreeBitmapPointers(bitmap);
435
436   free(bitmap);
437 }
438
439 Bitmap *CreateBitmapStruct(void)
440 {
441 #if defined(TARGET_SDL)
442   return checked_calloc(sizeof(struct SDLSurfaceInfo));
443 #else
444   return checked_calloc(sizeof(struct X11DrawableInfo));
445 #endif
446 }
447
448 Bitmap *CreateBitmap(int width, int height, int depth)
449 {
450   Bitmap *new_bitmap = CreateBitmapStruct();
451   int real_width  = MAX(1, width);      /* prevent zero bitmap width */
452   int real_height = MAX(1, height);     /* prevent zero bitmap height */
453   int real_depth  = GetRealDepth(depth);
454
455 #if defined(TARGET_SDL)
456   SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
457 #else
458   X11CreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
459 #endif
460
461   new_bitmap->width  = real_width;
462   new_bitmap->height = real_height;
463
464   return new_bitmap;
465 }
466
467 void ReCreateBitmap(Bitmap **bitmap, int width, int height, int depth)
468 {
469   Bitmap *new_bitmap = CreateBitmap(width, height, depth);
470
471   if (*bitmap == NULL)
472   {
473     *bitmap = new_bitmap;
474   }
475   else
476   {
477     TransferBitmapPointers(new_bitmap, *bitmap);
478     free(new_bitmap);
479   }
480 }
481
482 void CloseWindow(DrawWindow *window)
483 {
484 #if defined(TARGET_X11)
485   X11CloseWindow(window);
486 #endif
487 }
488
489 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
490                                        int draw_mask)
491 {
492   if (draw_mask == REDRAW_NONE)
493     return FALSE;
494
495   if (draw_mask & REDRAW_ALL)
496     return TRUE;
497
498   if ((draw_mask & REDRAW_FIELD) &&
499       x >= gfx.real_sx && x < gfx.real_sx + gfx.full_sxsize)
500     return TRUE;
501
502   if ((draw_mask & REDRAW_DOOR_1) &&
503       x >= gfx.dx && y < gfx.dy + gfx.dysize)
504     return TRUE;
505
506   if ((draw_mask & REDRAW_DOOR_2) &&
507       x >= gfx.dx && y >= gfx.vy)
508     return TRUE;
509
510   return FALSE;
511 }
512
513 boolean DrawingDeactivated(int x, int y, int width, int height)
514 {
515   return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
516 }
517
518 boolean DrawingOnBackground(int x, int y)
519 {
520   return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
521           CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
522 }
523
524 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
525                                   int *width, int *height, boolean is_dest)
526 {
527 #if 1
528   int clip_x, clip_y, clip_width, clip_height;
529
530   if (gfx.clipping_enabled && is_dest)  /* only clip destination bitmap */
531   {
532     clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
533     clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
534     clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
535     clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
536   }
537   else
538   {
539     clip_x = 0;
540     clip_y = 0;
541     clip_width = bitmap->width;
542     clip_height = bitmap->height;
543   }
544
545   /* skip if rectangle completely outside bitmap */
546
547   if (*x + *width  <= clip_x ||
548       *y + *height <= clip_y ||
549       *x >= clip_x + clip_width ||
550       *y >= clip_y + clip_height)
551     return FALSE;
552
553   /* clip if rectangle overlaps bitmap */
554
555   if (*x < clip_x)
556   {
557     *width -= clip_x - *x;
558     *x = clip_x;
559   }
560   else if (*x + *width > clip_x + clip_width)
561   {
562     *width = clip_x + clip_width - *x;
563   }
564
565   if (*y < clip_y)
566   {
567     *height -= clip_y - *y;
568     *y = clip_y;
569   }
570   else if (*y + *height > clip_y + clip_height)
571   {
572     *height = clip_y + clip_height - *y;
573   }
574
575   return TRUE;
576
577 #else
578
579   /* skip if rectangle completely outside bitmap */
580
581   if (*x + *width <= 0 ||
582       *y + *height <= 0 ||
583       *x >= bitmap->width ||
584       *y >= bitmap->height)
585     return FALSE;
586
587   /* clip if rectangle overlaps bitmap */
588
589   if (*x < 0)
590   {
591     *width += *x;
592     *x = 0;
593   }
594   else if (*x + *width > bitmap->width)
595   {
596     *width = bitmap->width - *x;
597   }
598
599   if (*y < 0)
600   {
601     *height += *y;
602     *y = 0;
603   }
604   else if (*y + *height > bitmap->height)
605   {
606     *height = bitmap->height - *y;
607   }
608
609   return TRUE;
610 #endif
611 }
612
613 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
614                 int src_x, int src_y, int width, int height,
615                 int dst_x, int dst_y)
616 {
617   int dst_x_unclipped = dst_x;
618   int dst_y_unclipped = dst_y;
619
620   if (DrawingDeactivated(dst_x, dst_y, width, height))
621     return;
622
623 #if 1
624   if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
625       !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
626     return;
627
628   /* source x/y might need adjustment if destination x/y was clipped top/left */
629   src_x += dst_x - dst_x_unclipped;
630   src_y += dst_y - dst_y_unclipped;
631
632 #else
633   /* skip if rectangle starts outside bitmap */
634   if (src_x >= src_bitmap->width ||
635       src_y >= src_bitmap->height ||
636       dst_x >= dst_bitmap->width ||
637       dst_y >= dst_bitmap->height)
638     return;
639
640   /* clip if rectangle overlaps bitmap */
641   if (src_x + width > src_bitmap->width)
642     width = src_bitmap->width - src_x;
643   if (src_y + height > src_bitmap->height)
644     height = src_bitmap->height - src_y;
645   if (dst_x + width > dst_bitmap->width)
646     width = dst_bitmap->width - dst_x;
647   if (dst_y + height > dst_bitmap->height)
648     height = dst_bitmap->height - dst_y;
649 #endif
650
651 #if 0
652   /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
653   /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
654      but is already fixed in SVN and should therefore finally be fixed with
655      the next official SDL release, which is probably version 1.2.14.) */
656 #if 1
657   /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
658 #if defined(TARGET_SDL) && defined(PLATFORM_WIN32)
659   if (src_bitmap == dst_bitmap)
660   {
661     /* !!! THIS IS A BUG (IN THE SDL LIBRARY?) AND SHOULD BE FIXED !!! */
662
663     /* needed when blitting directly to same bitmap -- should not be needed with
664        recent SDL libraries, but apparently does not work in 1.2.11 directly */
665
666     static Bitmap *tmp_bitmap = NULL;
667     static int tmp_bitmap_xsize = 0;
668     static int tmp_bitmap_ysize = 0;
669
670     /* start with largest static bitmaps for initial bitmap size ... */
671     if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
672     {
673       tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
674       tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
675     }
676
677     /* ... and allow for later re-adjustments due to custom artwork bitmaps */
678     if (src_bitmap->width > tmp_bitmap_xsize ||
679         src_bitmap->height > tmp_bitmap_ysize)
680     {
681       tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
682       tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
683
684       FreeBitmap(tmp_bitmap);
685
686       tmp_bitmap = NULL;
687     }
688
689     if (tmp_bitmap == NULL)
690       tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
691                                 DEFAULT_DEPTH);
692
693     sysCopyArea(src_bitmap, tmp_bitmap,
694                 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
695     sysCopyArea(tmp_bitmap, dst_bitmap,
696                 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
697
698     return;
699   }
700 #endif
701 #endif
702 #endif
703
704   sysCopyArea(src_bitmap, dst_bitmap,
705               src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
706 }
707
708 void FadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
709                    int fade_mode, int fade_delay, int post_delay,
710                    void (*draw_border_function)(void))
711 {
712 #if 1
713   /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
714   if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
715     return;
716 #endif
717
718 #if defined(TARGET_SDL)
719   SDLFadeRectangle(bitmap_cross, x, y, width, height,
720                    fade_mode, fade_delay, post_delay, draw_border_function);
721 #else
722   X11FadeRectangle(bitmap_cross, x, y, width, height,
723                    fade_mode, fade_delay, post_delay, draw_border_function);
724 #endif
725 }
726
727 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
728                    Pixel color)
729 {
730   if (DrawingDeactivated(x, y, width, height))
731     return;
732
733 #if 1
734   if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
735     return;
736 #else
737   /* skip if rectangle starts outside bitmap */
738   if (x >= bitmap->width ||
739       y >= bitmap->height)
740     return;
741
742   /* clip if rectangle overlaps bitmap */
743   if (x + width > bitmap->width)
744     width = bitmap->width - x;
745   if (y + height > bitmap->height)
746     height = bitmap->height - y;
747 #endif
748
749   sysFillRectangle(bitmap, x, y, width, height, color);
750 }
751
752 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
753 {
754   FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
755 }
756
757 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
758                                 int width, int height)
759 {
760   if (DrawingOnBackground(x, y))
761     BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
762   else
763     ClearRectangle(bitmap, x, y, width, height);
764 }
765
766 void SetClipMask(Bitmap *bitmap, GC clip_gc, Pixmap clip_pixmap)
767 {
768 #if defined(TARGET_X11)
769   if (clip_gc)
770   {
771     bitmap->clip_gc = clip_gc;
772     XSetClipMask(display, bitmap->clip_gc, clip_pixmap);
773   }
774 #endif
775 }
776
777 void SetClipOrigin(Bitmap *bitmap, GC clip_gc, int clip_x, int clip_y)
778 {
779 #if defined(TARGET_X11)
780   if (clip_gc)
781   {
782     bitmap->clip_gc = clip_gc;
783     XSetClipOrigin(display, bitmap->clip_gc, clip_x, clip_y);
784   }
785 #endif
786 }
787
788 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
789                       int src_x, int src_y, int width, int height,
790                       int dst_x, int dst_y)
791 {
792   if (DrawingDeactivated(dst_x, dst_y, width, height))
793     return;
794
795   sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
796               dst_x, dst_y, BLIT_MASKED);
797 }
798
799 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
800                             int src_x, int src_y, int width, int height,
801                             int dst_x, int dst_y)
802 {
803   if (DrawingOnBackground(dst_x, dst_y))
804   {
805     /* draw background */
806     BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
807                dst_x, dst_y);
808
809     /* draw foreground */
810     SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
811                   dst_x - src_x, dst_y - src_y);
812     BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
813                      dst_x, dst_y);
814   }
815   else
816     BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
817                dst_x, dst_y);
818 }
819
820 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
821                          int to_x, int to_y)
822 {
823 #if defined(TARGET_SDL)
824   SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
825 #else
826   X11DrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
827 #endif
828 }
829
830 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
831                          int to_x, int to_y)
832 {
833 #if defined(TARGET_SDL)
834   SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
835 #else
836   X11DrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
837 #endif
838 }
839
840 #if !defined(TARGET_X11_NATIVE)
841 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
842               int to_x, int to_y, Pixel pixel, int line_width)
843 {
844   int x, y;
845
846   for (x = 0; x < line_width; x++)
847   {
848     for (y = 0; y < line_width; y++)
849     {
850       int dx = x - line_width / 2;
851       int dy = y - line_width / 2;
852
853       if ((x == 0 && y == 0) ||
854           (x == 0 && y == line_width - 1) ||
855           (x == line_width - 1 && y == 0) ||
856           (x == line_width - 1 && y == line_width - 1))
857         continue;
858
859 #if defined(TARGET_SDL)
860       SDLDrawLine(bitmap,
861                   from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
862 #elif defined(TARGET_ALLEGRO)
863       AllegroDrawLine(bitmap->drawable, from_x + dx, from_y + dy,
864                       to_x + dx, to_y + dy, pixel);
865 #endif
866     }
867   }
868 }
869 #endif
870
871 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
872 {
873 #if !defined(TARGET_X11_NATIVE)
874   int line_width = 4;
875   int i;
876
877   for (i = 0; i < num_points - 1; i++)
878     DrawLine(bitmap, points[i].x, points[i].y,
879              points[i + 1].x, points[i + 1].y, pixel, line_width);
880
881   /*
882   SDLDrawLines(bitmap->surface, points, num_points, pixel);
883   */
884 #else
885   XSetForeground(display, bitmap->line_gc[1], pixel);
886   XDrawLines(display, bitmap->drawable, bitmap->line_gc[1],
887              (XPoint *)points, num_points, CoordModeOrigin);
888 #endif
889 }
890
891 Pixel GetPixel(Bitmap *bitmap, int x, int y)
892 {
893   if (x < 0 || x >= bitmap->width ||
894       y < 0 || y >= bitmap->height)
895     return BLACK_PIXEL;
896
897 #if defined(TARGET_SDL)
898   return SDLGetPixel(bitmap, x, y);
899 #elif defined(TARGET_ALLEGRO)
900   return AllegroGetPixel(bitmap->drawable, x, y);
901 #else
902   return X11GetPixel(bitmap, x, y);
903 #endif
904 }
905
906 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
907                       unsigned int color_g, unsigned int color_b)
908 {
909 #if defined(TARGET_SDL)
910   return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
911 #elif defined(TARGET_ALLEGRO)
912   return AllegroAllocColorCell(color_r << 8, color_g << 8, color_b << 8);
913 #else
914   return X11GetPixelFromRGB(color_r, color_g, color_b);
915 #endif
916 }
917
918 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
919 {
920   unsigned int color_r = (color >> 16) & 0xff;
921   unsigned int color_g = (color >>  8) & 0xff;
922   unsigned int color_b = (color >>  0) & 0xff;
923
924   return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
925 }
926
927 /* execute all pending screen drawing operations */
928 void FlushDisplay(void)
929 {
930 #ifndef TARGET_SDL
931   XFlush(display);
932 #endif
933 }
934
935 /* execute and wait for all pending screen drawing operations */
936 void SyncDisplay(void)
937 {
938 #ifndef TARGET_SDL
939   XSync(display, FALSE);
940 #endif
941 }
942
943 void KeyboardAutoRepeatOn(void)
944 {
945 #if defined(TARGET_SDL)
946   SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
947                       SDL_DEFAULT_REPEAT_INTERVAL / 2);
948   SDL_EnableUNICODE(1);
949 #else
950   if (display)
951     XAutoRepeatOn(display);
952 #endif
953 }
954
955 void KeyboardAutoRepeatOff(void)
956 {
957 #if defined(TARGET_SDL)
958   SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
959   SDL_EnableUNICODE(0);
960 #else
961   if (display)
962     XAutoRepeatOff(display);
963 #endif
964 }
965
966 boolean PointerInWindow(DrawWindow *window)
967 {
968 #if defined(TARGET_SDL)
969   return TRUE;
970 #else
971   Window root, child;
972   int root_x, root_y;
973   unsigned int mask;
974   int win_x, win_y;
975
976   /* if XQueryPointer() returns False, the pointer
977      is not on the same screen as the specified window */
978   return XQueryPointer(display, window->drawable, &root, &child,
979                        &root_x, &root_y, &win_x, &win_y, &mask);
980 #endif
981 }
982
983 boolean SetVideoMode(boolean fullscreen)
984 {
985 #if defined(TARGET_SDL)
986   return SDLSetVideoMode(&backbuffer, fullscreen);
987 #else
988   boolean success = TRUE;
989
990   if (fullscreen && video.fullscreen_available)
991   {
992     Error(ERR_WARN, "fullscreen not available in X11 version");
993
994     /* display error message only once */
995     video.fullscreen_available = FALSE;
996
997     success = FALSE;
998   }
999
1000   return success;
1001 #endif
1002 }
1003
1004 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
1005 {
1006 #if defined(TARGET_SDL)
1007   if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
1008       (!fullscreen && video.fullscreen_enabled))
1009     fullscreen = SetVideoMode(fullscreen);
1010 #endif
1011
1012   return fullscreen;
1013 }
1014
1015 Bitmap *LoadImage(char *filename)
1016 {
1017   Bitmap *new_bitmap;
1018
1019 #if defined(TARGET_SDL)
1020   new_bitmap = SDLLoadImage(filename);
1021 #else
1022   new_bitmap = X11LoadImage(filename);
1023 #endif
1024
1025   if (new_bitmap)
1026     new_bitmap->source_filename = getStringCopy(filename);
1027
1028   return new_bitmap;
1029 }
1030
1031 Bitmap *LoadCustomImage(char *basename)
1032 {
1033   char *filename = getCustomImageFilename(basename);
1034   Bitmap *new_bitmap;
1035
1036   if (filename == NULL)
1037     Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
1038
1039   if ((new_bitmap = LoadImage(filename)) == NULL)
1040     Error(ERR_EXIT, "LoadImage() failed: %s", GetError());
1041
1042   return new_bitmap;
1043 }
1044
1045 void ReloadCustomImage(Bitmap *bitmap, char *basename)
1046 {
1047   char *filename = getCustomImageFilename(basename);
1048   Bitmap *new_bitmap;
1049
1050   if (filename == NULL)         /* (should never happen) */
1051   {
1052     Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
1053     return;
1054   }
1055
1056   if (strEqual(filename, bitmap->source_filename))
1057   {
1058     /* The old and new image are the same (have the same filename and path).
1059        This usually means that this image does not exist in this graphic set
1060        and a fallback to the existing image is done. */
1061
1062     return;
1063   }
1064
1065   if ((new_bitmap = LoadImage(filename)) == NULL)
1066   {
1067     Error(ERR_WARN, "LoadImage() failed: %s", GetError());
1068     return;
1069   }
1070
1071   if (bitmap->width != new_bitmap->width ||
1072       bitmap->height != new_bitmap->height)
1073   {
1074     Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
1075           filename);
1076     FreeBitmap(new_bitmap);
1077     return;
1078   }
1079
1080   TransferBitmapPointers(new_bitmap, bitmap);
1081   free(new_bitmap);
1082 }
1083
1084 Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
1085 {
1086   Bitmap *dst_bitmap = CreateBitmap(zoom_width, zoom_height, DEFAULT_DEPTH);
1087
1088 #if defined(TARGET_SDL)
1089   SDLZoomBitmap(src_bitmap, dst_bitmap);
1090 #else
1091   X11ZoomBitmap(src_bitmap, dst_bitmap);
1092 #endif
1093
1094   return dst_bitmap;
1095 }
1096
1097 static void CreateScaledBitmaps(Bitmap *old_bitmap, int zoom_factor,
1098                                 boolean create_small_bitmaps)
1099 {
1100   Bitmap swap_bitmap;
1101   Bitmap *new_bitmap;
1102   Bitmap *tmp_bitmap_1;
1103   Bitmap *tmp_bitmap_2;
1104   Bitmap *tmp_bitmap_4;
1105   Bitmap *tmp_bitmap_8;
1106   Bitmap *tmp_bitmap_16;
1107   Bitmap *tmp_bitmap_32;
1108   int width_1, height_1;
1109   int width_2, height_2;
1110   int width_4, height_4;
1111   int width_8, height_8;
1112   int width_16, height_16;
1113   int width_32, height_32;
1114   int new_width, new_height;
1115
1116   /* calculate new image dimensions for normal sized image */
1117   width_1  = old_bitmap->width  * zoom_factor;
1118   height_1 = old_bitmap->height * zoom_factor;
1119
1120   /* get image with normal size (this might require scaling up) */
1121   if (zoom_factor != 1)
1122     tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1123   else
1124     tmp_bitmap_1 = old_bitmap;
1125
1126   /* this is only needed to make compilers happy */
1127   tmp_bitmap_2 = NULL;
1128   tmp_bitmap_4 = NULL;
1129   tmp_bitmap_8 = NULL;
1130   tmp_bitmap_16 = NULL;
1131   tmp_bitmap_32 = NULL;
1132
1133   if (create_small_bitmaps)
1134   {
1135     /* calculate new image dimensions for small images */
1136     width_2  = width_1  / 2;
1137     height_2 = height_1 / 2;
1138     width_4  = width_1  / 4;
1139     height_4 = height_1 / 4;
1140     width_8  = width_1  / 8;
1141     height_8 = height_1 / 8;
1142     width_16  = width_1  / 16;
1143     height_16 = height_1 / 16;
1144     width_32  = width_1  / 32;
1145     height_32 = height_1 / 32;
1146
1147     UPDATE_BUSY_STATE();
1148
1149     /* get image with 1/2 of normal size (for use in the level editor) */
1150     if (zoom_factor != 2)
1151       tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_1 / 2, height_1 / 2);
1152     else
1153       tmp_bitmap_2 = old_bitmap;
1154
1155     UPDATE_BUSY_STATE();
1156
1157     /* get image with 1/4 of normal size (for use in the level editor) */
1158     if (zoom_factor != 4)
1159       tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_2 / 2, height_2 / 2);
1160     else
1161       tmp_bitmap_4 = old_bitmap;
1162
1163     UPDATE_BUSY_STATE();
1164
1165     /* get image with 1/8 of normal size (for use on the preview screen) */
1166     if (zoom_factor != 8)
1167       tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_4 / 2, height_4 / 2);
1168     else
1169       tmp_bitmap_8 = old_bitmap;
1170
1171     UPDATE_BUSY_STATE();
1172
1173     /* get image with 1/16 of normal size (for use on the preview screen) */
1174     if (zoom_factor != 16)
1175       tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_8 / 2, height_8 / 2);
1176     else
1177       tmp_bitmap_16 = old_bitmap;
1178
1179     UPDATE_BUSY_STATE();
1180
1181     /* get image with 1/32 of normal size (for use on the preview screen) */
1182     if (zoom_factor != 32)
1183       tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_16 / 2, height_16 / 2);
1184     else
1185       tmp_bitmap_32 = old_bitmap;
1186
1187     UPDATE_BUSY_STATE();
1188   }
1189
1190 #if 0
1191   /* if image was scaled up, create new clipmask for normal size image */
1192   if (zoom_factor != 1)
1193   {
1194 #if defined(TARGET_X11)
1195     if (old_bitmap->clip_mask)
1196       XFreePixmap(display, old_bitmap->clip_mask);
1197
1198     old_bitmap->clip_mask =
1199       Pixmap_to_Mask(tmp_bitmap_1->drawable, width_1, height_1);
1200
1201     XSetClipMask(display, old_bitmap->stored_clip_gc, old_bitmap->clip_mask);
1202 #else
1203     SDL_Surface *tmp_surface_1 = tmp_bitmap_1->surface;
1204
1205     if (old_bitmap->surface_masked)
1206       SDL_FreeSurface(old_bitmap->surface_masked);
1207
1208     SDL_SetColorKey(tmp_surface_1, SDL_SRCCOLORKEY,
1209                     SDL_MapRGB(tmp_surface_1->format, 0x00, 0x00, 0x00));
1210     if ((old_bitmap->surface_masked = SDL_DisplayFormat(tmp_surface_1)) ==NULL)
1211       Error(ERR_EXIT, "SDL_DisplayFormat() failed");
1212     SDL_SetColorKey(tmp_surface_1, 0, 0);       /* reset transparent pixel */
1213 #endif
1214   }
1215 #endif
1216
1217   if (create_small_bitmaps)
1218   {
1219     new_width  = width_1;
1220     new_height = height_1 + (height_1 + 1) / 2;     /* prevent odd height */
1221
1222     new_bitmap = CreateBitmap(new_width, new_height, DEFAULT_DEPTH);
1223
1224     BlitBitmap(tmp_bitmap_1, new_bitmap, 0, 0, width_1, height_1, 0, 0);
1225     BlitBitmap(tmp_bitmap_2, new_bitmap, 0, 0, width_1 / 2, height_1 / 2,
1226                0, height_1);
1227     BlitBitmap(tmp_bitmap_4, new_bitmap, 0, 0, width_1 / 4, height_1 / 4,
1228                width_1 / 2, height_1);
1229     BlitBitmap(tmp_bitmap_8, new_bitmap, 0, 0, width_1 / 8, height_1 / 8,
1230                3 * width_1 / 4, height_1);
1231     BlitBitmap(tmp_bitmap_16, new_bitmap, 0, 0, width_1 / 16, height_1 / 16,
1232                7 * width_1 / 8, height_1);
1233     BlitBitmap(tmp_bitmap_32, new_bitmap, 0, 0, width_1 / 32, height_1 / 32,
1234                15 * width_1 / 16, height_1);
1235
1236     UPDATE_BUSY_STATE();
1237   }
1238   else
1239   {
1240     new_width  = width_1;
1241     new_height = height_1;
1242
1243     new_bitmap = tmp_bitmap_1;  /* directly use tmp_bitmap_1 as new bitmap */
1244   }
1245
1246   if (create_small_bitmaps)
1247   {
1248     /* if no small bitmaps created, tmp_bitmap_1 is used as new bitmap now */
1249     if (zoom_factor != 1)
1250       FreeBitmap(tmp_bitmap_1);
1251
1252     if (zoom_factor != 2)
1253       FreeBitmap(tmp_bitmap_2);
1254
1255     if (zoom_factor != 4)
1256       FreeBitmap(tmp_bitmap_4);
1257
1258     if (zoom_factor != 8)
1259       FreeBitmap(tmp_bitmap_8);
1260
1261     if (zoom_factor != 16)
1262       FreeBitmap(tmp_bitmap_16);
1263
1264     if (zoom_factor != 32)
1265       FreeBitmap(tmp_bitmap_32);
1266   }
1267
1268   /* replace image with extended image (containing 1/1, 1/2, 1/4, 1/8 size) */
1269 #if defined(TARGET_SDL)
1270   swap_bitmap.surface = old_bitmap->surface;
1271   old_bitmap->surface = new_bitmap->surface;
1272   new_bitmap->surface = swap_bitmap.surface;
1273 #else
1274   swap_bitmap.drawable = old_bitmap->drawable;
1275   old_bitmap->drawable = new_bitmap->drawable;
1276   new_bitmap->drawable = swap_bitmap.drawable;
1277 #endif
1278
1279   old_bitmap->width  = new_bitmap->width;
1280   old_bitmap->height = new_bitmap->height;
1281
1282 #if 1
1283   /* this replaces all blit masks created when loading -- maybe optimize this */
1284   {
1285 #if defined(TARGET_X11)
1286     if (old_bitmap->clip_mask)
1287       XFreePixmap(display, old_bitmap->clip_mask);
1288
1289     old_bitmap->clip_mask =
1290       Pixmap_to_Mask(old_bitmap->drawable, new_width, new_height);
1291
1292     XSetClipMask(display, old_bitmap->stored_clip_gc, old_bitmap->clip_mask);
1293 #else
1294     SDL_Surface *old_surface = old_bitmap->surface;
1295
1296     if (old_bitmap->surface_masked)
1297       SDL_FreeSurface(old_bitmap->surface_masked);
1298
1299     SDL_SetColorKey(old_surface, SDL_SRCCOLORKEY,
1300                     SDL_MapRGB(old_surface->format, 0x00, 0x00, 0x00));
1301     if ((old_bitmap->surface_masked = SDL_DisplayFormat(old_surface)) ==NULL)
1302       Error(ERR_EXIT, "SDL_DisplayFormat() failed");
1303     SDL_SetColorKey(old_surface, 0, 0);         /* reset transparent pixel */
1304 #endif
1305   }
1306 #endif
1307
1308   UPDATE_BUSY_STATE();
1309
1310   FreeBitmap(new_bitmap);       /* this actually frees the _old_ bitmap now */
1311 }
1312
1313 void CreateBitmapWithSmallBitmaps(Bitmap *old_bitmap, int zoom_factor)
1314 {
1315   CreateScaledBitmaps(old_bitmap, zoom_factor, TRUE);
1316 }
1317
1318 void ScaleBitmap(Bitmap *old_bitmap, int zoom_factor)
1319 {
1320   CreateScaledBitmaps(old_bitmap, zoom_factor, FALSE);
1321 }
1322
1323
1324 /* ------------------------------------------------------------------------- */
1325 /* mouse pointer functions                                                   */
1326 /* ------------------------------------------------------------------------- */
1327
1328 #if !defined(PLATFORM_MSDOS)
1329 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER            0
1330 /* XPM image definitions */
1331 static const char *cursor_image_none[] =
1332 {
1333   /* width height num_colors chars_per_pixel */
1334   "    16    16        3            1",
1335
1336   /* colors */
1337   "X c #000000",
1338   ". c #ffffff",
1339   "  c None",
1340
1341   /* pixels */
1342   "                ",
1343   "                ",
1344   "                ",
1345   "                ",
1346   "                ",
1347   "                ",
1348   "                ",
1349   "                ",
1350   "                ",
1351   "                ",
1352   "                ",
1353   "                ",
1354   "                ",
1355   "                ",
1356   "                ",
1357   "                ",
1358
1359   /* hot spot */
1360   "0,0"
1361 };
1362 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1363 static const char *cursor_image_dot[] =
1364 {
1365   /* width height num_colors chars_per_pixel */
1366   "    16    16        3            1",
1367
1368   /* colors */
1369   "X c #000000",
1370   ". c #ffffff",
1371   "  c None",
1372
1373   /* pixels */
1374   " X              ",
1375   "X.X             ",
1376   " X              ",
1377   "                ",
1378   "                ",
1379   "                ",
1380   "                ",
1381   "                ",
1382   "                ",
1383   "                ",
1384   "                ",
1385   "                ",
1386   "                ",
1387   "                ",
1388   "                ",
1389   "                ",
1390
1391   /* hot spot */
1392   "1,1"
1393 };
1394 static const char **cursor_image_playfield = cursor_image_dot;
1395 #else
1396 /* some people complained about a "white dot" on the screen and thought it
1397    was a graphical error... OK, let's just remove the whole pointer :-) */
1398 static const char **cursor_image_playfield = cursor_image_none;
1399 #endif
1400
1401 #if defined(TARGET_SDL)
1402 static const int cursor_bit_order = BIT_ORDER_MSB;
1403 #elif defined(TARGET_X11_NATIVE)
1404 static const int cursor_bit_order = BIT_ORDER_LSB;
1405 #endif
1406
1407 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1408 {
1409   struct MouseCursorInfo *cursor;
1410   boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1411   int header_lines = 4;
1412   int x, y, i;
1413
1414   cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1415
1416   sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1417
1418   i = -1;
1419   for (y = 0; y < cursor->width; y++)
1420   {
1421     for (x = 0; x < cursor->height; x++)
1422     {
1423       int bit_nr = x % 8;
1424       int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1425
1426       if (bit_nr == 0)
1427       {
1428         i++;
1429         cursor->data[i] = cursor->mask[i] = 0;
1430       }
1431
1432       switch (image[header_lines + y][x])
1433       {
1434         case 'X':
1435           cursor->data[i] |= bit_mask;
1436           cursor->mask[i] |= bit_mask;
1437           break;
1438
1439         case '.':
1440           cursor->mask[i] |= bit_mask;
1441           break;
1442
1443         case ' ':
1444           break;
1445       }
1446     }
1447   }
1448
1449   sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1450
1451   return cursor;
1452 }
1453 #endif  /* !PLATFORM_MSDOS */
1454
1455 void SetMouseCursor(int mode)
1456 {
1457 #if !defined(PLATFORM_MSDOS)
1458   static struct MouseCursorInfo *cursor_none = NULL;
1459   static struct MouseCursorInfo *cursor_playfield = NULL;
1460   struct MouseCursorInfo *cursor_new;
1461
1462   if (cursor_none == NULL)
1463     cursor_none = get_cursor_from_image(cursor_image_none);
1464
1465   if (cursor_playfield == NULL)
1466     cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1467
1468   cursor_new = (mode == CURSOR_DEFAULT   ? NULL :
1469                 mode == CURSOR_NONE      ? cursor_none :
1470                 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1471
1472 #if defined(TARGET_SDL)
1473   SDLSetMouseCursor(cursor_new);
1474 #elif defined(TARGET_X11_NATIVE)
1475   X11SetMouseCursor(cursor_new);
1476 #endif
1477 #endif
1478 }
1479
1480
1481 /* ========================================================================= */
1482 /* audio functions                                                           */
1483 /* ========================================================================= */
1484
1485 void OpenAudio(void)
1486 {
1487   /* always start with reliable default values */
1488   audio.sound_available = FALSE;
1489   audio.music_available = FALSE;
1490   audio.loops_available = FALSE;
1491
1492   audio.sound_enabled = FALSE;
1493   audio.sound_deactivated = FALSE;
1494
1495   audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1496   audio.mixer_pid = 0;
1497   audio.device_name = NULL;
1498   audio.device_fd = -1;
1499
1500   audio.num_channels = 0;
1501   audio.music_channel = 0;
1502   audio.first_sound_channel = 0;
1503
1504 #if defined(TARGET_SDL)
1505   SDLOpenAudio();
1506 #elif defined(PLATFORM_UNIX)
1507   UnixOpenAudio();
1508 #elif defined(PLATFORM_MSDOS)
1509   MSDOSOpenAudio();
1510 #endif
1511 }
1512
1513 void CloseAudio(void)
1514 {
1515 #if defined(TARGET_SDL)
1516   SDLCloseAudio();
1517 #elif defined(PLATFORM_UNIX)
1518   UnixCloseAudio();
1519 #elif defined(PLATFORM_MSDOS)
1520   MSDOSCloseAudio();
1521 #endif
1522
1523   audio.sound_enabled = FALSE;
1524 }
1525
1526 void SetAudioMode(boolean enabled)
1527 {
1528   if (!audio.sound_available)
1529     return;
1530
1531   audio.sound_enabled = enabled;
1532 }
1533
1534
1535 /* ========================================================================= */
1536 /* event functions                                                           */
1537 /* ========================================================================= */
1538
1539 void InitEventFilter(EventFilter filter_function)
1540 {
1541 #if defined(TARGET_SDL)
1542   /* set event filter to filter out certain events */
1543   SDL_SetEventFilter(filter_function);
1544 #endif
1545 }
1546
1547 boolean PendingEvent(void)
1548 {
1549 #if defined(TARGET_SDL)
1550   return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1551 #else
1552   return (XPending(display) ? TRUE : FALSE);
1553 #endif
1554 }
1555
1556 void NextEvent(Event *event)
1557 {
1558 #if defined(TARGET_SDL)
1559   SDLNextEvent(event);
1560 #else
1561   XNextEvent(display, event);
1562 #endif
1563 }
1564
1565 void PeekEvent(Event *event)
1566 {
1567 #if defined(TARGET_SDL)
1568   SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1569 #else
1570   XPeekEvent(display, event);
1571 #endif
1572 }
1573
1574 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1575 {
1576 #if defined(TARGET_SDL)
1577
1578 #if 0
1579   printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1580          (int)event->keysym.unicode,
1581          (int)event->keysym.sym,
1582          (int)SDL_GetModState());
1583 #endif
1584
1585   if (with_modifiers &&
1586       event->keysym.unicode > 0x0000 &&
1587       event->keysym.unicode < 0x2000)
1588     return event->keysym.unicode;
1589   else
1590     return event->keysym.sym;
1591
1592 #else
1593
1594 #if 0
1595   printf("with modifiers == '0x%04x', without modifiers == '0x%04x'\n",
1596          (int)XLookupKeysym(event, event->state),
1597          (int)XLookupKeysym(event, 0));
1598 #endif
1599
1600   if (with_modifiers)
1601     return XLookupKeysym(event, event->state);
1602   else
1603     return XLookupKeysym(event, 0);
1604 #endif
1605 }
1606
1607 KeyMod HandleKeyModState(Key key, int key_status)
1608 {
1609   static KeyMod current_modifiers = KMOD_None;
1610
1611   if (key != KSYM_UNDEFINED)    /* new key => check for modifier key change */
1612   {
1613     KeyMod new_modifier = KMOD_None;
1614
1615     switch(key)
1616     {
1617       case KSYM_Shift_L:
1618         new_modifier = KMOD_Shift_L;
1619         break;
1620       case KSYM_Shift_R:
1621         new_modifier = KMOD_Shift_R;
1622         break;
1623       case KSYM_Control_L:
1624         new_modifier = KMOD_Control_L;
1625         break;
1626       case KSYM_Control_R:
1627         new_modifier = KMOD_Control_R;
1628         break;
1629       case KSYM_Meta_L:
1630         new_modifier = KMOD_Meta_L;
1631         break;
1632       case KSYM_Meta_R:
1633         new_modifier = KMOD_Meta_R;
1634         break;
1635       case KSYM_Alt_L:
1636         new_modifier = KMOD_Alt_L;
1637         break;
1638       case KSYM_Alt_R:
1639         new_modifier = KMOD_Alt_R;
1640         break;
1641       default:
1642         break;
1643     }
1644
1645     if (key_status == KEY_PRESSED)
1646       current_modifiers |= new_modifier;
1647     else
1648       current_modifiers &= ~new_modifier;
1649   }
1650
1651   return current_modifiers;
1652 }
1653
1654 KeyMod GetKeyModState()
1655 {
1656 #if defined(TARGET_SDL)
1657   return (KeyMod)SDL_GetModState();
1658 #else
1659   return HandleKeyModState(KSYM_UNDEFINED, 0);
1660 #endif
1661 }
1662
1663 KeyMod GetKeyModStateFromEvents()
1664 {
1665   /* always use key modifier state as tracked from key events (this is needed
1666      if the modifier key event was injected into the event queue, but the key
1667      was not really pressed on keyboard -- SDL_GetModState() seems to directly
1668      query the keys as held pressed on the keyboard) -- this case is currently
1669      only used to filter out clipboard insert events from "True X-Mouse" tool */
1670
1671   return HandleKeyModState(KSYM_UNDEFINED, 0);
1672 }
1673
1674 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1675 {
1676   if (event->type != EVENT_CLIENTMESSAGE)
1677     return FALSE;
1678
1679 #if defined(TARGET_SDL)
1680   return TRUE;          /* the only possible message here is SDL_QUIT */
1681 #elif defined(PLATFORM_UNIX)
1682   if ((event->window == window->drawable) &&
1683       (event->data.l[0] == XInternAtom(display, "WM_DELETE_WINDOW", FALSE)))
1684     return TRUE;
1685 #endif
1686
1687   return FALSE;
1688 }
1689
1690
1691 /* ========================================================================= */
1692 /* joystick functions                                                        */
1693 /* ========================================================================= */
1694
1695 void InitJoysticks()
1696 {
1697   int i;
1698
1699 #if defined(NO_JOYSTICK)
1700   return;       /* joysticks generally deactivated by compile-time directive */
1701 #endif
1702
1703   /* always start with reliable default values */
1704   joystick.status = JOYSTICK_NOT_AVAILABLE;
1705   for (i = 0; i < MAX_PLAYERS; i++)
1706     joystick.fd[i] = -1;                /* joystick device closed */
1707
1708 #if defined(TARGET_SDL)
1709   SDLInitJoysticks();
1710 #elif defined(PLATFORM_UNIX)
1711   UnixInitJoysticks();
1712 #elif defined(PLATFORM_MSDOS)
1713   MSDOSInitJoysticks();
1714 #endif
1715
1716 #if 0
1717   for (i = 0; i < MAX_PLAYERS; i++)
1718     printf("::: Joystick for player %d: %d\n", i, joystick.fd[i]);
1719 #endif
1720 }
1721
1722 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1723 {
1724 #if defined(TARGET_SDL)
1725   return SDLReadJoystick(nr, x, y, b1, b2);
1726 #elif defined(PLATFORM_UNIX)
1727   return UnixReadJoystick(nr, x, y, b1, b2);
1728 #elif defined(PLATFORM_MSDOS)
1729   return MSDOSReadJoystick(nr, x, y, b1, b2);
1730 #endif
1731 }