1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
24 #define ENABLE_UNUSED_CODE 0 /* currently unused functions */
27 /* ========================================================================= */
28 /* exported variables */
29 /* ========================================================================= */
31 struct ProgramInfo program;
32 struct OptionInfo options;
33 struct VideoSystemInfo video;
34 struct AudioSystemInfo audio;
36 struct ArtworkInfo artwork;
37 struct JoystickInfo joystick;
38 struct SetupInfo setup;
40 LevelDirTree *leveldir_first_all = NULL;
41 LevelDirTree *leveldir_first = NULL;
42 LevelDirTree *leveldir_current = NULL;
45 struct LevelStats level_stats[MAX_LEVELS];
47 DrawWindow *window = NULL;
48 DrawBuffer *backbuffer = NULL;
49 DrawBuffer *drawto = NULL;
51 int button_status = MB_NOT_PRESSED;
52 boolean motion_status = FALSE;
53 #if defined(TARGET_SDL2)
54 boolean keyrepeat_status = TRUE;
57 int redraw_mask = REDRAW_NONE;
63 /* ========================================================================= */
64 /* init/close functions */
65 /* ========================================================================= */
67 void InitProgramInfo(char *argv0,
68 char *userdata_subdir, char *userdata_subdir_unix,
69 char *program_title, char *icon_title,
70 char *sdl_icon_filename, char *cookie_prefix,
73 program.command_basepath = getBasePath(argv0);
74 program.command_basename = getBaseName(argv0);
76 program.userdata_subdir = userdata_subdir;
77 program.userdata_subdir_unix = userdata_subdir_unix;
78 program.userdata_path = getUserGameDataDir();
80 program.program_title = program_title;
81 program.window_title = "(undefined)";
82 program.icon_title = icon_title;
84 program.sdl_icon_filename = sdl_icon_filename;
86 program.cookie_prefix = cookie_prefix;
88 program.version_major = VERSION_MAJOR(program_version);
89 program.version_minor = VERSION_MINOR(program_version);
90 program.version_patch = VERSION_PATCH(program_version);
91 program.version_build = VERSION_BUILD(program_version);
92 program.version_ident = program_version;
94 program.error_filename = getErrorFilename(ERROR_BASENAME);
95 program.error_file = stderr;
100 program.window_title = program.window_title_function();
105 void InitWindowTitleFunction(char *(*window_title_function)(void))
107 program.window_title_function = window_title_function;
110 void InitExitMessageFunction(void (*exit_message_function)(char *, va_list))
112 program.exit_message_function = exit_message_function;
115 void InitExitFunction(void (*exit_function)(int))
117 program.exit_function = exit_function;
119 /* set signal handlers to custom exit function */
120 signal(SIGINT, exit_function);
121 signal(SIGTERM, exit_function);
123 /* set exit function to automatically cleanup SDL stuff after exit() */
127 void InitPlatformDependentStuff(void)
129 // this is initialized in GetOptions(), but may already be used before
130 options.verbose = TRUE;
132 #if defined(PLATFORM_MACOSX)
133 updateUserGameDataDir();
138 #if defined(TARGET_SDL2)
139 int sdl_init_flags = SDL_INIT_EVENTS | SDL_INIT_NOPARACHUTE;
141 int sdl_init_flags = SDL_INIT_EVENTTHREAD | SDL_INIT_NOPARACHUTE;
144 if (SDL_Init(sdl_init_flags) < 0)
145 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
150 void ClosePlatformDependentStuff(void)
152 #if defined(PLATFORM_WIN32)
157 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
158 int real_sx, int real_sy,
159 int full_sxsize, int full_sysize,
160 Bitmap *field_save_buffer)
166 gfx.real_sx = real_sx;
167 gfx.real_sy = real_sy;
168 gfx.full_sxsize = full_sxsize;
169 gfx.full_sysize = full_sysize;
171 gfx.field_save_buffer = field_save_buffer;
173 gfx.drawing_area_changed = FALSE;
175 SetDrawDeactivationMask(REDRAW_NONE); /* do not deactivate drawing */
176 SetDrawBackgroundMask(REDRAW_NONE); /* deactivate masked drawing */
179 void InitGfxTileSizeInfo(int game_tile_size, int standard_tile_size)
181 gfx.game_tile_size = game_tile_size;
182 gfx.standard_tile_size = standard_tile_size;
185 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
193 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
201 void InitGfxDoor3Info(int ex, int ey, int exsize, int eysize)
209 void InitGfxWindowInfo(int win_xsize, int win_ysize)
211 gfx.win_xsize = win_xsize;
212 gfx.win_ysize = win_ysize;
214 gfx.background_bitmap_mask = REDRAW_NONE;
216 ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize, DEFAULT_DEPTH);
219 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
221 /* currently only used by MSDOS code to alloc VRAM buffer, if available */
222 /* 2009-03-24: also (temporarily?) used for overlapping blit workaround */
223 gfx.scrollbuffer_width = scrollbuffer_width;
224 gfx.scrollbuffer_height = scrollbuffer_height;
227 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
229 gfx.clipping_enabled = enabled;
232 gfx.clip_width = width;
233 gfx.clip_height = height;
236 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
238 gfx.draw_busy_anim_function = draw_busy_anim_function;
241 void InitGfxCustomArtworkInfo()
243 gfx.override_level_graphics = FALSE;
244 gfx.override_level_sounds = FALSE;
245 gfx.override_level_music = FALSE;
247 gfx.draw_init_text = TRUE;
250 void SetDrawDeactivationMask(int draw_deactivation_mask)
252 gfx.draw_deactivation_mask = draw_deactivation_mask;
255 void SetDrawBackgroundMask(int draw_background_mask)
257 gfx.draw_background_mask = draw_background_mask;
260 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
262 if (background_bitmap_tile != NULL)
263 gfx.background_bitmap_mask |= mask;
265 gfx.background_bitmap_mask &= ~mask;
267 if (background_bitmap_tile == NULL) /* empty background requested */
270 if (mask == REDRAW_ALL)
271 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
272 0, 0, video.width, video.height);
273 else if (mask == REDRAW_FIELD)
274 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
275 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
276 else if (mask == REDRAW_DOOR_1)
277 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
278 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
281 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
283 /* remove every mask before setting mask for window */
284 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
285 SetBackgroundBitmap(NULL, 0xffff); /* !!! FIX THIS !!! */
286 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
289 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
291 /* remove window area mask before setting mask for main area */
292 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
293 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
294 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
297 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
299 /* remove window area mask before setting mask for door area */
300 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
301 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
302 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
306 /* ========================================================================= */
307 /* video functions */
308 /* ========================================================================= */
310 inline static int GetRealDepth(int depth)
312 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
315 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
316 int width, int height, Pixel color)
318 SDLFillRectangle(bitmap, x, y, width, height, color);
321 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
322 int src_x, int src_y, int width, int height,
323 int dst_x, int dst_y, int mask_mode)
325 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
326 dst_x, dst_y, mask_mode);
329 void LimitScreenUpdates(boolean enable)
331 SDLLimitScreenUpdates(enable);
334 void InitVideoDisplay(void)
336 SDLInitVideoDisplay();
339 void CloseVideoDisplay(void)
341 KeyboardAutoRepeatOn();
343 SDL_QuitSubSystem(SDL_INIT_VIDEO);
346 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
349 video.height = height;
350 video.depth = GetRealDepth(depth);
352 video.fullscreen_available = FULLSCREEN_STATUS;
353 video.fullscreen_enabled = FALSE;
355 video.window_scaling_available = WINDOW_SCALING_STATUS;
357 SDLInitVideoBuffer(&backbuffer, &window, fullscreen);
362 inline static void FreeBitmapPointers(Bitmap *bitmap)
367 SDLFreeBitmapPointers(bitmap);
369 checked_free(bitmap->source_filename);
370 bitmap->source_filename = NULL;
373 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
376 if (src_bitmap == NULL || dst_bitmap == NULL)
379 FreeBitmapPointers(dst_bitmap);
381 *dst_bitmap = *src_bitmap;
384 void FreeBitmap(Bitmap *bitmap)
389 FreeBitmapPointers(bitmap);
394 Bitmap *CreateBitmapStruct(void)
396 return checked_calloc(sizeof(struct SDLSurfaceInfo));
399 Bitmap *CreateBitmap(int width, int height, int depth)
401 Bitmap *new_bitmap = CreateBitmapStruct();
402 int real_width = MAX(1, width); /* prevent zero bitmap width */
403 int real_height = MAX(1, height); /* prevent zero bitmap height */
404 int real_depth = GetRealDepth(depth);
406 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
408 new_bitmap->width = real_width;
409 new_bitmap->height = real_height;
414 void ReCreateBitmap(Bitmap **bitmap, int width, int height, int depth)
416 Bitmap *new_bitmap = CreateBitmap(width, height, depth);
420 *bitmap = new_bitmap;
424 TransferBitmapPointers(new_bitmap, *bitmap);
429 void CloseWindow(DrawWindow *window)
433 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
436 if (draw_mask == REDRAW_NONE)
439 if (draw_mask & REDRAW_ALL)
442 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
445 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
448 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
451 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
457 boolean DrawingDeactivated(int x, int y, int width, int height)
459 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
462 boolean DrawingOnBackground(int x, int y)
464 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
465 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
468 boolean DrawingAreaChanged()
470 int drawing_area_changed = gfx.drawing_area_changed;
472 // reset flag for change of drawing area after querying it
473 gfx.drawing_area_changed = FALSE;
475 return drawing_area_changed;
478 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
479 int *width, int *height, boolean is_dest)
481 int clip_x, clip_y, clip_width, clip_height;
483 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
485 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
486 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
487 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
488 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
494 clip_width = bitmap->width;
495 clip_height = bitmap->height;
498 /* skip if rectangle completely outside bitmap */
500 if (*x + *width <= clip_x ||
501 *y + *height <= clip_y ||
502 *x >= clip_x + clip_width ||
503 *y >= clip_y + clip_height)
506 /* clip if rectangle overlaps bitmap */
510 *width -= clip_x - *x;
513 else if (*x + *width > clip_x + clip_width)
515 *width = clip_x + clip_width - *x;
520 *height -= clip_y - *y;
523 else if (*y + *height > clip_y + clip_height)
525 *height = clip_y + clip_height - *y;
531 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
532 int src_x, int src_y, int width, int height,
533 int dst_x, int dst_y)
535 int dst_x_unclipped = dst_x;
536 int dst_y_unclipped = dst_y;
538 if (src_bitmap == NULL || dst_bitmap == NULL)
541 if (DrawingDeactivated(dst_x, dst_y, width, height))
544 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
545 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
548 /* source x/y might need adjustment if destination x/y was clipped top/left */
549 src_x += dst_x - dst_x_unclipped;
550 src_y += dst_y - dst_y_unclipped;
552 #if defined(TARGET_SDL2)
553 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
554 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
555 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
556 but is already fixed in SVN and should therefore finally be fixed with
557 the next official SDL release, which is probably version 1.2.14.) */
558 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
560 if (src_bitmap == dst_bitmap)
562 /* needed when blitting directly to same bitmap -- should not be needed with
563 recent SDL libraries, but apparently does not work in 1.2.11 directly */
565 static Bitmap *tmp_bitmap = NULL;
566 static int tmp_bitmap_xsize = 0;
567 static int tmp_bitmap_ysize = 0;
569 /* start with largest static bitmaps for initial bitmap size ... */
570 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
572 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
573 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
576 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
577 if (src_bitmap->width > tmp_bitmap_xsize ||
578 src_bitmap->height > tmp_bitmap_ysize)
580 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
581 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
583 FreeBitmap(tmp_bitmap);
588 if (tmp_bitmap == NULL)
589 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
592 sysCopyArea(src_bitmap, tmp_bitmap,
593 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
594 sysCopyArea(tmp_bitmap, dst_bitmap,
595 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
601 sysCopyArea(src_bitmap, dst_bitmap,
602 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
605 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
606 int src_x, int src_y, int src_width, int src_height,
607 int dst_x, int dst_y, int dst_width, int dst_height)
609 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
610 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
611 int dst_xsize = dst_width;
612 int dst_ysize = dst_height;
613 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
614 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
617 for (y = 0; y < src_ysteps; y++)
619 for (x = 0; x < src_xsteps; x++)
621 int draw_x = dst_x + x * src_xsize;
622 int draw_y = dst_y + y * src_ysize;
623 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
624 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
626 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
632 void FadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
633 int fade_mode, int fade_delay, int post_delay,
634 void (*draw_border_function)(void))
636 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
637 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
640 SDLFadeRectangle(bitmap_cross, x, y, width, height,
641 fade_mode, fade_delay, post_delay, draw_border_function);
644 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
647 if (DrawingDeactivated(x, y, width, height))
650 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
653 sysFillRectangle(bitmap, x, y, width, height, color);
656 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
658 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
661 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
662 int width, int height)
664 if (DrawingOnBackground(x, y))
665 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
667 ClearRectangle(bitmap, x, y, width, height);
670 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
671 int src_x, int src_y, int width, int height,
672 int dst_x, int dst_y)
674 if (DrawingDeactivated(dst_x, dst_y, width, height))
677 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
678 dst_x, dst_y, BLIT_MASKED);
681 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
682 int src_x, int src_y, int width, int height,
683 int dst_x, int dst_y)
685 if (DrawingOnBackground(dst_x, dst_y))
687 /* draw background */
688 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
691 /* draw foreground */
692 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
696 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
700 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
703 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
706 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
709 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
712 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
713 int to_x, int to_y, Pixel pixel, int line_width)
717 for (x = 0; x < line_width; x++)
719 for (y = 0; y < line_width; y++)
721 int dx = x - line_width / 2;
722 int dy = y - line_width / 2;
724 if ((x == 0 && y == 0) ||
725 (x == 0 && y == line_width - 1) ||
726 (x == line_width - 1 && y == 0) ||
727 (x == line_width - 1 && y == line_width - 1))
731 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
736 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
741 for (i = 0; i < num_points - 1; i++)
742 DrawLine(bitmap, points[i].x, points[i].y,
743 points[i + 1].x, points[i + 1].y, pixel, line_width);
746 SDLDrawLines(bitmap->surface, points, num_points, pixel);
750 Pixel GetPixel(Bitmap *bitmap, int x, int y)
752 if (x < 0 || x >= bitmap->width ||
753 y < 0 || y >= bitmap->height)
756 return SDLGetPixel(bitmap, x, y);
759 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
760 unsigned int color_g, unsigned int color_b)
762 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
765 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
767 unsigned int color_r = (color >> 16) & 0xff;
768 unsigned int color_g = (color >> 8) & 0xff;
769 unsigned int color_b = (color >> 0) & 0xff;
771 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
774 void KeyboardAutoRepeatOn(void)
776 #if defined(TARGET_SDL2)
777 keyrepeat_status = TRUE;
779 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
780 SDL_DEFAULT_REPEAT_INTERVAL / 2);
781 SDL_EnableUNICODE(1);
785 void KeyboardAutoRepeatOff(void)
787 #if defined(TARGET_SDL2)
788 keyrepeat_status = FALSE;
790 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
791 SDL_EnableUNICODE(0);
795 boolean PointerInWindow(DrawWindow *window)
800 boolean SetVideoMode(boolean fullscreen)
802 return SDLSetVideoMode(&backbuffer, fullscreen);
805 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
807 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
808 (!fullscreen && video.fullscreen_enabled))
809 fullscreen = SetVideoMode(fullscreen);
814 Bitmap *LoadImage(char *filename)
818 new_bitmap = SDLLoadImage(filename);
821 new_bitmap->source_filename = getStringCopy(filename);
826 Bitmap *LoadCustomImage(char *basename)
828 char *filename = getCustomImageFilename(basename);
831 if (filename == NULL)
832 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
834 if ((new_bitmap = LoadImage(filename)) == NULL)
835 Error(ERR_EXIT, "LoadImage() failed: %s", GetError());
840 void ReloadCustomImage(Bitmap *bitmap, char *basename)
842 char *filename = getCustomImageFilename(basename);
845 if (filename == NULL) /* (should never happen) */
847 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
851 if (strEqual(filename, bitmap->source_filename))
853 /* The old and new image are the same (have the same filename and path).
854 This usually means that this image does not exist in this graphic set
855 and a fallback to the existing image is done. */
860 if ((new_bitmap = LoadImage(filename)) == NULL)
862 Error(ERR_WARN, "LoadImage() failed: %s", GetError());
866 if (bitmap->width != new_bitmap->width ||
867 bitmap->height != new_bitmap->height)
869 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
871 FreeBitmap(new_bitmap);
875 TransferBitmapPointers(new_bitmap, bitmap);
879 Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
881 Bitmap *dst_bitmap = SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
886 static void SetMaskedBitmapSurface(Bitmap *bitmap)
891 SDL_Surface *surface = bitmap->surface;
893 if (bitmap->surface_masked)
894 SDL_FreeSurface(bitmap->surface_masked);
896 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
897 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
899 if ((bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
900 Error(ERR_EXIT, "SDL_DisplayFormat() failed");
902 SDL_SetColorKey(surface, UNSET_TRANSPARENT_PIXEL, 0);
905 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
907 if (bitmaps[IMG_BITMAP_CUSTOM])
909 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
911 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
914 if (gfx.game_tile_size == gfx.standard_tile_size)
916 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
921 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
922 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
923 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
925 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
927 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
928 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
930 SetMaskedBitmapSurface(bitmap_new);
933 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
934 int tile_size, boolean create_small_bitmaps)
936 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
937 Bitmap *tmp_bitmap_final = NULL;
938 Bitmap *tmp_bitmap_0 = NULL;
939 Bitmap *tmp_bitmap_1 = NULL;
940 Bitmap *tmp_bitmap_2 = NULL;
941 Bitmap *tmp_bitmap_4 = NULL;
942 Bitmap *tmp_bitmap_8 = NULL;
943 Bitmap *tmp_bitmap_16 = NULL;
944 Bitmap *tmp_bitmap_32 = NULL;
945 int width_final, height_final;
946 int width_0, height_0;
947 int width_1, height_1;
948 int width_2, height_2;
949 int width_4, height_4;
950 int width_8, height_8;
951 int width_16, height_16;
952 int width_32, height_32;
953 int old_width, old_height;
956 print_timestamp_init("CreateScaledBitmaps");
958 old_width = old_bitmap->width;
959 old_height = old_bitmap->height;
961 /* calculate new image dimensions for final image size */
962 width_final = old_width * zoom_factor;
963 height_final = old_height * zoom_factor;
965 /* get image with final size (this might require scaling up) */
966 /* ("final" size may result in non-standard tile size image) */
967 if (zoom_factor != 1)
968 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
970 tmp_bitmap_final = old_bitmap;
974 width_0 = width_1 = width_final;
975 height_0 = height_1 = height_final;
977 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
979 if (create_small_bitmaps)
981 /* check if we have a non-gameplay tile size image */
982 if (tile_size != gfx.game_tile_size)
984 /* get image with gameplay tile size */
985 width_0 = width_final * gfx.game_tile_size / tile_size;
986 height_0 = height_final * gfx.game_tile_size / tile_size;
988 if (width_0 == old_width)
989 tmp_bitmap_0 = old_bitmap;
990 else if (width_0 == width_final)
991 tmp_bitmap_0 = tmp_bitmap_final;
993 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
998 /* check if we have a non-standard tile size image */
999 if (tile_size != gfx.standard_tile_size)
1001 /* get image with standard tile size */
1002 width_1 = width_final * gfx.standard_tile_size / tile_size;
1003 height_1 = height_final * gfx.standard_tile_size / tile_size;
1005 if (width_1 == old_width)
1006 tmp_bitmap_1 = old_bitmap;
1007 else if (width_1 == width_final)
1008 tmp_bitmap_1 = tmp_bitmap_final;
1009 else if (width_1 == width_0)
1010 tmp_bitmap_1 = tmp_bitmap_0;
1012 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1014 UPDATE_BUSY_STATE();
1017 /* calculate new image dimensions for small images */
1018 width_2 = width_1 / 2;
1019 height_2 = height_1 / 2;
1020 width_4 = width_1 / 4;
1021 height_4 = height_1 / 4;
1022 width_8 = width_1 / 8;
1023 height_8 = height_1 / 8;
1024 width_16 = width_1 / 16;
1025 height_16 = height_1 / 16;
1026 width_32 = width_1 / 32;
1027 height_32 = height_1 / 32;
1029 /* get image with 1/2 of normal size (for use in the level editor) */
1030 if (width_2 == old_width)
1031 tmp_bitmap_2 = old_bitmap;
1033 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1035 UPDATE_BUSY_STATE();
1037 /* get image with 1/4 of normal size (for use in the level editor) */
1038 if (width_4 == old_width)
1039 tmp_bitmap_4 = old_bitmap;
1041 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1043 UPDATE_BUSY_STATE();
1045 /* get image with 1/8 of normal size (for use on the preview screen) */
1046 if (width_8 == old_width)
1047 tmp_bitmap_8 = old_bitmap;
1049 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1051 UPDATE_BUSY_STATE();
1053 /* get image with 1/16 of normal size (for use on the preview screen) */
1054 if (width_16 == old_width)
1055 tmp_bitmap_16 = old_bitmap;
1057 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1059 UPDATE_BUSY_STATE();
1061 /* get image with 1/32 of normal size (for use on the preview screen) */
1062 if (width_32 == old_width)
1063 tmp_bitmap_32 = old_bitmap;
1065 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1067 UPDATE_BUSY_STATE();
1069 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1070 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1071 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1072 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1073 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1074 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1076 if (width_0 != width_1)
1077 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1079 if (bitmaps[IMG_BITMAP_CUSTOM])
1080 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1082 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1084 boolean free_old_bitmap = TRUE;
1086 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1087 if (bitmaps[i] == old_bitmap)
1088 free_old_bitmap = FALSE;
1090 if (free_old_bitmap)
1091 FreeBitmap(old_bitmap);
1095 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1098 // create corresponding bitmaps for masked blitting
1099 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1100 if (bitmaps[i] != NULL &&
1101 bitmaps[i] != old_bitmap)
1102 SetMaskedBitmapSurface(bitmaps[i]);
1104 UPDATE_BUSY_STATE();
1106 print_timestamp_done("CreateScaledBitmaps");
1109 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1112 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1115 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1117 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1121 /* ------------------------------------------------------------------------- */
1122 /* mouse pointer functions */
1123 /* ------------------------------------------------------------------------- */
1125 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1127 /* XPM image definitions */
1128 static const char *cursor_image_none[] =
1130 /* width height num_colors chars_per_pixel */
1160 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1161 static const char *cursor_image_dot[] =
1163 /* width height num_colors chars_per_pixel */
1192 static const char **cursor_image_playfield = cursor_image_dot;
1194 /* some people complained about a "white dot" on the screen and thought it
1195 was a graphical error... OK, let's just remove the whole pointer :-) */
1196 static const char **cursor_image_playfield = cursor_image_none;
1199 static const int cursor_bit_order = BIT_ORDER_MSB;
1201 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1203 struct MouseCursorInfo *cursor;
1204 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1205 int header_lines = 4;
1208 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1210 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1213 for (y = 0; y < cursor->width; y++)
1215 for (x = 0; x < cursor->height; x++)
1218 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1223 cursor->data[i] = cursor->mask[i] = 0;
1226 switch (image[header_lines + y][x])
1229 cursor->data[i] |= bit_mask;
1230 cursor->mask[i] |= bit_mask;
1234 cursor->mask[i] |= bit_mask;
1243 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1248 void SetMouseCursor(int mode)
1250 static struct MouseCursorInfo *cursor_none = NULL;
1251 static struct MouseCursorInfo *cursor_playfield = NULL;
1252 struct MouseCursorInfo *cursor_new;
1254 if (cursor_none == NULL)
1255 cursor_none = get_cursor_from_image(cursor_image_none);
1257 if (cursor_playfield == NULL)
1258 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1260 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1261 mode == CURSOR_NONE ? cursor_none :
1262 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1264 SDLSetMouseCursor(cursor_new);
1268 /* ========================================================================= */
1269 /* audio functions */
1270 /* ========================================================================= */
1272 void OpenAudio(void)
1274 /* always start with reliable default values */
1275 audio.sound_available = FALSE;
1276 audio.music_available = FALSE;
1277 audio.loops_available = FALSE;
1279 audio.sound_enabled = FALSE;
1280 audio.sound_deactivated = FALSE;
1282 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1283 audio.mixer_pid = 0;
1284 audio.device_name = NULL;
1285 audio.device_fd = -1;
1287 audio.num_channels = 0;
1288 audio.music_channel = 0;
1289 audio.first_sound_channel = 0;
1294 void CloseAudio(void)
1298 audio.sound_enabled = FALSE;
1301 void SetAudioMode(boolean enabled)
1303 if (!audio.sound_available)
1306 audio.sound_enabled = enabled;
1310 /* ========================================================================= */
1311 /* event functions */
1312 /* ========================================================================= */
1314 void InitEventFilter(EventFilter filter_function)
1316 /* set event filter to filter out certain events */
1317 #if defined(TARGET_SDL2)
1318 SDL_SetEventFilter(filter_function, NULL);
1320 SDL_SetEventFilter(filter_function);
1324 boolean PendingEvent(void)
1326 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1329 void NextEvent(Event *event)
1331 SDLNextEvent(event);
1334 void PeekEvent(Event *event)
1336 #if defined(TARGET_SDL2)
1337 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1339 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1343 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1345 #if defined(TARGET_SDL2)
1346 /* key up/down events in SDL2 do not return text characters anymore */
1347 return event->keysym.sym;
1350 #if ENABLE_UNUSED_CODE
1351 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1352 (int)event->keysym.unicode,
1353 (int)event->keysym.sym,
1354 (int)SDL_GetModState());
1357 if (with_modifiers &&
1358 event->keysym.unicode > 0x0000 &&
1359 event->keysym.unicode < 0x2000)
1360 return event->keysym.unicode;
1362 return event->keysym.sym;
1367 KeyMod HandleKeyModState(Key key, int key_status)
1369 static KeyMod current_modifiers = KMOD_None;
1371 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1373 KeyMod new_modifier = KMOD_None;
1378 new_modifier = KMOD_Shift_L;
1381 new_modifier = KMOD_Shift_R;
1383 case KSYM_Control_L:
1384 new_modifier = KMOD_Control_L;
1386 case KSYM_Control_R:
1387 new_modifier = KMOD_Control_R;
1390 new_modifier = KMOD_Meta_L;
1393 new_modifier = KMOD_Meta_R;
1396 new_modifier = KMOD_Alt_L;
1399 new_modifier = KMOD_Alt_R;
1405 if (key_status == KEY_PRESSED)
1406 current_modifiers |= new_modifier;
1408 current_modifiers &= ~new_modifier;
1411 return current_modifiers;
1414 KeyMod GetKeyModState()
1416 return (KeyMod)SDL_GetModState();
1419 KeyMod GetKeyModStateFromEvents()
1421 /* always use key modifier state as tracked from key events (this is needed
1422 if the modifier key event was injected into the event queue, but the key
1423 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1424 query the keys as held pressed on the keyboard) -- this case is currently
1425 only used to filter out clipboard insert events from "True X-Mouse" tool */
1427 return HandleKeyModState(KSYM_UNDEFINED, 0);
1430 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1432 if (event->type != EVENT_CLIENTMESSAGE)
1435 return TRUE; /* the only possible message here is SDL_QUIT */
1439 /* ========================================================================= */
1440 /* joystick functions */
1441 /* ========================================================================= */
1443 void InitJoysticks()
1447 #if defined(NO_JOYSTICK)
1448 return; /* joysticks generally deactivated by compile-time directive */
1451 /* always start with reliable default values */
1452 joystick.status = JOYSTICK_NOT_AVAILABLE;
1453 for (i = 0; i < MAX_PLAYERS; i++)
1454 joystick.fd[i] = -1; /* joystick device closed */
1459 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1461 return SDLReadJoystick(nr, x, y, b1, b2);