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