major cleanup of preprocessor hell
[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 CreateScaledBitmaps(Bitmap *old_bitmap, int zoom_factor,
900                                 int tile_size, boolean create_small_bitmaps)
901 {
902   Bitmap swap_bitmap;
903   Bitmap *new_bitmap;
904   Bitmap *tmp_bitmap_final = NULL;
905   Bitmap *tmp_bitmap_0 = NULL;
906   Bitmap *tmp_bitmap_1 = NULL;
907   Bitmap *tmp_bitmap_2 = NULL;
908   Bitmap *tmp_bitmap_4 = NULL;
909   Bitmap *tmp_bitmap_8 = NULL;
910   Bitmap *tmp_bitmap_16 = NULL;
911   Bitmap *tmp_bitmap_32 = NULL;
912   int width_final, height_final;
913   int width_0, height_0;
914   int width_1, height_1;
915   int width_2, height_2;
916   int width_4, height_4;
917   int width_8, height_8;
918   int width_16, height_16;
919   int width_32, height_32;
920   int old_width, old_height;
921   int new_width, new_height;
922
923   print_timestamp_init("CreateScaledBitmaps");
924
925   old_width  = old_bitmap->width;
926   old_height = old_bitmap->height;
927
928   /* calculate new image dimensions for final image size */
929   width_final  = old_width  * zoom_factor;
930   height_final = old_height * zoom_factor;
931
932   /* get image with final size (this might require scaling up) */
933   /* ("final" size may result in non-standard tile size image) */
934   if (zoom_factor != 1)
935     tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
936   else
937     tmp_bitmap_final = old_bitmap;
938
939   UPDATE_BUSY_STATE();
940
941   width_0  = width_1  = width_final;
942   height_0 = height_1 = height_final;
943
944   tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
945
946   if (create_small_bitmaps)
947   {
948     /* check if we have a non-gameplay tile size image */
949     if (tile_size != gfx.game_tile_size)
950     {
951       /* get image with gameplay tile size */
952       width_0  = width_final  * gfx.game_tile_size / tile_size;
953       height_0 = height_final * gfx.game_tile_size / tile_size;
954
955       if (width_0 == old_width)
956         tmp_bitmap_0 = old_bitmap;
957       else if (width_0 == width_final)
958         tmp_bitmap_0 = tmp_bitmap_final;
959       else
960         tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
961
962       UPDATE_BUSY_STATE();
963     }
964
965     /* check if we have a non-standard tile size image */
966     if (tile_size != gfx.standard_tile_size)
967     {
968       /* get image with standard tile size */
969       width_1  = width_final  * gfx.standard_tile_size / tile_size;
970       height_1 = height_final * gfx.standard_tile_size / tile_size;
971
972       if (width_1 == old_width)
973         tmp_bitmap_1 = old_bitmap;
974       else if (width_1 == width_final)
975         tmp_bitmap_1 = tmp_bitmap_final;
976       else if (width_1 == width_0)
977         tmp_bitmap_1 = tmp_bitmap_0;
978       else
979         tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
980
981       UPDATE_BUSY_STATE();
982     }
983   }
984
985   if (create_small_bitmaps)
986   {
987     /* calculate new image dimensions for small images */
988     width_2  = width_1  / 2;
989     height_2 = height_1 / 2;
990     width_4  = width_1  / 4;
991     height_4 = height_1 / 4;
992     width_8  = width_1  / 8;
993     height_8 = height_1 / 8;
994     width_16  = width_1  / 16;
995     height_16 = height_1 / 16;
996     width_32  = width_1  / 32;
997     height_32 = height_1 / 32;
998
999     /* get image with 1/2 of normal size (for use in the level editor) */
1000     if (width_2 == old_width)
1001       tmp_bitmap_2 = old_bitmap;
1002     else
1003       tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1004
1005     UPDATE_BUSY_STATE();
1006
1007     /* get image with 1/4 of normal size (for use in the level editor) */
1008     if (width_4 == old_width)
1009       tmp_bitmap_4 = old_bitmap;
1010     else
1011       tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1012
1013     UPDATE_BUSY_STATE();
1014
1015     /* get image with 1/8 of normal size (for use on the preview screen) */
1016     if (width_8 == old_width)
1017       tmp_bitmap_8 = old_bitmap;
1018     else
1019       tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1020
1021     UPDATE_BUSY_STATE();
1022
1023     /* get image with 1/16 of normal size (for use on the preview screen) */
1024     if (width_16 == old_width)
1025       tmp_bitmap_16 = old_bitmap;
1026     else
1027       tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1028
1029     UPDATE_BUSY_STATE();
1030
1031     /* get image with 1/32 of normal size (for use on the preview screen) */
1032     if (width_32 == old_width)
1033       tmp_bitmap_32 = old_bitmap;
1034     else
1035       tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1036
1037     UPDATE_BUSY_STATE();
1038   }
1039
1040   if (create_small_bitmaps)
1041   {
1042     new_width  = width_1;
1043     new_height = height_1 + (height_1 + 1) / 2;     /* prevent odd height */
1044
1045     if (width_0 != width_1)
1046     {
1047       new_width += width_0;
1048       new_height = MAX(new_height, height_0);
1049     }
1050
1051     new_bitmap = CreateBitmap(new_width, new_height, DEFAULT_DEPTH);
1052
1053     if (width_0 != width_1)
1054       BlitBitmap(tmp_bitmap_0, new_bitmap, 0, 0, width_0, height_0, width_1, 0);
1055
1056     BlitBitmap(tmp_bitmap_1, new_bitmap, 0, 0, width_1, height_1, 0, 0);
1057     BlitBitmap(tmp_bitmap_2, new_bitmap, 0, 0, width_1 / 2, height_1 / 2,
1058                0, height_1);
1059     BlitBitmap(tmp_bitmap_4, new_bitmap, 0, 0, width_1 / 4, height_1 / 4,
1060                width_1 / 2, height_1);
1061     BlitBitmap(tmp_bitmap_8, new_bitmap, 0, 0, width_1 / 8, height_1 / 8,
1062                3 * width_1 / 4, height_1);
1063     BlitBitmap(tmp_bitmap_16, new_bitmap, 0, 0, width_1 / 16, height_1 / 16,
1064                7 * width_1 / 8, height_1);
1065     BlitBitmap(tmp_bitmap_32, new_bitmap, 0, 0, width_1 / 32, height_1 / 32,
1066                15 * width_1 / 16, height_1);
1067
1068     UPDATE_BUSY_STATE();
1069   }
1070   else
1071   {
1072     new_width  = width_1;
1073     new_height = height_1;
1074
1075     new_bitmap = tmp_bitmap_1;  /* directly use tmp_bitmap_1 as new bitmap */
1076   }
1077
1078   if (create_small_bitmaps)
1079   {
1080     /* if no small bitmaps created, tmp_bitmap_1 is used as new bitmap now */
1081
1082     if (tmp_bitmap_final != old_bitmap)
1083       FreeBitmap(tmp_bitmap_final);
1084
1085     if (tmp_bitmap_0 != old_bitmap &&
1086         tmp_bitmap_0 != tmp_bitmap_final)
1087       FreeBitmap(tmp_bitmap_0);
1088
1089     if (tmp_bitmap_1 != old_bitmap &&
1090         tmp_bitmap_1 != tmp_bitmap_final &&
1091         tmp_bitmap_1 != tmp_bitmap_0)
1092       FreeBitmap(tmp_bitmap_1);
1093
1094     if (tmp_bitmap_2 != old_bitmap)
1095       FreeBitmap(tmp_bitmap_2);
1096
1097     if (tmp_bitmap_4 != old_bitmap)
1098       FreeBitmap(tmp_bitmap_4);
1099
1100     if (tmp_bitmap_8 != old_bitmap)
1101       FreeBitmap(tmp_bitmap_8);
1102
1103     if (tmp_bitmap_16 != old_bitmap)
1104       FreeBitmap(tmp_bitmap_16);
1105
1106     if (tmp_bitmap_32 != old_bitmap)
1107       FreeBitmap(tmp_bitmap_32);
1108   }
1109
1110   /* replace image with extended image (containing 1/1, 1/2, 1/4, 1/8 size) */
1111   swap_bitmap.surface = old_bitmap->surface;
1112   old_bitmap->surface = new_bitmap->surface;
1113   new_bitmap->surface = swap_bitmap.surface;
1114
1115   old_bitmap->width  = new_bitmap->width;
1116   old_bitmap->height = new_bitmap->height;
1117
1118   /* this replaces all blit masks created when loading -- maybe optimize this */
1119   {
1120     SDL_Surface *old_surface = old_bitmap->surface;
1121
1122     if (old_bitmap->surface_masked)
1123       SDL_FreeSurface(old_bitmap->surface_masked);
1124
1125     SDL_SetColorKey(old_surface, SET_TRANSPARENT_PIXEL,
1126                     SDL_MapRGB(old_surface->format, 0x00, 0x00, 0x00));
1127
1128     if ((old_bitmap->surface_masked = SDLGetNativeSurface(old_surface)) == NULL)
1129       Error(ERR_EXIT, "SDL_DisplayFormat() failed");
1130
1131     SDL_SetColorKey(old_surface, UNSET_TRANSPARENT_PIXEL, 0);
1132   }
1133
1134   UPDATE_BUSY_STATE();
1135
1136   FreeBitmap(new_bitmap);       /* this actually frees the _old_ bitmap now */
1137
1138   print_timestamp_done("CreateScaledBitmaps");
1139 }
1140
1141 void CreateBitmapWithSmallBitmaps(Bitmap *old_bitmap, int zoom_factor,
1142                                   int tile_size)
1143 {
1144   CreateScaledBitmaps(old_bitmap, zoom_factor, tile_size, TRUE);
1145 }
1146
1147 void ScaleBitmap(Bitmap *old_bitmap, int zoom_factor)
1148 {
1149   CreateScaledBitmaps(old_bitmap, zoom_factor, 0, FALSE);
1150 }
1151
1152
1153 /* ------------------------------------------------------------------------- */
1154 /* mouse pointer functions                                                   */
1155 /* ------------------------------------------------------------------------- */
1156
1157 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER            0
1158
1159 /* XPM image definitions */
1160 static const char *cursor_image_none[] =
1161 {
1162   /* width height num_colors chars_per_pixel */
1163   "    16    16        3            1",
1164
1165   /* colors */
1166   "X c #000000",
1167   ". c #ffffff",
1168   "  c None",
1169
1170   /* pixels */
1171   "                ",
1172   "                ",
1173   "                ",
1174   "                ",
1175   "                ",
1176   "                ",
1177   "                ",
1178   "                ",
1179   "                ",
1180   "                ",
1181   "                ",
1182   "                ",
1183   "                ",
1184   "                ",
1185   "                ",
1186   "                ",
1187
1188   /* hot spot */
1189   "0,0"
1190 };
1191
1192 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1193 static const char *cursor_image_dot[] =
1194 {
1195   /* width height num_colors chars_per_pixel */
1196   "    16    16        3            1",
1197
1198   /* colors */
1199   "X c #000000",
1200   ". c #ffffff",
1201   "  c None",
1202
1203   /* pixels */
1204   " X              ",
1205   "X.X             ",
1206   " X              ",
1207   "                ",
1208   "                ",
1209   "                ",
1210   "                ",
1211   "                ",
1212   "                ",
1213   "                ",
1214   "                ",
1215   "                ",
1216   "                ",
1217   "                ",
1218   "                ",
1219   "                ",
1220
1221   /* hot spot */
1222   "1,1"
1223 };
1224 static const char **cursor_image_playfield = cursor_image_dot;
1225 #else
1226 /* some people complained about a "white dot" on the screen and thought it
1227    was a graphical error... OK, let's just remove the whole pointer :-) */
1228 static const char **cursor_image_playfield = cursor_image_none;
1229 #endif
1230
1231 static const int cursor_bit_order = BIT_ORDER_MSB;
1232
1233 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1234 {
1235   struct MouseCursorInfo *cursor;
1236   boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1237   int header_lines = 4;
1238   int x, y, i;
1239
1240   cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1241
1242   sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1243
1244   i = -1;
1245   for (y = 0; y < cursor->width; y++)
1246   {
1247     for (x = 0; x < cursor->height; x++)
1248     {
1249       int bit_nr = x % 8;
1250       int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1251
1252       if (bit_nr == 0)
1253       {
1254         i++;
1255         cursor->data[i] = cursor->mask[i] = 0;
1256       }
1257
1258       switch (image[header_lines + y][x])
1259       {
1260         case 'X':
1261           cursor->data[i] |= bit_mask;
1262           cursor->mask[i] |= bit_mask;
1263           break;
1264
1265         case '.':
1266           cursor->mask[i] |= bit_mask;
1267           break;
1268
1269         case ' ':
1270           break;
1271       }
1272     }
1273   }
1274
1275   sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1276
1277   return cursor;
1278 }
1279
1280 void SetMouseCursor(int mode)
1281 {
1282   static struct MouseCursorInfo *cursor_none = NULL;
1283   static struct MouseCursorInfo *cursor_playfield = NULL;
1284   struct MouseCursorInfo *cursor_new;
1285
1286   if (cursor_none == NULL)
1287     cursor_none = get_cursor_from_image(cursor_image_none);
1288
1289   if (cursor_playfield == NULL)
1290     cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1291
1292   cursor_new = (mode == CURSOR_DEFAULT   ? NULL :
1293                 mode == CURSOR_NONE      ? cursor_none :
1294                 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1295
1296   SDLSetMouseCursor(cursor_new);
1297 }
1298
1299
1300 /* ========================================================================= */
1301 /* audio functions                                                           */
1302 /* ========================================================================= */
1303
1304 void OpenAudio(void)
1305 {
1306   /* always start with reliable default values */
1307   audio.sound_available = FALSE;
1308   audio.music_available = FALSE;
1309   audio.loops_available = FALSE;
1310
1311   audio.sound_enabled = FALSE;
1312   audio.sound_deactivated = FALSE;
1313
1314   audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1315   audio.mixer_pid = 0;
1316   audio.device_name = NULL;
1317   audio.device_fd = -1;
1318
1319   audio.num_channels = 0;
1320   audio.music_channel = 0;
1321   audio.first_sound_channel = 0;
1322
1323   SDLOpenAudio();
1324 }
1325
1326 void CloseAudio(void)
1327 {
1328   SDLCloseAudio();
1329
1330   audio.sound_enabled = FALSE;
1331 }
1332
1333 void SetAudioMode(boolean enabled)
1334 {
1335   if (!audio.sound_available)
1336     return;
1337
1338   audio.sound_enabled = enabled;
1339 }
1340
1341
1342 /* ========================================================================= */
1343 /* event functions                                                           */
1344 /* ========================================================================= */
1345
1346 void InitEventFilter(EventFilter filter_function)
1347 {
1348   /* set event filter to filter out certain events */
1349 #if defined(TARGET_SDL2)
1350   SDL_SetEventFilter(filter_function, NULL);
1351 #else
1352   SDL_SetEventFilter(filter_function);
1353 #endif
1354 }
1355
1356 boolean PendingEvent(void)
1357 {
1358   return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1359 }
1360
1361 void NextEvent(Event *event)
1362 {
1363   SDLNextEvent(event);
1364 }
1365
1366 void PeekEvent(Event *event)
1367 {
1368 #if defined(TARGET_SDL2)
1369   SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1370 #else
1371   SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1372 #endif
1373 }
1374
1375 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1376 {
1377 #if defined(TARGET_SDL2)
1378   /* key up/down events in SDL2 do not return text characters anymore */
1379   return event->keysym.sym;
1380 #else
1381
1382 #if ENABLE_UNUSED_CODE
1383   printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1384          (int)event->keysym.unicode,
1385          (int)event->keysym.sym,
1386          (int)SDL_GetModState());
1387 #endif
1388
1389   if (with_modifiers &&
1390       event->keysym.unicode > 0x0000 &&
1391       event->keysym.unicode < 0x2000)
1392     return event->keysym.unicode;
1393   else
1394     return event->keysym.sym;
1395
1396 #endif
1397 }
1398
1399 KeyMod HandleKeyModState(Key key, int key_status)
1400 {
1401   static KeyMod current_modifiers = KMOD_None;
1402
1403   if (key != KSYM_UNDEFINED)    /* new key => check for modifier key change */
1404   {
1405     KeyMod new_modifier = KMOD_None;
1406
1407     switch(key)
1408     {
1409       case KSYM_Shift_L:
1410         new_modifier = KMOD_Shift_L;
1411         break;
1412       case KSYM_Shift_R:
1413         new_modifier = KMOD_Shift_R;
1414         break;
1415       case KSYM_Control_L:
1416         new_modifier = KMOD_Control_L;
1417         break;
1418       case KSYM_Control_R:
1419         new_modifier = KMOD_Control_R;
1420         break;
1421       case KSYM_Meta_L:
1422         new_modifier = KMOD_Meta_L;
1423         break;
1424       case KSYM_Meta_R:
1425         new_modifier = KMOD_Meta_R;
1426         break;
1427       case KSYM_Alt_L:
1428         new_modifier = KMOD_Alt_L;
1429         break;
1430       case KSYM_Alt_R:
1431         new_modifier = KMOD_Alt_R;
1432         break;
1433       default:
1434         break;
1435     }
1436
1437     if (key_status == KEY_PRESSED)
1438       current_modifiers |= new_modifier;
1439     else
1440       current_modifiers &= ~new_modifier;
1441   }
1442
1443   return current_modifiers;
1444 }
1445
1446 KeyMod GetKeyModState()
1447 {
1448   return (KeyMod)SDL_GetModState();
1449 }
1450
1451 KeyMod GetKeyModStateFromEvents()
1452 {
1453   /* always use key modifier state as tracked from key events (this is needed
1454      if the modifier key event was injected into the event queue, but the key
1455      was not really pressed on keyboard -- SDL_GetModState() seems to directly
1456      query the keys as held pressed on the keyboard) -- this case is currently
1457      only used to filter out clipboard insert events from "True X-Mouse" tool */
1458
1459   return HandleKeyModState(KSYM_UNDEFINED, 0);
1460 }
1461
1462 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1463 {
1464   if (event->type != EVENT_CLIENTMESSAGE)
1465     return FALSE;
1466
1467   return TRUE;          /* the only possible message here is SDL_QUIT */
1468 }
1469
1470
1471 /* ========================================================================= */
1472 /* joystick functions                                                        */
1473 /* ========================================================================= */
1474
1475 void InitJoysticks()
1476 {
1477   int i;
1478
1479 #if defined(NO_JOYSTICK)
1480   return;       /* joysticks generally deactivated by compile-time directive */
1481 #endif
1482
1483   /* always start with reliable default values */
1484   joystick.status = JOYSTICK_NOT_AVAILABLE;
1485   for (i = 0; i < MAX_PLAYERS; i++)
1486     joystick.fd[i] = -1;                /* joystick device closed */
1487
1488   SDLInitJoysticks();
1489 }
1490
1491 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1492 {
1493   return SDLReadJoystick(nr, x, y, b1, b2);
1494 }