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