changed signal handling to not catch Ctrl-C anymore
[rocksndiamonds.git] / src / libgame / system.c
1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // system.c
10 // ============================================================================
11
12 #include <string.h>
13 #include <signal.h>
14
15 #include "platform.h"
16
17 #include "system.h"
18 #include "image.h"
19 #include "sound.h"
20 #include "setup.h"
21 #include "joystick.h"
22 #include "misc.h"
23
24 #define ENABLE_UNUSED_CODE      0       /* currently unused functions */
25
26
27 /* ========================================================================= */
28 /* exported variables                                                        */
29 /* ========================================================================= */
30
31 struct ProgramInfo      program;
32 struct OptionInfo       options;
33 struct VideoSystemInfo  video;
34 struct AudioSystemInfo  audio;
35 struct GfxInfo          gfx;
36 struct ArtworkInfo      artwork;
37 struct JoystickInfo     joystick;
38 struct SetupInfo        setup;
39
40 LevelDirTree           *leveldir_first_all = NULL;
41 LevelDirTree           *leveldir_first = NULL;
42 LevelDirTree           *leveldir_current = NULL;
43 int                     level_nr;
44
45 struct LevelStats       level_stats[MAX_LEVELS];
46
47 DrawWindow             *window = NULL;
48 DrawBuffer             *backbuffer = NULL;
49 DrawBuffer             *drawto = NULL;
50
51 int                     button_status = MB_NOT_PRESSED;
52 boolean                 motion_status = FALSE;
53 #if defined(TARGET_SDL2)
54 boolean                 keyrepeat_status = TRUE;
55 #endif
56
57 int                     redraw_mask = REDRAW_NONE;
58
59 int                     FrameCounter = 0;
60
61
62 /* ========================================================================= */
63 /* init/close functions                                                      */
64 /* ========================================================================= */
65
66 void InitProgramInfo(char *argv0, char *config_filename, char *userdata_subdir,
67                      char *program_title, char *icon_title,
68                      char *icon_filename, char *cookie_prefix,
69                      int program_version)
70 {
71   program.command_basepath = getBasePath(argv0);
72   program.command_basename = getBaseName(argv0);
73
74   program.config_filename = config_filename;
75
76   program.userdata_subdir = userdata_subdir;
77   program.userdata_path = getUserGameDataDir();
78
79   program.program_title = program_title;
80   program.window_title = "(undefined)";
81   program.icon_title = icon_title;
82
83   program.icon_filename = icon_filename;
84
85   program.cookie_prefix = cookie_prefix;
86
87   program.version_major = VERSION_MAJOR(program_version);
88   program.version_minor = VERSION_MINOR(program_version);
89   program.version_patch = VERSION_PATCH(program_version);
90   program.version_build = VERSION_BUILD(program_version);
91   program.version_ident = program_version;
92
93   program.log_filename[LOG_OUT_ID] = getLogFilename(LOG_OUT_BASENAME);
94   program.log_filename[LOG_ERR_ID] = getLogFilename(LOG_ERR_BASENAME);
95   program.log_file[LOG_OUT_ID] = program.log_file_default[LOG_OUT_ID] = stdout;
96   program.log_file[LOG_ERR_ID] = program.log_file_default[LOG_ERR_ID] = stderr;
97 }
98
99 void SetWindowTitle()
100 {
101   program.window_title = program.window_title_function();
102
103   SDLSetWindowTitle();
104 }
105
106 void InitWindowTitleFunction(char *(*window_title_function)(void))
107 {
108   program.window_title_function = window_title_function;
109 }
110
111 void InitExitMessageFunction(void (*exit_message_function)(char *, va_list))
112 {
113   program.exit_message_function = exit_message_function;
114 }
115
116 void InitExitFunction(void (*exit_function)(int))
117 {
118   program.exit_function = exit_function;
119
120   /* set signal handlers to custom exit function */
121   // signal(SIGINT, exit_function);
122   signal(SIGTERM, exit_function);
123
124   /* set exit function to automatically cleanup SDL stuff after exit() */
125   atexit(SDL_Quit);
126 }
127
128 void InitPlatformDependentStuff(void)
129 {
130   // this is initialized in GetOptions(), but may already be used before
131   options.verbose = TRUE;
132
133   OpenLogFiles();
134
135 #if defined(TARGET_SDL2)
136   int sdl_init_flags = SDL_INIT_EVENTS      | SDL_INIT_NOPARACHUTE;
137 #else
138   int sdl_init_flags = SDL_INIT_EVENTTHREAD | SDL_INIT_NOPARACHUTE;
139 #endif
140
141   if (SDL_Init(sdl_init_flags) < 0)
142     Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
143
144   SDLNet_Init();
145 }
146
147 void ClosePlatformDependentStuff(void)
148 {
149   CloseLogFiles();
150 }
151
152 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
153                       int real_sx, int real_sy,
154                       int full_sxsize, int full_sysize,
155                       Bitmap *field_save_buffer)
156 {
157   gfx.sx = sx;
158   gfx.sy = sy;
159   gfx.sxsize = sxsize;
160   gfx.sysize = sysize;
161   gfx.real_sx = real_sx;
162   gfx.real_sy = real_sy;
163   gfx.full_sxsize = full_sxsize;
164   gfx.full_sysize = full_sysize;
165
166   gfx.field_save_buffer = field_save_buffer;
167
168   SetDrawDeactivationMask(REDRAW_NONE);         /* do not deactivate drawing */
169   SetDrawBackgroundMask(REDRAW_NONE);           /* deactivate masked drawing */
170 }
171
172 void InitGfxTileSizeInfo(int game_tile_size, int standard_tile_size)
173 {
174   gfx.game_tile_size = game_tile_size;
175   gfx.standard_tile_size = standard_tile_size;
176 }
177
178 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
179 {
180   gfx.dx = dx;
181   gfx.dy = dy;
182   gfx.dxsize = dxsize;
183   gfx.dysize = dysize;
184 }
185
186 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
187 {
188   gfx.vx = vx;
189   gfx.vy = vy;
190   gfx.vxsize = vxsize;
191   gfx.vysize = vysize;
192 }
193
194 void InitGfxDoor3Info(int ex, int ey, int exsize, int eysize)
195 {
196   gfx.ex = ex;
197   gfx.ey = ey;
198   gfx.exsize = exsize;
199   gfx.eysize = eysize;
200 }
201
202 void InitGfxWindowInfo(int win_xsize, int win_ysize)
203 {
204   gfx.win_xsize = win_xsize;
205   gfx.win_ysize = win_ysize;
206
207   gfx.background_bitmap_mask = REDRAW_NONE;
208
209   ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize, DEFAULT_DEPTH);
210 }
211
212 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
213 {
214   /* currently only used by MSDOS code to alloc VRAM buffer, if available */
215   /* 2009-03-24: also (temporarily?) used for overlapping blit workaround */
216   gfx.scrollbuffer_width = scrollbuffer_width;
217   gfx.scrollbuffer_height = scrollbuffer_height;
218 }
219
220 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
221 {
222   gfx.clipping_enabled = enabled;
223   gfx.clip_x = x;
224   gfx.clip_y = y;
225   gfx.clip_width = width;
226   gfx.clip_height = height;
227 }
228
229 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
230 {
231   gfx.draw_busy_anim_function = draw_busy_anim_function;
232 }
233
234 void InitGfxCustomArtworkInfo()
235 {
236   gfx.override_level_graphics = FALSE;
237   gfx.override_level_sounds = FALSE;
238   gfx.override_level_music = FALSE;
239
240   gfx.draw_init_text = TRUE;
241 }
242
243 void InitGfxOtherSettings()
244 {
245   gfx.cursor_mode = CURSOR_DEFAULT;
246 }
247
248 void SetDrawDeactivationMask(int draw_deactivation_mask)
249 {
250   gfx.draw_deactivation_mask = draw_deactivation_mask;
251 }
252
253 void SetDrawBackgroundMask(int draw_background_mask)
254 {
255   gfx.draw_background_mask = draw_background_mask;
256 }
257
258 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
259 {
260   if (background_bitmap_tile != NULL)
261     gfx.background_bitmap_mask |= mask;
262   else
263     gfx.background_bitmap_mask &= ~mask;
264
265   if (background_bitmap_tile == NULL)   /* empty background requested */
266     return;
267
268   if (mask == REDRAW_ALL)
269     BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
270                     0, 0, video.width, video.height);
271   else if (mask == REDRAW_FIELD)
272     BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
273                     gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
274   else if (mask == REDRAW_DOOR_1)
275     BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
276                     gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
277 }
278
279 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
280 {
281   /* remove every mask before setting mask for window */
282   /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
283   SetBackgroundBitmap(NULL, 0xffff);            /* !!! FIX THIS !!! */
284   SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
285 }
286
287 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
288 {
289   /* remove window area mask before setting mask for main area */
290   /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
291   SetBackgroundBitmap(NULL, REDRAW_ALL);        /* !!! FIX THIS !!! */
292   SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
293 }
294
295 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
296 {
297   /* remove window area mask before setting mask for door area */
298   /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
299   SetBackgroundBitmap(NULL, REDRAW_ALL);        /* !!! FIX THIS !!! */
300   SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
301 }
302
303
304 /* ========================================================================= */
305 /* video functions                                                           */
306 /* ========================================================================= */
307
308 inline static int GetRealDepth(int depth)
309 {
310   return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
311 }
312
313 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
314                                     int width, int height, Pixel color)
315 {
316   SDLFillRectangle(bitmap, x, y, width, height, color);
317
318   if (bitmap == backbuffer)
319     SetRedrawMaskFromArea(x, y, width, height);
320 }
321
322 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
323                                int src_x, int src_y, int width, int height,
324                                int dst_x, int dst_y, int mask_mode)
325 {
326   SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
327               dst_x, dst_y, mask_mode);
328
329   if (dst_bitmap == backbuffer)
330     SetRedrawMaskFromArea(dst_x, dst_y, width, height);
331 }
332
333 void LimitScreenUpdates(boolean enable)
334 {
335   SDLLimitScreenUpdates(enable);
336 }
337
338 void InitVideoDisplay(void)
339 {
340   SDLInitVideoDisplay();
341 }
342
343 void CloseVideoDisplay(void)
344 {
345   KeyboardAutoRepeatOn();
346
347   SDL_QuitSubSystem(SDL_INIT_VIDEO);
348 }
349
350 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
351 {
352   video.width = width;
353   video.height = height;
354   video.depth = GetRealDepth(depth);
355
356   video.fullscreen_available = FULLSCREEN_STATUS;
357   video.fullscreen_enabled = FALSE;
358
359   video.window_scaling_available = WINDOW_SCALING_STATUS;
360
361   SDLInitVideoBuffer(&backbuffer, &window, fullscreen);
362
363   video.initialized = TRUE;
364
365   drawto = backbuffer;
366 }
367
368 inline static void FreeBitmapPointers(Bitmap *bitmap)
369 {
370   if (bitmap == NULL)
371     return;
372
373   SDLFreeBitmapPointers(bitmap);
374
375   checked_free(bitmap->source_filename);
376   bitmap->source_filename = NULL;
377 }
378
379 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
380                                           Bitmap *dst_bitmap)
381 {
382   if (src_bitmap == NULL || dst_bitmap == NULL)
383     return;
384
385   FreeBitmapPointers(dst_bitmap);
386
387   *dst_bitmap = *src_bitmap;
388 }
389
390 void FreeBitmap(Bitmap *bitmap)
391 {
392   if (bitmap == NULL)
393     return;
394
395   FreeBitmapPointers(bitmap);
396
397   free(bitmap);
398 }
399
400 Bitmap *CreateBitmapStruct(void)
401 {
402   return checked_calloc(sizeof(struct SDLSurfaceInfo));
403 }
404
405 Bitmap *CreateBitmap(int width, int height, int depth)
406 {
407   Bitmap *new_bitmap = CreateBitmapStruct();
408   int real_width  = MAX(1, width);      /* prevent zero bitmap width */
409   int real_height = MAX(1, height);     /* prevent zero bitmap height */
410   int real_depth  = GetRealDepth(depth);
411
412   SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
413
414   new_bitmap->width  = real_width;
415   new_bitmap->height = real_height;
416
417   return new_bitmap;
418 }
419
420 void ReCreateBitmap(Bitmap **bitmap, int width, int height, int depth)
421 {
422   Bitmap *new_bitmap = CreateBitmap(width, height, depth);
423
424   if (*bitmap == NULL)
425   {
426     *bitmap = new_bitmap;
427   }
428   else
429   {
430     TransferBitmapPointers(new_bitmap, *bitmap);
431     free(new_bitmap);
432   }
433 }
434
435 void CloseWindow(DrawWindow *window)
436 {
437 }
438
439 void SetRedrawMaskFromArea(int x, int y, int width, int height)
440 {
441   int x1 = x;
442   int y1 = y;
443   int x2 = x + width - 1;
444   int y2 = y + height - 1;
445
446   if (width == 0 || height == 0)
447     return;
448
449   if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
450     redraw_mask |= REDRAW_FIELD;
451   else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
452     redraw_mask |= REDRAW_DOOR_1;
453   else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
454     redraw_mask |= REDRAW_DOOR_2;
455   else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
456     redraw_mask |= REDRAW_DOOR_3;
457   else
458     redraw_mask = REDRAW_ALL;
459 }
460
461 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
462                                        int draw_mask)
463 {
464   if (draw_mask == REDRAW_NONE)
465     return FALSE;
466
467   if (draw_mask & REDRAW_ALL)
468     return TRUE;
469
470   if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
471     return TRUE;
472
473   if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
474     return TRUE;
475
476   if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
477     return TRUE;
478
479   if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
480     return TRUE;
481
482   return FALSE;
483 }
484
485 boolean DrawingDeactivated(int x, int y, int width, int height)
486 {
487   return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
488 }
489
490 boolean DrawingOnBackground(int x, int y)
491 {
492   return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
493           CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
494 }
495
496 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
497                                   int *width, int *height, boolean is_dest)
498 {
499   int clip_x, clip_y, clip_width, clip_height;
500
501   if (gfx.clipping_enabled && is_dest)  /* only clip destination bitmap */
502   {
503     clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
504     clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
505     clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
506     clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
507   }
508   else
509   {
510     clip_x = 0;
511     clip_y = 0;
512     clip_width = bitmap->width;
513     clip_height = bitmap->height;
514   }
515
516   /* skip if rectangle completely outside bitmap */
517
518   if (*x + *width  <= clip_x ||
519       *y + *height <= clip_y ||
520       *x >= clip_x + clip_width ||
521       *y >= clip_y + clip_height)
522     return FALSE;
523
524   /* clip if rectangle overlaps bitmap */
525
526   if (*x < clip_x)
527   {
528     *width -= clip_x - *x;
529     *x = clip_x;
530   }
531   else if (*x + *width > clip_x + clip_width)
532   {
533     *width = clip_x + clip_width - *x;
534   }
535
536   if (*y < clip_y)
537   {
538     *height -= clip_y - *y;
539     *y = clip_y;
540   }
541   else if (*y + *height > clip_y + clip_height)
542   {
543     *height = clip_y + clip_height - *y;
544   }
545
546   return TRUE;
547 }
548
549 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
550                 int src_x, int src_y, int width, int height,
551                 int dst_x, int dst_y)
552 {
553   int dst_x_unclipped = dst_x;
554   int dst_y_unclipped = dst_y;
555
556   if (src_bitmap == NULL || dst_bitmap == NULL)
557     return;
558
559   if (DrawingDeactivated(dst_x, dst_y, width, height))
560     return;
561
562   if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
563       !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
564     return;
565
566   /* source x/y might need adjustment if destination x/y was clipped top/left */
567   src_x += dst_x - dst_x_unclipped;
568   src_y += dst_y - dst_y_unclipped;
569
570 #if defined(TARGET_SDL2)
571   /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
572   /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
573   /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
574      but is already fixed in SVN and should therefore finally be fixed with
575      the next official SDL release, which is probably version 1.2.14.) */
576   /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
577
578   if (src_bitmap == dst_bitmap)
579   {
580     /* needed when blitting directly to same bitmap -- should not be needed with
581        recent SDL libraries, but apparently does not work in 1.2.11 directly */
582
583     static Bitmap *tmp_bitmap = NULL;
584     static int tmp_bitmap_xsize = 0;
585     static int tmp_bitmap_ysize = 0;
586
587     /* start with largest static bitmaps for initial bitmap size ... */
588     if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
589     {
590       tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
591       tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
592     }
593
594     /* ... and allow for later re-adjustments due to custom artwork bitmaps */
595     if (src_bitmap->width > tmp_bitmap_xsize ||
596         src_bitmap->height > tmp_bitmap_ysize)
597     {
598       tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
599       tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
600
601       FreeBitmap(tmp_bitmap);
602
603       tmp_bitmap = NULL;
604     }
605
606     if (tmp_bitmap == NULL)
607       tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
608                                 DEFAULT_DEPTH);
609
610     sysCopyArea(src_bitmap, tmp_bitmap,
611                 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
612     sysCopyArea(tmp_bitmap, dst_bitmap,
613                 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
614
615     return;
616   }
617 #endif
618
619   sysCopyArea(src_bitmap, dst_bitmap,
620               src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
621 }
622
623 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
624                      int src_x, int src_y, int src_width, int src_height,
625                      int dst_x, int dst_y, int dst_width, int dst_height)
626 {
627   int src_xsize = (src_width  == 0 ? src_bitmap->width  : src_width);
628   int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
629   int dst_xsize = dst_width;
630   int dst_ysize = dst_height;
631   int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
632   int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
633   int x, y;
634
635   for (y = 0; y < src_ysteps; y++)
636   {
637     for (x = 0; x < src_xsteps; x++)
638     {
639       int draw_x = dst_x + x * src_xsize;
640       int draw_y = dst_y + y * src_ysize;
641       int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
642       int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
643
644       BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
645                  draw_x, draw_y);
646     }
647   }
648 }
649
650 void FadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
651                    int fade_mode, int fade_delay, int post_delay,
652                    void (*draw_border_function)(void))
653 {
654   /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
655   if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
656     return;
657
658   SDLFadeRectangle(bitmap_cross, x, y, width, height,
659                    fade_mode, fade_delay, post_delay, draw_border_function);
660 }
661
662 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
663                    Pixel color)
664 {
665   if (DrawingDeactivated(x, y, width, height))
666     return;
667
668   if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
669     return;
670
671   sysFillRectangle(bitmap, x, y, width, height, color);
672 }
673
674 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
675 {
676   FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
677 }
678
679 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
680                                 int width, int height)
681 {
682   if (DrawingOnBackground(x, y))
683     BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
684   else
685     ClearRectangle(bitmap, x, y, width, height);
686 }
687
688 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
689                       int src_x, int src_y, int width, int height,
690                       int dst_x, int dst_y)
691 {
692   if (DrawingDeactivated(dst_x, dst_y, width, height))
693     return;
694
695   sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
696               dst_x, dst_y, BLIT_MASKED);
697 }
698
699 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
700                             int src_x, int src_y, int width, int height,
701                             int dst_x, int dst_y)
702 {
703   if (DrawingOnBackground(dst_x, dst_y))
704   {
705     /* draw background */
706     BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
707                dst_x, dst_y);
708
709     /* draw foreground */
710     BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
711                      dst_x, dst_y);
712   }
713   else
714     BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
715                dst_x, dst_y);
716 }
717
718 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
719                          int to_x, int to_y)
720 {
721   SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
722 }
723
724 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
725                          int to_x, int to_y)
726 {
727   SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
728 }
729
730 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
731               int to_x, int to_y, Pixel pixel, int line_width)
732 {
733   int x, y;
734
735   for (x = 0; x < line_width; x++)
736   {
737     for (y = 0; y < line_width; y++)
738     {
739       int dx = x - line_width / 2;
740       int dy = y - line_width / 2;
741
742       if ((x == 0 && y == 0) ||
743           (x == 0 && y == line_width - 1) ||
744           (x == line_width - 1 && y == 0) ||
745           (x == line_width - 1 && y == line_width - 1))
746         continue;
747
748       SDLDrawLine(bitmap,
749                   from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
750     }
751   }
752 }
753
754 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
755 {
756   int line_width = 4;
757   int i;
758
759   for (i = 0; i < num_points - 1; i++)
760     DrawLine(bitmap, points[i].x, points[i].y,
761              points[i + 1].x, points[i + 1].y, pixel, line_width);
762
763   /*
764   SDLDrawLines(bitmap->surface, points, num_points, pixel);
765   */
766 }
767
768 Pixel GetPixel(Bitmap *bitmap, int x, int y)
769 {
770   if (x < 0 || x >= bitmap->width ||
771       y < 0 || y >= bitmap->height)
772     return BLACK_PIXEL;
773
774   return SDLGetPixel(bitmap, x, y);
775 }
776
777 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
778                       unsigned int color_g, unsigned int color_b)
779 {
780   return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
781 }
782
783 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
784 {
785   unsigned int color_r = (color >> 16) & 0xff;
786   unsigned int color_g = (color >>  8) & 0xff;
787   unsigned int color_b = (color >>  0) & 0xff;
788
789   return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
790 }
791
792 void KeyboardAutoRepeatOn(void)
793 {
794 #if defined(TARGET_SDL2)
795   keyrepeat_status = TRUE;
796 #else
797   SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
798                       SDL_DEFAULT_REPEAT_INTERVAL / 2);
799   SDL_EnableUNICODE(1);
800 #endif
801 }
802
803 void KeyboardAutoRepeatOff(void)
804 {
805 #if defined(TARGET_SDL2)
806   keyrepeat_status = FALSE;
807 #else
808   SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
809   SDL_EnableUNICODE(0);
810 #endif
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('%s') failed: %s", basename, 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('%s') failed: %s", basename, 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   gfx.cursor_mode = mode;
1280 }
1281
1282
1283 /* ========================================================================= */
1284 /* audio functions                                                           */
1285 /* ========================================================================= */
1286
1287 void OpenAudio(void)
1288 {
1289   /* always start with reliable default values */
1290   audio.sound_available = FALSE;
1291   audio.music_available = FALSE;
1292   audio.loops_available = FALSE;
1293
1294   audio.sound_enabled = FALSE;
1295   audio.sound_deactivated = FALSE;
1296
1297   audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1298   audio.mixer_pid = 0;
1299   audio.device_name = NULL;
1300   audio.device_fd = -1;
1301
1302   audio.num_channels = 0;
1303   audio.music_channel = 0;
1304   audio.first_sound_channel = 0;
1305
1306   SDLOpenAudio();
1307 }
1308
1309 void CloseAudio(void)
1310 {
1311   SDLCloseAudio();
1312
1313   audio.sound_enabled = FALSE;
1314 }
1315
1316 void SetAudioMode(boolean enabled)
1317 {
1318   if (!audio.sound_available)
1319     return;
1320
1321   audio.sound_enabled = enabled;
1322 }
1323
1324
1325 /* ========================================================================= */
1326 /* event functions                                                           */
1327 /* ========================================================================= */
1328
1329 void InitEventFilter(EventFilter filter_function)
1330 {
1331   /* set event filter to filter out certain events */
1332 #if defined(TARGET_SDL2)
1333   SDL_SetEventFilter(filter_function, NULL);
1334 #else
1335   SDL_SetEventFilter(filter_function);
1336 #endif
1337 }
1338
1339 boolean PendingEvent(void)
1340 {
1341   return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1342 }
1343
1344 void NextEvent(Event *event)
1345 {
1346   SDLNextEvent(event);
1347 }
1348
1349 void PeekEvent(Event *event)
1350 {
1351 #if defined(TARGET_SDL2)
1352   SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1353 #else
1354   SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1355 #endif
1356 }
1357
1358 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1359 {
1360 #if defined(TARGET_SDL2)
1361   /* key up/down events in SDL2 do not return text characters anymore */
1362   return event->keysym.sym;
1363 #else
1364
1365 #if ENABLE_UNUSED_CODE
1366   printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1367          (int)event->keysym.unicode,
1368          (int)event->keysym.sym,
1369          (int)SDL_GetModState());
1370 #endif
1371
1372   if (with_modifiers &&
1373       event->keysym.unicode > 0x0000 &&
1374       event->keysym.unicode < 0x2000)
1375     return event->keysym.unicode;
1376   else
1377     return event->keysym.sym;
1378
1379 #endif
1380 }
1381
1382 KeyMod HandleKeyModState(Key key, int key_status)
1383 {
1384   static KeyMod current_modifiers = KMOD_None;
1385
1386   if (key != KSYM_UNDEFINED)    /* new key => check for modifier key change */
1387   {
1388     KeyMod new_modifier = KMOD_None;
1389
1390     switch(key)
1391     {
1392       case KSYM_Shift_L:
1393         new_modifier = KMOD_Shift_L;
1394         break;
1395       case KSYM_Shift_R:
1396         new_modifier = KMOD_Shift_R;
1397         break;
1398       case KSYM_Control_L:
1399         new_modifier = KMOD_Control_L;
1400         break;
1401       case KSYM_Control_R:
1402         new_modifier = KMOD_Control_R;
1403         break;
1404       case KSYM_Meta_L:
1405         new_modifier = KMOD_Meta_L;
1406         break;
1407       case KSYM_Meta_R:
1408         new_modifier = KMOD_Meta_R;
1409         break;
1410       case KSYM_Alt_L:
1411         new_modifier = KMOD_Alt_L;
1412         break;
1413       case KSYM_Alt_R:
1414         new_modifier = KMOD_Alt_R;
1415         break;
1416       default:
1417         break;
1418     }
1419
1420     if (key_status == KEY_PRESSED)
1421       current_modifiers |= new_modifier;
1422     else
1423       current_modifiers &= ~new_modifier;
1424   }
1425
1426   return current_modifiers;
1427 }
1428
1429 KeyMod GetKeyModState()
1430 {
1431   return (KeyMod)SDL_GetModState();
1432 }
1433
1434 KeyMod GetKeyModStateFromEvents()
1435 {
1436   /* always use key modifier state as tracked from key events (this is needed
1437      if the modifier key event was injected into the event queue, but the key
1438      was not really pressed on keyboard -- SDL_GetModState() seems to directly
1439      query the keys as held pressed on the keyboard) -- this case is currently
1440      only used to filter out clipboard insert events from "True X-Mouse" tool */
1441
1442   return HandleKeyModState(KSYM_UNDEFINED, 0);
1443 }
1444
1445 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1446 {
1447   if (event->type != EVENT_CLIENTMESSAGE)
1448     return FALSE;
1449
1450   return TRUE;          /* the only possible message here is SDL_QUIT */
1451 }
1452
1453
1454 /* ========================================================================= */
1455 /* joystick functions                                                        */
1456 /* ========================================================================= */
1457
1458 void InitJoysticks()
1459 {
1460   int i;
1461
1462 #if defined(NO_JOYSTICK)
1463   return;       /* joysticks generally deactivated by compile-time directive */
1464 #endif
1465
1466   /* always start with reliable default values */
1467   joystick.status = JOYSTICK_NOT_AVAILABLE;
1468   for (i = 0; i < MAX_PLAYERS; i++)
1469     joystick.fd[i] = -1;                /* joystick device closed */
1470
1471   SDLInitJoysticks();
1472 }
1473
1474 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1475 {
1476   return SDLReadJoystick(nr, x, y, b1, b2);
1477 }