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