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