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