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