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