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