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