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