added custom graphics properties to define image and in-game tile size
[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   Bitmap *dst_bitmap = CreateBitmap(zoom_width, zoom_height, DEFAULT_DEPTH);
1119
1120   SDLZoomBitmap(src_bitmap, dst_bitmap);
1121
1122   return dst_bitmap;
1123 }
1124
1125 static void CreateScaledBitmaps(Bitmap *old_bitmap, int zoom_factor,
1126                                 int tile_size, boolean create_small_bitmaps)
1127 {
1128   Bitmap swap_bitmap;
1129   Bitmap *new_bitmap;
1130   Bitmap *tmp_bitmap_final = NULL;
1131   Bitmap *tmp_bitmap_0 = NULL;
1132   Bitmap *tmp_bitmap_1 = NULL;
1133   Bitmap *tmp_bitmap_2 = NULL;
1134   Bitmap *tmp_bitmap_4 = NULL;
1135   Bitmap *tmp_bitmap_8 = NULL;
1136   Bitmap *tmp_bitmap_16 = NULL;
1137   Bitmap *tmp_bitmap_32 = NULL;
1138   int width_final, height_final;
1139   int width_0, height_0;
1140   int width_1, height_1;
1141   int width_2, height_2;
1142   int width_4, height_4;
1143   int width_8, height_8;
1144   int width_16, height_16;
1145 #if 1
1146   int width_32, height_32;
1147 #endif
1148   int old_width, old_height;
1149   int new_width, new_height;
1150
1151   old_width  = old_bitmap->width;
1152   old_height = old_bitmap->height;
1153
1154 #if 1
1155   /* calculate new image dimensions for final image size */
1156   width_final  = old_width  * zoom_factor;
1157   height_final = old_height * zoom_factor;
1158
1159   /* get image with final size (this might require scaling up) */
1160   /* ("final" size may result in non-standard tile size image) */
1161   if (zoom_factor != 1)
1162     tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1163   else
1164     tmp_bitmap_final = old_bitmap;
1165
1166 #else
1167
1168   /* calculate new image dimensions for final image size */
1169   width_1  = old_width  * zoom_factor;
1170   height_1 = old_height * zoom_factor;
1171
1172   /* get image with final size (this might require scaling up) */
1173   /* ("final" size may result in non-standard tile size image) */
1174   if (zoom_factor != 1)
1175     tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1176   else
1177     tmp_bitmap_1 = old_bitmap;
1178 #endif
1179
1180   UPDATE_BUSY_STATE();
1181
1182   width_0  = width_1  = width_final;
1183   height_0 = height_1 = height_final;
1184
1185   tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1186
1187 #if 1
1188   if (create_small_bitmaps)
1189   {
1190     /* check if we have a non-gameplay tile size image */
1191     if (tile_size != gfx.game_tile_size)
1192     {
1193       /* get image with gameplay tile size */
1194       width_0  = width_final  * gfx.game_tile_size / tile_size;
1195       height_0 = height_final * gfx.game_tile_size / tile_size;
1196
1197       if (width_0 == old_width)
1198         tmp_bitmap_0 = old_bitmap;
1199       else if (width_0 == width_final)
1200         tmp_bitmap_0 = tmp_bitmap_final;
1201       else
1202         tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1203
1204       UPDATE_BUSY_STATE();
1205     }
1206
1207     /* check if we have a non-standard tile size image */
1208     if (tile_size != gfx.standard_tile_size)
1209     {
1210       /* get image with standard tile size */
1211       width_1  = width_final  * gfx.standard_tile_size / tile_size;
1212       height_1 = height_final * gfx.standard_tile_size / tile_size;
1213
1214       if (width_1 == old_width)
1215         tmp_bitmap_1 = old_bitmap;
1216       else if (width_1 == width_final)
1217         tmp_bitmap_1 = tmp_bitmap_final;
1218       else if (width_1 == width_0)
1219         tmp_bitmap_1 = tmp_bitmap_0;
1220       else
1221         tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1222
1223       UPDATE_BUSY_STATE();
1224     }
1225   }
1226 #endif
1227
1228   if (create_small_bitmaps)
1229   {
1230     /* calculate new image dimensions for small images */
1231     width_2  = width_1  / 2;
1232     height_2 = height_1 / 2;
1233     width_4  = width_1  / 4;
1234     height_4 = height_1 / 4;
1235     width_8  = width_1  / 8;
1236     height_8 = height_1 / 8;
1237     width_16  = width_1  / 16;
1238     height_16 = height_1 / 16;
1239 #if 1
1240     width_32  = width_1  / 32;
1241     height_32 = height_1 / 32;
1242 #endif
1243
1244 #if 1
1245     /* get image with 1/2 of normal size (for use in the level editor) */
1246     if (width_2 == old_width)
1247       tmp_bitmap_2 = old_bitmap;
1248     else
1249       tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1250
1251     UPDATE_BUSY_STATE();
1252
1253     /* get image with 1/4 of normal size (for use in the level editor) */
1254     if (width_4 == old_width)
1255       tmp_bitmap_4 = old_bitmap;
1256     else
1257       tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1258
1259     UPDATE_BUSY_STATE();
1260
1261     /* get image with 1/8 of normal size (for use on the preview screen) */
1262     if (width_8 == old_width)
1263       tmp_bitmap_8 = old_bitmap;
1264     else
1265       tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1266
1267     UPDATE_BUSY_STATE();
1268
1269     /* get image with 1/16 of normal size (for use on the preview screen) */
1270     if (width_16 == old_width)
1271       tmp_bitmap_16 = old_bitmap;
1272     else
1273       tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1274
1275     UPDATE_BUSY_STATE();
1276
1277     /* get image with 1/32 of normal size (for use on the preview screen) */
1278     if (width_32 == old_width)
1279       tmp_bitmap_32 = old_bitmap;
1280     else
1281       tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1282
1283     UPDATE_BUSY_STATE();
1284
1285 #else
1286
1287     /* get image with 1/2 of normal size (for use in the level editor) */
1288     if (zoom_factor != 2)
1289       tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_1 / 2, height_1 / 2);
1290     else
1291       tmp_bitmap_2 = old_bitmap;
1292
1293     UPDATE_BUSY_STATE();
1294
1295     /* get image with 1/4 of normal size (for use in the level editor) */
1296     if (zoom_factor != 4)
1297       tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_2 / 2, height_2 / 2);
1298     else
1299       tmp_bitmap_4 = old_bitmap;
1300
1301     UPDATE_BUSY_STATE();
1302
1303     /* get image with 1/8 of normal size (for use on the preview screen) */
1304     if (zoom_factor != 8)
1305       tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_4 / 2, height_4 / 2);
1306     else
1307       tmp_bitmap_8 = old_bitmap;
1308
1309     UPDATE_BUSY_STATE();
1310
1311     /* get image with 1/16 of normal size (for use on the preview screen) */
1312     if (zoom_factor != 16)
1313       tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_8 / 2, height_8 / 2);
1314     else
1315       tmp_bitmap_16 = old_bitmap;
1316
1317     UPDATE_BUSY_STATE();
1318
1319     /* get image with 1/32 of normal size (for use on the preview screen) */
1320     if (zoom_factor != 32)
1321       tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_16 / 2, height_16 / 2);
1322     else
1323       tmp_bitmap_32 = old_bitmap;
1324
1325     UPDATE_BUSY_STATE();
1326 #endif
1327   }
1328
1329 #if 0
1330   /* if image was scaled up, create new clipmask for normal size image */
1331   if (zoom_factor != 1)
1332   {
1333     SDL_Surface *tmp_surface_1 = tmp_bitmap_1->surface;
1334
1335     if (old_bitmap->surface_masked)
1336       SDL_FreeSurface(old_bitmap->surface_masked);
1337
1338     SDL_SetColorKey(tmp_surface_1, SET_TRANSPARENT_PIXEL,
1339                     SDL_MapRGB(tmp_surface_1->format, 0x00, 0x00, 0x00));
1340     if ((old_bitmap->surface_masked = SDL_DisplayFormat(tmp_surface_1)) ==NULL)
1341       Error(ERR_EXIT, "SDL_DisplayFormat() failed");
1342     SDL_SetColorKey(tmp_surface_1, UNSET_TRANSPARENT_PIXEL, 0);
1343   }
1344 #endif
1345
1346   if (create_small_bitmaps)
1347   {
1348     new_width  = width_1;
1349     new_height = height_1 + (height_1 + 1) / 2;     /* prevent odd height */
1350
1351 #if 1
1352     if (width_0 != width_1)
1353     {
1354       new_width += width_0;
1355       new_height = MAX(new_height, height_0);
1356     }
1357 #endif
1358
1359     new_bitmap = CreateBitmap(new_width, new_height, DEFAULT_DEPTH);
1360
1361 #if 1
1362     if (width_0 != width_1)
1363       BlitBitmap(tmp_bitmap_0, new_bitmap, 0, 0, width_0, height_0, width_1, 0);
1364 #endif
1365
1366     BlitBitmap(tmp_bitmap_1, new_bitmap, 0, 0, width_1, height_1, 0, 0);
1367     BlitBitmap(tmp_bitmap_2, new_bitmap, 0, 0, width_1 / 2, height_1 / 2,
1368                0, height_1);
1369     BlitBitmap(tmp_bitmap_4, new_bitmap, 0, 0, width_1 / 4, height_1 / 4,
1370                width_1 / 2, height_1);
1371     BlitBitmap(tmp_bitmap_8, new_bitmap, 0, 0, width_1 / 8, height_1 / 8,
1372                3 * width_1 / 4, height_1);
1373     BlitBitmap(tmp_bitmap_16, new_bitmap, 0, 0, width_1 / 16, height_1 / 16,
1374                7 * width_1 / 8, height_1);
1375     BlitBitmap(tmp_bitmap_32, new_bitmap, 0, 0, width_1 / 32, height_1 / 32,
1376                15 * width_1 / 16, height_1);
1377
1378     UPDATE_BUSY_STATE();
1379   }
1380   else
1381   {
1382     new_width  = width_1;
1383     new_height = height_1;
1384
1385     new_bitmap = tmp_bitmap_1;  /* directly use tmp_bitmap_1 as new bitmap */
1386   }
1387
1388   if (create_small_bitmaps)
1389   {
1390     /* if no small bitmaps created, tmp_bitmap_1 is used as new bitmap now */
1391
1392 #if 1
1393     if (tmp_bitmap_final != old_bitmap)
1394       FreeBitmap(tmp_bitmap_final);
1395
1396     if (tmp_bitmap_0 != old_bitmap &&
1397         tmp_bitmap_0 != tmp_bitmap_final)
1398       FreeBitmap(tmp_bitmap_0);
1399
1400     if (tmp_bitmap_1 != old_bitmap &&
1401         tmp_bitmap_1 != tmp_bitmap_final &&
1402         tmp_bitmap_1 != tmp_bitmap_0)
1403       FreeBitmap(tmp_bitmap_1);
1404
1405     if (tmp_bitmap_2 != old_bitmap)
1406       FreeBitmap(tmp_bitmap_2);
1407
1408     if (tmp_bitmap_4 != old_bitmap)
1409       FreeBitmap(tmp_bitmap_4);
1410
1411     if (tmp_bitmap_8 != old_bitmap)
1412       FreeBitmap(tmp_bitmap_8);
1413
1414     if (tmp_bitmap_16 != old_bitmap)
1415       FreeBitmap(tmp_bitmap_16);
1416
1417     if (tmp_bitmap_32 != old_bitmap)
1418       FreeBitmap(tmp_bitmap_32);
1419
1420 #else
1421
1422     if (zoom_factor != 1)
1423       FreeBitmap(tmp_bitmap_1);
1424
1425     if (zoom_factor != 2)
1426       FreeBitmap(tmp_bitmap_2);
1427
1428     if (zoom_factor != 4)
1429       FreeBitmap(tmp_bitmap_4);
1430
1431     if (zoom_factor != 8)
1432       FreeBitmap(tmp_bitmap_8);
1433
1434     if (zoom_factor != 16)
1435       FreeBitmap(tmp_bitmap_16);
1436
1437     if (zoom_factor != 32)
1438       FreeBitmap(tmp_bitmap_32);
1439 #endif
1440   }
1441
1442   /* replace image with extended image (containing 1/1, 1/2, 1/4, 1/8 size) */
1443 #if defined(TARGET_SDL)
1444   swap_bitmap.surface = old_bitmap->surface;
1445   old_bitmap->surface = new_bitmap->surface;
1446   new_bitmap->surface = swap_bitmap.surface;
1447 #else
1448   swap_bitmap.drawable = old_bitmap->drawable;
1449   old_bitmap->drawable = new_bitmap->drawable;
1450   new_bitmap->drawable = swap_bitmap.drawable;
1451 #endif
1452
1453   old_bitmap->width  = new_bitmap->width;
1454   old_bitmap->height = new_bitmap->height;
1455
1456 #if 1
1457   /* this replaces all blit masks created when loading -- maybe optimize this */
1458   {
1459     SDL_Surface *old_surface = old_bitmap->surface;
1460
1461     if (old_bitmap->surface_masked)
1462       SDL_FreeSurface(old_bitmap->surface_masked);
1463
1464     SDL_SetColorKey(old_surface, SET_TRANSPARENT_PIXEL,
1465                     SDL_MapRGB(old_surface->format, 0x00, 0x00, 0x00));
1466
1467     if ((old_bitmap->surface_masked = SDL_DisplayFormat(old_surface)) == NULL)
1468       Error(ERR_EXIT, "SDL_DisplayFormat() failed");
1469
1470     SDL_SetColorKey(old_surface, UNSET_TRANSPARENT_PIXEL, 0);
1471   }
1472 #endif
1473
1474   UPDATE_BUSY_STATE();
1475
1476   FreeBitmap(new_bitmap);       /* this actually frees the _old_ bitmap now */
1477 }
1478
1479 void CreateBitmapWithSmallBitmaps(Bitmap *old_bitmap, int zoom_factor,
1480                                   int tile_size)
1481 {
1482   CreateScaledBitmaps(old_bitmap, zoom_factor, tile_size, TRUE);
1483 }
1484
1485 void ScaleBitmap(Bitmap *old_bitmap, int zoom_factor)
1486 {
1487   CreateScaledBitmaps(old_bitmap, zoom_factor, 0, FALSE);
1488 }
1489
1490
1491 /* ------------------------------------------------------------------------- */
1492 /* mouse pointer functions                                                   */
1493 /* ------------------------------------------------------------------------- */
1494
1495 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER            0
1496
1497 /* XPM image definitions */
1498 static const char *cursor_image_none[] =
1499 {
1500   /* width height num_colors chars_per_pixel */
1501   "    16    16        3            1",
1502
1503   /* colors */
1504   "X c #000000",
1505   ". c #ffffff",
1506   "  c None",
1507
1508   /* pixels */
1509   "                ",
1510   "                ",
1511   "                ",
1512   "                ",
1513   "                ",
1514   "                ",
1515   "                ",
1516   "                ",
1517   "                ",
1518   "                ",
1519   "                ",
1520   "                ",
1521   "                ",
1522   "                ",
1523   "                ",
1524   "                ",
1525
1526   /* hot spot */
1527   "0,0"
1528 };
1529
1530 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1531 static const char *cursor_image_dot[] =
1532 {
1533   /* width height num_colors chars_per_pixel */
1534   "    16    16        3            1",
1535
1536   /* colors */
1537   "X c #000000",
1538   ". c #ffffff",
1539   "  c None",
1540
1541   /* pixels */
1542   " X              ",
1543   "X.X             ",
1544   " X              ",
1545   "                ",
1546   "                ",
1547   "                ",
1548   "                ",
1549   "                ",
1550   "                ",
1551   "                ",
1552   "                ",
1553   "                ",
1554   "                ",
1555   "                ",
1556   "                ",
1557   "                ",
1558
1559   /* hot spot */
1560   "1,1"
1561 };
1562 static const char **cursor_image_playfield = cursor_image_dot;
1563 #else
1564 /* some people complained about a "white dot" on the screen and thought it
1565    was a graphical error... OK, let's just remove the whole pointer :-) */
1566 static const char **cursor_image_playfield = cursor_image_none;
1567 #endif
1568
1569 static const int cursor_bit_order = BIT_ORDER_MSB;
1570
1571 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1572 {
1573   struct MouseCursorInfo *cursor;
1574   boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1575   int header_lines = 4;
1576   int x, y, i;
1577
1578   cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1579
1580   sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1581
1582   i = -1;
1583   for (y = 0; y < cursor->width; y++)
1584   {
1585     for (x = 0; x < cursor->height; x++)
1586     {
1587       int bit_nr = x % 8;
1588       int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1589
1590       if (bit_nr == 0)
1591       {
1592         i++;
1593         cursor->data[i] = cursor->mask[i] = 0;
1594       }
1595
1596       switch (image[header_lines + y][x])
1597       {
1598         case 'X':
1599           cursor->data[i] |= bit_mask;
1600           cursor->mask[i] |= bit_mask;
1601           break;
1602
1603         case '.':
1604           cursor->mask[i] |= bit_mask;
1605           break;
1606
1607         case ' ':
1608           break;
1609       }
1610     }
1611   }
1612
1613   sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1614
1615   return cursor;
1616 }
1617
1618 void SetMouseCursor(int mode)
1619 {
1620   static struct MouseCursorInfo *cursor_none = NULL;
1621   static struct MouseCursorInfo *cursor_playfield = NULL;
1622   struct MouseCursorInfo *cursor_new;
1623
1624   if (cursor_none == NULL)
1625     cursor_none = get_cursor_from_image(cursor_image_none);
1626
1627   if (cursor_playfield == NULL)
1628     cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1629
1630   cursor_new = (mode == CURSOR_DEFAULT   ? NULL :
1631                 mode == CURSOR_NONE      ? cursor_none :
1632                 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1633
1634   SDLSetMouseCursor(cursor_new);
1635 }
1636
1637
1638 /* ========================================================================= */
1639 /* audio functions                                                           */
1640 /* ========================================================================= */
1641
1642 void OpenAudio(void)
1643 {
1644   /* always start with reliable default values */
1645   audio.sound_available = FALSE;
1646   audio.music_available = FALSE;
1647   audio.loops_available = FALSE;
1648
1649   audio.sound_enabled = FALSE;
1650   audio.sound_deactivated = FALSE;
1651
1652   audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1653   audio.mixer_pid = 0;
1654   audio.device_name = NULL;
1655   audio.device_fd = -1;
1656
1657   audio.num_channels = 0;
1658   audio.music_channel = 0;
1659   audio.first_sound_channel = 0;
1660
1661 #if defined(TARGET_SDL)
1662   SDLOpenAudio();
1663 #endif
1664 }
1665
1666 void CloseAudio(void)
1667 {
1668 #if defined(TARGET_SDL)
1669   SDLCloseAudio();
1670 #endif
1671
1672   audio.sound_enabled = FALSE;
1673 }
1674
1675 void SetAudioMode(boolean enabled)
1676 {
1677   if (!audio.sound_available)
1678     return;
1679
1680   audio.sound_enabled = enabled;
1681 }
1682
1683
1684 /* ========================================================================= */
1685 /* event functions                                                           */
1686 /* ========================================================================= */
1687
1688 void InitEventFilter(EventFilter filter_function)
1689 {
1690   /* set event filter to filter out certain events */
1691 #if defined(TARGET_SDL)
1692 #if defined(TARGET_SDL2)
1693   SDL_SetEventFilter(filter_function, NULL);
1694 #else
1695   SDL_SetEventFilter(filter_function);
1696 #endif
1697 #endif
1698 }
1699
1700 boolean PendingEvent(void)
1701 {
1702 #if defined(TARGET_SDL)
1703   return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1704 #else
1705   return (XPending(display) ? TRUE : FALSE);
1706 #endif
1707 }
1708
1709 void NextEvent(Event *event)
1710 {
1711 #if defined(TARGET_SDL)
1712   SDLNextEvent(event);
1713 #else
1714   XNextEvent(display, event);
1715 #endif
1716 }
1717
1718 void PeekEvent(Event *event)
1719 {
1720 #if defined(TARGET_SDL)
1721 #if defined(TARGET_SDL2)
1722   SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1723 #else
1724   SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1725 #endif
1726 #else
1727   XPeekEvent(display, event);
1728 #endif
1729 }
1730
1731 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1732 {
1733 #if defined(TARGET_SDL)
1734 #if defined(TARGET_SDL2)
1735   /* key up/down events in SDL2 do not return text characters anymore */
1736   return event->keysym.sym;
1737 #else
1738
1739 #if 0
1740   printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1741          (int)event->keysym.unicode,
1742          (int)event->keysym.sym,
1743          (int)SDL_GetModState());
1744 #endif
1745
1746   if (with_modifiers &&
1747       event->keysym.unicode > 0x0000 &&
1748       event->keysym.unicode < 0x2000)
1749     return event->keysym.unicode;
1750   else
1751     return event->keysym.sym;
1752
1753 #endif
1754 #else
1755
1756 #if 0
1757   printf("with modifiers == '0x%04x', without modifiers == '0x%04x'\n",
1758          (int)XLookupKeysym(event, event->state),
1759          (int)XLookupKeysym(event, 0));
1760 #endif
1761
1762   if (with_modifiers)
1763     return XLookupKeysym(event, event->state);
1764   else
1765     return XLookupKeysym(event, 0);
1766 #endif
1767 }
1768
1769 KeyMod HandleKeyModState(Key key, int key_status)
1770 {
1771   static KeyMod current_modifiers = KMOD_None;
1772
1773   if (key != KSYM_UNDEFINED)    /* new key => check for modifier key change */
1774   {
1775     KeyMod new_modifier = KMOD_None;
1776
1777     switch(key)
1778     {
1779       case KSYM_Shift_L:
1780         new_modifier = KMOD_Shift_L;
1781         break;
1782       case KSYM_Shift_R:
1783         new_modifier = KMOD_Shift_R;
1784         break;
1785       case KSYM_Control_L:
1786         new_modifier = KMOD_Control_L;
1787         break;
1788       case KSYM_Control_R:
1789         new_modifier = KMOD_Control_R;
1790         break;
1791       case KSYM_Meta_L:
1792         new_modifier = KMOD_Meta_L;
1793         break;
1794       case KSYM_Meta_R:
1795         new_modifier = KMOD_Meta_R;
1796         break;
1797       case KSYM_Alt_L:
1798         new_modifier = KMOD_Alt_L;
1799         break;
1800       case KSYM_Alt_R:
1801         new_modifier = KMOD_Alt_R;
1802         break;
1803       default:
1804         break;
1805     }
1806
1807     if (key_status == KEY_PRESSED)
1808       current_modifiers |= new_modifier;
1809     else
1810       current_modifiers &= ~new_modifier;
1811   }
1812
1813   return current_modifiers;
1814 }
1815
1816 KeyMod GetKeyModState()
1817 {
1818 #if defined(TARGET_SDL)
1819   return (KeyMod)SDL_GetModState();
1820 #else
1821   return HandleKeyModState(KSYM_UNDEFINED, 0);
1822 #endif
1823 }
1824
1825 KeyMod GetKeyModStateFromEvents()
1826 {
1827   /* always use key modifier state as tracked from key events (this is needed
1828      if the modifier key event was injected into the event queue, but the key
1829      was not really pressed on keyboard -- SDL_GetModState() seems to directly
1830      query the keys as held pressed on the keyboard) -- this case is currently
1831      only used to filter out clipboard insert events from "True X-Mouse" tool */
1832
1833   return HandleKeyModState(KSYM_UNDEFINED, 0);
1834 }
1835
1836 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1837 {
1838   if (event->type != EVENT_CLIENTMESSAGE)
1839     return FALSE;
1840
1841 #if defined(TARGET_SDL)
1842   return TRUE;          /* the only possible message here is SDL_QUIT */
1843 #endif
1844
1845   return FALSE;
1846 }
1847
1848
1849 /* ========================================================================= */
1850 /* joystick functions                                                        */
1851 /* ========================================================================= */
1852
1853 void InitJoysticks()
1854 {
1855   int i;
1856
1857 #if defined(NO_JOYSTICK)
1858   return;       /* joysticks generally deactivated by compile-time directive */
1859 #endif
1860
1861   /* always start with reliable default values */
1862   joystick.status = JOYSTICK_NOT_AVAILABLE;
1863   for (i = 0; i < MAX_PLAYERS; i++)
1864     joystick.fd[i] = -1;                /* joystick device closed */
1865
1866 #if defined(TARGET_SDL)
1867   SDLInitJoysticks();
1868 #endif
1869
1870 #if 0
1871   for (i = 0; i < MAX_PLAYERS; i++)
1872     printf("::: Joystick for player %d: %d\n", i, joystick.fd[i]);
1873 #endif
1874 }
1875
1876 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1877 {
1878 #if defined(TARGET_SDL)
1879   return SDLReadJoystick(nr, x, y, b1, b2);
1880 #endif
1881 }