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