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