f0d73e7311e9e102686a56319b8c4ce59e4f7df9
[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 SetVideoMode(boolean fullscreen)
804 {
805   return SDLSetVideoMode(&backbuffer, fullscreen);
806 }
807
808 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
809 {
810   if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
811       (!fullscreen && video.fullscreen_enabled))
812     fullscreen = SetVideoMode(fullscreen);
813
814   return fullscreen;
815 }
816
817 Bitmap *LoadImage(char *filename)
818 {
819   Bitmap *new_bitmap;
820
821   new_bitmap = SDLLoadImage(filename);
822
823   if (new_bitmap)
824     new_bitmap->source_filename = getStringCopy(filename);
825
826   return new_bitmap;
827 }
828
829 Bitmap *LoadCustomImage(char *basename)
830 {
831   char *filename = getCustomImageFilename(basename);
832   Bitmap *new_bitmap;
833
834   if (filename == NULL)
835     Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
836
837   if ((new_bitmap = LoadImage(filename)) == NULL)
838     Error(ERR_EXIT, "LoadImage() failed: %s", GetError());
839
840   return new_bitmap;
841 }
842
843 void ReloadCustomImage(Bitmap *bitmap, char *basename)
844 {
845   char *filename = getCustomImageFilename(basename);
846   Bitmap *new_bitmap;
847
848   if (filename == NULL)         /* (should never happen) */
849   {
850     Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
851     return;
852   }
853
854   if (strEqual(filename, bitmap->source_filename))
855   {
856     /* The old and new image are the same (have the same filename and path).
857        This usually means that this image does not exist in this graphic set
858        and a fallback to the existing image is done. */
859
860     return;
861   }
862
863   if ((new_bitmap = LoadImage(filename)) == NULL)
864   {
865     Error(ERR_WARN, "LoadImage() failed: %s", GetError());
866     return;
867   }
868
869   if (bitmap->width != new_bitmap->width ||
870       bitmap->height != new_bitmap->height)
871   {
872     Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
873           filename);
874     FreeBitmap(new_bitmap);
875     return;
876   }
877
878   TransferBitmapPointers(new_bitmap, bitmap);
879   free(new_bitmap);
880 }
881
882 Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
883 {
884   Bitmap *dst_bitmap = SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
885
886   return dst_bitmap;
887 }
888
889 static void SetMaskedBitmapSurface(Bitmap *bitmap)
890 {
891   if (bitmap == NULL)
892     return;
893
894   SDL_Surface *surface = bitmap->surface;
895
896   if (bitmap->surface_masked)
897     SDL_FreeSurface(bitmap->surface_masked);
898
899   SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
900                   SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
901
902   if ((bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
903     Error(ERR_EXIT, "SDL_DisplayFormat() failed");
904
905   SDL_SetColorKey(surface, UNSET_TRANSPARENT_PIXEL, 0);
906 }
907
908 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
909 {
910   if (bitmaps[IMG_BITMAP_CUSTOM])
911   {
912     FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
913
914     bitmaps[IMG_BITMAP_CUSTOM] = NULL;
915   }
916
917   if (gfx.game_tile_size == gfx.standard_tile_size)
918   {
919     bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
920
921     return;
922   }
923
924   Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
925   int width  = bitmap->width  * gfx.game_tile_size / gfx.standard_tile_size;;
926   int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
927
928   Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
929
930   bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
931   bitmaps[IMG_BITMAP_GAME]   = bitmap_new;
932
933   SetMaskedBitmapSurface(bitmap_new);
934 }
935
936 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
937                                 int tile_size, boolean create_small_bitmaps)
938 {
939   Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
940   Bitmap *tmp_bitmap_final = NULL;
941   Bitmap *tmp_bitmap_0 = NULL;
942   Bitmap *tmp_bitmap_1 = NULL;
943   Bitmap *tmp_bitmap_2 = NULL;
944   Bitmap *tmp_bitmap_4 = NULL;
945   Bitmap *tmp_bitmap_8 = NULL;
946   Bitmap *tmp_bitmap_16 = NULL;
947   Bitmap *tmp_bitmap_32 = NULL;
948   int width_final, height_final;
949   int width_0, height_0;
950   int width_1, height_1;
951   int width_2, height_2;
952   int width_4, height_4;
953   int width_8, height_8;
954   int width_16, height_16;
955   int width_32, height_32;
956   int old_width, old_height;
957   int i;
958
959   print_timestamp_init("CreateScaledBitmaps");
960
961   old_width  = old_bitmap->width;
962   old_height = old_bitmap->height;
963
964   /* calculate new image dimensions for final image size */
965   width_final  = old_width  * zoom_factor;
966   height_final = old_height * zoom_factor;
967
968   /* get image with final size (this might require scaling up) */
969   /* ("final" size may result in non-standard tile size image) */
970   if (zoom_factor != 1)
971     tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
972   else
973     tmp_bitmap_final = old_bitmap;
974
975   UPDATE_BUSY_STATE();
976
977   width_0  = width_1  = width_final;
978   height_0 = height_1 = height_final;
979
980   tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
981
982   if (create_small_bitmaps)
983   {
984     /* check if we have a non-gameplay tile size image */
985     if (tile_size != gfx.game_tile_size)
986     {
987       /* get image with gameplay tile size */
988       width_0  = width_final  * gfx.game_tile_size / tile_size;
989       height_0 = height_final * gfx.game_tile_size / tile_size;
990
991       if (width_0 == old_width)
992         tmp_bitmap_0 = old_bitmap;
993       else if (width_0 == width_final)
994         tmp_bitmap_0 = tmp_bitmap_final;
995       else
996         tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
997
998       UPDATE_BUSY_STATE();
999     }
1000
1001     /* check if we have a non-standard tile size image */
1002     if (tile_size != gfx.standard_tile_size)
1003     {
1004       /* get image with standard tile size */
1005       width_1  = width_final  * gfx.standard_tile_size / tile_size;
1006       height_1 = height_final * gfx.standard_tile_size / tile_size;
1007
1008       if (width_1 == old_width)
1009         tmp_bitmap_1 = old_bitmap;
1010       else if (width_1 == width_final)
1011         tmp_bitmap_1 = tmp_bitmap_final;
1012       else if (width_1 == width_0)
1013         tmp_bitmap_1 = tmp_bitmap_0;
1014       else
1015         tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1016
1017       UPDATE_BUSY_STATE();
1018     }
1019
1020     /* calculate new image dimensions for small images */
1021     width_2  = width_1  / 2;
1022     height_2 = height_1 / 2;
1023     width_4  = width_1  / 4;
1024     height_4 = height_1 / 4;
1025     width_8  = width_1  / 8;
1026     height_8 = height_1 / 8;
1027     width_16  = width_1  / 16;
1028     height_16 = height_1 / 16;
1029     width_32  = width_1  / 32;
1030     height_32 = height_1 / 32;
1031
1032     /* get image with 1/2 of normal size (for use in the level editor) */
1033     if (width_2 == old_width)
1034       tmp_bitmap_2 = old_bitmap;
1035     else
1036       tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1037
1038     UPDATE_BUSY_STATE();
1039
1040     /* get image with 1/4 of normal size (for use in the level editor) */
1041     if (width_4 == old_width)
1042       tmp_bitmap_4 = old_bitmap;
1043     else
1044       tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1045
1046     UPDATE_BUSY_STATE();
1047
1048     /* get image with 1/8 of normal size (for use on the preview screen) */
1049     if (width_8 == old_width)
1050       tmp_bitmap_8 = old_bitmap;
1051     else
1052       tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1053
1054     UPDATE_BUSY_STATE();
1055
1056     /* get image with 1/16 of normal size (for use on the preview screen) */
1057     if (width_16 == old_width)
1058       tmp_bitmap_16 = old_bitmap;
1059     else
1060       tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1061
1062     UPDATE_BUSY_STATE();
1063
1064     /* get image with 1/32 of normal size (for use on the preview screen) */
1065     if (width_32 == old_width)
1066       tmp_bitmap_32 = old_bitmap;
1067     else
1068       tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1069
1070     UPDATE_BUSY_STATE();
1071
1072     bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1073     bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1074     bitmaps[IMG_BITMAP_8x8]   = tmp_bitmap_4;
1075     bitmaps[IMG_BITMAP_4x4]   = tmp_bitmap_8;
1076     bitmaps[IMG_BITMAP_2x2]   = tmp_bitmap_16;
1077     bitmaps[IMG_BITMAP_1x1]   = tmp_bitmap_32;
1078
1079     if (width_0 != width_1)
1080       bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1081
1082     if (bitmaps[IMG_BITMAP_CUSTOM])
1083       bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1084     else
1085       bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1086
1087     boolean free_old_bitmap = TRUE;
1088
1089     for (i = 0; i < NUM_IMG_BITMAPS; i++)
1090       if (bitmaps[i] == old_bitmap)
1091         free_old_bitmap = FALSE;
1092
1093     if (free_old_bitmap)
1094       FreeBitmap(old_bitmap);
1095   }
1096   else
1097   {
1098     bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1099   }
1100
1101   // create corresponding bitmaps for masked blitting
1102   for (i = 0; i < NUM_IMG_BITMAPS; i++)
1103     if (bitmaps[i] != NULL &&
1104         bitmaps[i] != old_bitmap)
1105       SetMaskedBitmapSurface(bitmaps[i]);
1106
1107   UPDATE_BUSY_STATE();
1108
1109   print_timestamp_done("CreateScaledBitmaps");
1110 }
1111
1112 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1113                                   int tile_size)
1114 {
1115   CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1116 }
1117
1118 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1119 {
1120   CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1121 }
1122
1123
1124 /* ------------------------------------------------------------------------- */
1125 /* mouse pointer functions                                                   */
1126 /* ------------------------------------------------------------------------- */
1127
1128 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER            0
1129
1130 /* XPM image definitions */
1131 static const char *cursor_image_none[] =
1132 {
1133   /* width height num_colors chars_per_pixel */
1134   "    16    16        3            1",
1135
1136   /* colors */
1137   "X c #000000",
1138   ". c #ffffff",
1139   "  c None",
1140
1141   /* pixels */
1142   "                ",
1143   "                ",
1144   "                ",
1145   "                ",
1146   "                ",
1147   "                ",
1148   "                ",
1149   "                ",
1150   "                ",
1151   "                ",
1152   "                ",
1153   "                ",
1154   "                ",
1155   "                ",
1156   "                ",
1157   "                ",
1158
1159   /* hot spot */
1160   "0,0"
1161 };
1162
1163 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1164 static const char *cursor_image_dot[] =
1165 {
1166   /* width height num_colors chars_per_pixel */
1167   "    16    16        3            1",
1168
1169   /* colors */
1170   "X c #000000",
1171   ". c #ffffff",
1172   "  c None",
1173
1174   /* pixels */
1175   " X              ",
1176   "X.X             ",
1177   " X              ",
1178   "                ",
1179   "                ",
1180   "                ",
1181   "                ",
1182   "                ",
1183   "                ",
1184   "                ",
1185   "                ",
1186   "                ",
1187   "                ",
1188   "                ",
1189   "                ",
1190   "                ",
1191
1192   /* hot spot */
1193   "1,1"
1194 };
1195 static const char **cursor_image_playfield = cursor_image_dot;
1196 #else
1197 /* some people complained about a "white dot" on the screen and thought it
1198    was a graphical error... OK, let's just remove the whole pointer :-) */
1199 static const char **cursor_image_playfield = cursor_image_none;
1200 #endif
1201
1202 static const int cursor_bit_order = BIT_ORDER_MSB;
1203
1204 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1205 {
1206   struct MouseCursorInfo *cursor;
1207   boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1208   int header_lines = 4;
1209   int x, y, i;
1210
1211   cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1212
1213   sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1214
1215   i = -1;
1216   for (y = 0; y < cursor->width; y++)
1217   {
1218     for (x = 0; x < cursor->height; x++)
1219     {
1220       int bit_nr = x % 8;
1221       int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1222
1223       if (bit_nr == 0)
1224       {
1225         i++;
1226         cursor->data[i] = cursor->mask[i] = 0;
1227       }
1228
1229       switch (image[header_lines + y][x])
1230       {
1231         case 'X':
1232           cursor->data[i] |= bit_mask;
1233           cursor->mask[i] |= bit_mask;
1234           break;
1235
1236         case '.':
1237           cursor->mask[i] |= bit_mask;
1238           break;
1239
1240         case ' ':
1241           break;
1242       }
1243     }
1244   }
1245
1246   sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1247
1248   return cursor;
1249 }
1250
1251 void SetMouseCursor(int mode)
1252 {
1253   static struct MouseCursorInfo *cursor_none = NULL;
1254   static struct MouseCursorInfo *cursor_playfield = NULL;
1255   struct MouseCursorInfo *cursor_new;
1256
1257   if (cursor_none == NULL)
1258     cursor_none = get_cursor_from_image(cursor_image_none);
1259
1260   if (cursor_playfield == NULL)
1261     cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1262
1263   cursor_new = (mode == CURSOR_DEFAULT   ? NULL :
1264                 mode == CURSOR_NONE      ? cursor_none :
1265                 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1266
1267   SDLSetMouseCursor(cursor_new);
1268
1269   gfx.cursor_mode = mode;
1270 }
1271
1272
1273 /* ========================================================================= */
1274 /* audio functions                                                           */
1275 /* ========================================================================= */
1276
1277 void OpenAudio(void)
1278 {
1279   /* always start with reliable default values */
1280   audio.sound_available = FALSE;
1281   audio.music_available = FALSE;
1282   audio.loops_available = FALSE;
1283
1284   audio.sound_enabled = FALSE;
1285   audio.sound_deactivated = FALSE;
1286
1287   audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1288   audio.mixer_pid = 0;
1289   audio.device_name = NULL;
1290   audio.device_fd = -1;
1291
1292   audio.num_channels = 0;
1293   audio.music_channel = 0;
1294   audio.first_sound_channel = 0;
1295
1296   SDLOpenAudio();
1297 }
1298
1299 void CloseAudio(void)
1300 {
1301   SDLCloseAudio();
1302
1303   audio.sound_enabled = FALSE;
1304 }
1305
1306 void SetAudioMode(boolean enabled)
1307 {
1308   if (!audio.sound_available)
1309     return;
1310
1311   audio.sound_enabled = enabled;
1312 }
1313
1314
1315 /* ========================================================================= */
1316 /* event functions                                                           */
1317 /* ========================================================================= */
1318
1319 void InitEventFilter(EventFilter filter_function)
1320 {
1321   /* set event filter to filter out certain events */
1322 #if defined(TARGET_SDL2)
1323   SDL_SetEventFilter(filter_function, NULL);
1324 #else
1325   SDL_SetEventFilter(filter_function);
1326 #endif
1327 }
1328
1329 boolean PendingEvent(void)
1330 {
1331   return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1332 }
1333
1334 void NextEvent(Event *event)
1335 {
1336   SDLNextEvent(event);
1337 }
1338
1339 void PeekEvent(Event *event)
1340 {
1341 #if defined(TARGET_SDL2)
1342   SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1343 #else
1344   SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1345 #endif
1346 }
1347
1348 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1349 {
1350 #if defined(TARGET_SDL2)
1351   /* key up/down events in SDL2 do not return text characters anymore */
1352   return event->keysym.sym;
1353 #else
1354
1355 #if ENABLE_UNUSED_CODE
1356   printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1357          (int)event->keysym.unicode,
1358          (int)event->keysym.sym,
1359          (int)SDL_GetModState());
1360 #endif
1361
1362   if (with_modifiers &&
1363       event->keysym.unicode > 0x0000 &&
1364       event->keysym.unicode < 0x2000)
1365     return event->keysym.unicode;
1366   else
1367     return event->keysym.sym;
1368
1369 #endif
1370 }
1371
1372 KeyMod HandleKeyModState(Key key, int key_status)
1373 {
1374   static KeyMod current_modifiers = KMOD_None;
1375
1376   if (key != KSYM_UNDEFINED)    /* new key => check for modifier key change */
1377   {
1378     KeyMod new_modifier = KMOD_None;
1379
1380     switch(key)
1381     {
1382       case KSYM_Shift_L:
1383         new_modifier = KMOD_Shift_L;
1384         break;
1385       case KSYM_Shift_R:
1386         new_modifier = KMOD_Shift_R;
1387         break;
1388       case KSYM_Control_L:
1389         new_modifier = KMOD_Control_L;
1390         break;
1391       case KSYM_Control_R:
1392         new_modifier = KMOD_Control_R;
1393         break;
1394       case KSYM_Meta_L:
1395         new_modifier = KMOD_Meta_L;
1396         break;
1397       case KSYM_Meta_R:
1398         new_modifier = KMOD_Meta_R;
1399         break;
1400       case KSYM_Alt_L:
1401         new_modifier = KMOD_Alt_L;
1402         break;
1403       case KSYM_Alt_R:
1404         new_modifier = KMOD_Alt_R;
1405         break;
1406       default:
1407         break;
1408     }
1409
1410     if (key_status == KEY_PRESSED)
1411       current_modifiers |= new_modifier;
1412     else
1413       current_modifiers &= ~new_modifier;
1414   }
1415
1416   return current_modifiers;
1417 }
1418
1419 KeyMod GetKeyModState()
1420 {
1421   return (KeyMod)SDL_GetModState();
1422 }
1423
1424 KeyMod GetKeyModStateFromEvents()
1425 {
1426   /* always use key modifier state as tracked from key events (this is needed
1427      if the modifier key event was injected into the event queue, but the key
1428      was not really pressed on keyboard -- SDL_GetModState() seems to directly
1429      query the keys as held pressed on the keyboard) -- this case is currently
1430      only used to filter out clipboard insert events from "True X-Mouse" tool */
1431
1432   return HandleKeyModState(KSYM_UNDEFINED, 0);
1433 }
1434
1435 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1436 {
1437   if (event->type != EVENT_CLIENTMESSAGE)
1438     return FALSE;
1439
1440   return TRUE;          /* the only possible message here is SDL_QUIT */
1441 }
1442
1443
1444 /* ========================================================================= */
1445 /* joystick functions                                                        */
1446 /* ========================================================================= */
1447
1448 void InitJoysticks()
1449 {
1450   int i;
1451
1452 #if defined(NO_JOYSTICK)
1453   return;       /* joysticks generally deactivated by compile-time directive */
1454 #endif
1455
1456   /* always start with reliable default values */
1457   joystick.status = JOYSTICK_NOT_AVAILABLE;
1458   for (i = 0; i < MAX_PLAYERS; i++)
1459     joystick.fd[i] = -1;                /* joystick device closed */
1460
1461   SDLInitJoysticks();
1462 }
1463
1464 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1465 {
1466   return SDLReadJoystick(nr, x, y, b1, b2);
1467 }