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