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