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