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