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