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