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