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