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