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