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