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