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