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