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