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