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