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