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