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