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