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