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