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