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