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