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