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