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