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