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