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