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