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