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