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 InitGfxOtherSettings()
252 gfx.cursor_mode = CURSOR_DEFAULT;
255 void SetDrawDeactivationMask(int draw_deactivation_mask)
257 gfx.draw_deactivation_mask = draw_deactivation_mask;
260 void SetDrawBackgroundMask(int draw_background_mask)
262 gfx.draw_background_mask = draw_background_mask;
265 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
267 if (background_bitmap_tile != NULL)
268 gfx.background_bitmap_mask |= mask;
270 gfx.background_bitmap_mask &= ~mask;
272 if (background_bitmap_tile == NULL) /* empty background requested */
275 if (mask == REDRAW_ALL)
276 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
277 0, 0, video.width, video.height);
278 else if (mask == REDRAW_FIELD)
279 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
280 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
281 else if (mask == REDRAW_DOOR_1)
282 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
283 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
286 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
288 /* remove every mask before setting mask for window */
289 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
290 SetBackgroundBitmap(NULL, 0xffff); /* !!! FIX THIS !!! */
291 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
294 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
296 /* remove window area mask before setting mask for main area */
297 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
298 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
299 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
302 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
304 /* remove window area mask before setting mask for door area */
305 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
306 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
307 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
311 /* ========================================================================= */
312 /* video functions */
313 /* ========================================================================= */
315 inline static int GetRealDepth(int depth)
317 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
320 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
321 int width, int height, Pixel color)
323 SDLFillRectangle(bitmap, x, y, width, height, color);
326 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
327 int src_x, int src_y, int width, int height,
328 int dst_x, int dst_y, int mask_mode)
330 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
331 dst_x, dst_y, mask_mode);
334 void LimitScreenUpdates(boolean enable)
336 SDLLimitScreenUpdates(enable);
339 void InitVideoDisplay(void)
341 SDLInitVideoDisplay();
344 void CloseVideoDisplay(void)
346 KeyboardAutoRepeatOn();
348 SDL_QuitSubSystem(SDL_INIT_VIDEO);
351 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
354 video.height = height;
355 video.depth = GetRealDepth(depth);
357 video.fullscreen_available = FULLSCREEN_STATUS;
358 video.fullscreen_enabled = FALSE;
360 video.window_scaling_available = WINDOW_SCALING_STATUS;
362 SDLInitVideoBuffer(&backbuffer, &window, fullscreen);
367 inline static void FreeBitmapPointers(Bitmap *bitmap)
372 SDLFreeBitmapPointers(bitmap);
374 checked_free(bitmap->source_filename);
375 bitmap->source_filename = NULL;
378 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
381 if (src_bitmap == NULL || dst_bitmap == NULL)
384 FreeBitmapPointers(dst_bitmap);
386 *dst_bitmap = *src_bitmap;
389 void FreeBitmap(Bitmap *bitmap)
394 FreeBitmapPointers(bitmap);
399 Bitmap *CreateBitmapStruct(void)
401 return checked_calloc(sizeof(struct SDLSurfaceInfo));
404 Bitmap *CreateBitmap(int width, int height, int depth)
406 Bitmap *new_bitmap = CreateBitmapStruct();
407 int real_width = MAX(1, width); /* prevent zero bitmap width */
408 int real_height = MAX(1, height); /* prevent zero bitmap height */
409 int real_depth = GetRealDepth(depth);
411 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
413 new_bitmap->width = real_width;
414 new_bitmap->height = real_height;
419 void ReCreateBitmap(Bitmap **bitmap, int width, int height, int depth)
421 Bitmap *new_bitmap = CreateBitmap(width, height, depth);
425 *bitmap = new_bitmap;
429 TransferBitmapPointers(new_bitmap, *bitmap);
434 void CloseWindow(DrawWindow *window)
438 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
441 if (draw_mask == REDRAW_NONE)
444 if (draw_mask & REDRAW_ALL)
447 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
450 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
453 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
456 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
462 boolean DrawingDeactivated(int x, int y, int width, int height)
464 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
467 boolean DrawingOnBackground(int x, int y)
469 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
470 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
473 boolean DrawingAreaChanged()
475 int drawing_area_changed = gfx.drawing_area_changed;
477 // reset flag for change of drawing area after querying it
478 gfx.drawing_area_changed = FALSE;
480 return drawing_area_changed;
483 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
484 int *width, int *height, boolean is_dest)
486 int clip_x, clip_y, clip_width, clip_height;
488 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
490 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
491 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
492 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
493 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
499 clip_width = bitmap->width;
500 clip_height = bitmap->height;
503 /* skip if rectangle completely outside bitmap */
505 if (*x + *width <= clip_x ||
506 *y + *height <= clip_y ||
507 *x >= clip_x + clip_width ||
508 *y >= clip_y + clip_height)
511 /* clip if rectangle overlaps bitmap */
515 *width -= clip_x - *x;
518 else if (*x + *width > clip_x + clip_width)
520 *width = clip_x + clip_width - *x;
525 *height -= clip_y - *y;
528 else if (*y + *height > clip_y + clip_height)
530 *height = clip_y + clip_height - *y;
536 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
537 int src_x, int src_y, int width, int height,
538 int dst_x, int dst_y)
540 int dst_x_unclipped = dst_x;
541 int dst_y_unclipped = dst_y;
543 if (src_bitmap == NULL || dst_bitmap == NULL)
546 if (DrawingDeactivated(dst_x, dst_y, width, height))
549 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
550 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
553 /* source x/y might need adjustment if destination x/y was clipped top/left */
554 src_x += dst_x - dst_x_unclipped;
555 src_y += dst_y - dst_y_unclipped;
557 #if defined(TARGET_SDL2)
558 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
559 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
560 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
561 but is already fixed in SVN and should therefore finally be fixed with
562 the next official SDL release, which is probably version 1.2.14.) */
563 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
565 if (src_bitmap == dst_bitmap)
567 /* needed when blitting directly to same bitmap -- should not be needed with
568 recent SDL libraries, but apparently does not work in 1.2.11 directly */
570 static Bitmap *tmp_bitmap = NULL;
571 static int tmp_bitmap_xsize = 0;
572 static int tmp_bitmap_ysize = 0;
574 /* start with largest static bitmaps for initial bitmap size ... */
575 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
577 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
578 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
581 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
582 if (src_bitmap->width > tmp_bitmap_xsize ||
583 src_bitmap->height > tmp_bitmap_ysize)
585 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
586 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
588 FreeBitmap(tmp_bitmap);
593 if (tmp_bitmap == NULL)
594 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
597 sysCopyArea(src_bitmap, tmp_bitmap,
598 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
599 sysCopyArea(tmp_bitmap, dst_bitmap,
600 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
606 sysCopyArea(src_bitmap, dst_bitmap,
607 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
610 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
611 int src_x, int src_y, int src_width, int src_height,
612 int dst_x, int dst_y, int dst_width, int dst_height)
614 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
615 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
616 int dst_xsize = dst_width;
617 int dst_ysize = dst_height;
618 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
619 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
622 for (y = 0; y < src_ysteps; y++)
624 for (x = 0; x < src_xsteps; x++)
626 int draw_x = dst_x + x * src_xsize;
627 int draw_y = dst_y + y * src_ysize;
628 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
629 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
631 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
637 void FadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
638 int fade_mode, int fade_delay, int post_delay,
639 void (*draw_border_function)(void))
641 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
642 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
645 SDLFadeRectangle(bitmap_cross, x, y, width, height,
646 fade_mode, fade_delay, post_delay, draw_border_function);
649 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
652 if (DrawingDeactivated(x, y, width, height))
655 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
658 sysFillRectangle(bitmap, x, y, width, height, color);
661 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
663 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
666 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
667 int width, int height)
669 if (DrawingOnBackground(x, y))
670 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
672 ClearRectangle(bitmap, x, y, width, height);
675 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
676 int src_x, int src_y, int width, int height,
677 int dst_x, int dst_y)
679 if (DrawingDeactivated(dst_x, dst_y, width, height))
682 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
683 dst_x, dst_y, BLIT_MASKED);
686 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
687 int src_x, int src_y, int width, int height,
688 int dst_x, int dst_y)
690 if (DrawingOnBackground(dst_x, dst_y))
692 /* draw background */
693 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
696 /* draw foreground */
697 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
701 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
705 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
708 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
711 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
714 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
717 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
718 int to_x, int to_y, Pixel pixel, int line_width)
722 for (x = 0; x < line_width; x++)
724 for (y = 0; y < line_width; y++)
726 int dx = x - line_width / 2;
727 int dy = y - line_width / 2;
729 if ((x == 0 && y == 0) ||
730 (x == 0 && y == line_width - 1) ||
731 (x == line_width - 1 && y == 0) ||
732 (x == line_width - 1 && y == line_width - 1))
736 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
741 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
746 for (i = 0; i < num_points - 1; i++)
747 DrawLine(bitmap, points[i].x, points[i].y,
748 points[i + 1].x, points[i + 1].y, pixel, line_width);
751 SDLDrawLines(bitmap->surface, points, num_points, pixel);
755 Pixel GetPixel(Bitmap *bitmap, int x, int y)
757 if (x < 0 || x >= bitmap->width ||
758 y < 0 || y >= bitmap->height)
761 return SDLGetPixel(bitmap, x, y);
764 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
765 unsigned int color_g, unsigned int color_b)
767 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
770 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
772 unsigned int color_r = (color >> 16) & 0xff;
773 unsigned int color_g = (color >> 8) & 0xff;
774 unsigned int color_b = (color >> 0) & 0xff;
776 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
779 void KeyboardAutoRepeatOn(void)
781 #if defined(TARGET_SDL2)
782 keyrepeat_status = TRUE;
784 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
785 SDL_DEFAULT_REPEAT_INTERVAL / 2);
786 SDL_EnableUNICODE(1);
790 void KeyboardAutoRepeatOff(void)
792 #if defined(TARGET_SDL2)
793 keyrepeat_status = FALSE;
795 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
796 SDL_EnableUNICODE(0);
800 boolean PointerInWindow(DrawWindow *window)
805 boolean SetVideoMode(boolean fullscreen)
807 return SDLSetVideoMode(&backbuffer, fullscreen);
810 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
812 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
813 (!fullscreen && video.fullscreen_enabled))
814 fullscreen = SetVideoMode(fullscreen);
819 Bitmap *LoadImage(char *filename)
823 new_bitmap = SDLLoadImage(filename);
826 new_bitmap->source_filename = getStringCopy(filename);
831 Bitmap *LoadCustomImage(char *basename)
833 char *filename = getCustomImageFilename(basename);
836 if (filename == NULL)
837 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
839 if ((new_bitmap = LoadImage(filename)) == NULL)
840 Error(ERR_EXIT, "LoadImage() failed: %s", GetError());
845 void ReloadCustomImage(Bitmap *bitmap, char *basename)
847 char *filename = getCustomImageFilename(basename);
850 if (filename == NULL) /* (should never happen) */
852 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
856 if (strEqual(filename, bitmap->source_filename))
858 /* The old and new image are the same (have the same filename and path).
859 This usually means that this image does not exist in this graphic set
860 and a fallback to the existing image is done. */
865 if ((new_bitmap = LoadImage(filename)) == NULL)
867 Error(ERR_WARN, "LoadImage() failed: %s", GetError());
871 if (bitmap->width != new_bitmap->width ||
872 bitmap->height != new_bitmap->height)
874 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
876 FreeBitmap(new_bitmap);
880 TransferBitmapPointers(new_bitmap, bitmap);
884 Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
886 Bitmap *dst_bitmap = SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
891 static void SetMaskedBitmapSurface(Bitmap *bitmap)
896 SDL_Surface *surface = bitmap->surface;
898 if (bitmap->surface_masked)
899 SDL_FreeSurface(bitmap->surface_masked);
901 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
902 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
904 if ((bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
905 Error(ERR_EXIT, "SDL_DisplayFormat() failed");
907 SDL_SetColorKey(surface, UNSET_TRANSPARENT_PIXEL, 0);
910 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
912 if (bitmaps[IMG_BITMAP_CUSTOM])
914 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
916 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
919 if (gfx.game_tile_size == gfx.standard_tile_size)
921 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
926 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
927 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
928 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
930 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
932 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
933 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
935 SetMaskedBitmapSurface(bitmap_new);
938 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
939 int tile_size, boolean create_small_bitmaps)
941 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
942 Bitmap *tmp_bitmap_final = NULL;
943 Bitmap *tmp_bitmap_0 = NULL;
944 Bitmap *tmp_bitmap_1 = NULL;
945 Bitmap *tmp_bitmap_2 = NULL;
946 Bitmap *tmp_bitmap_4 = NULL;
947 Bitmap *tmp_bitmap_8 = NULL;
948 Bitmap *tmp_bitmap_16 = NULL;
949 Bitmap *tmp_bitmap_32 = NULL;
950 int width_final, height_final;
951 int width_0, height_0;
952 int width_1, height_1;
953 int width_2, height_2;
954 int width_4, height_4;
955 int width_8, height_8;
956 int width_16, height_16;
957 int width_32, height_32;
958 int old_width, old_height;
961 print_timestamp_init("CreateScaledBitmaps");
963 old_width = old_bitmap->width;
964 old_height = old_bitmap->height;
966 /* calculate new image dimensions for final image size */
967 width_final = old_width * zoom_factor;
968 height_final = old_height * zoom_factor;
970 /* get image with final size (this might require scaling up) */
971 /* ("final" size may result in non-standard tile size image) */
972 if (zoom_factor != 1)
973 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
975 tmp_bitmap_final = old_bitmap;
979 width_0 = width_1 = width_final;
980 height_0 = height_1 = height_final;
982 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
984 if (create_small_bitmaps)
986 /* check if we have a non-gameplay tile size image */
987 if (tile_size != gfx.game_tile_size)
989 /* get image with gameplay tile size */
990 width_0 = width_final * gfx.game_tile_size / tile_size;
991 height_0 = height_final * gfx.game_tile_size / tile_size;
993 if (width_0 == old_width)
994 tmp_bitmap_0 = old_bitmap;
995 else if (width_0 == width_final)
996 tmp_bitmap_0 = tmp_bitmap_final;
998 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1000 UPDATE_BUSY_STATE();
1003 /* check if we have a non-standard tile size image */
1004 if (tile_size != gfx.standard_tile_size)
1006 /* get image with standard tile size */
1007 width_1 = width_final * gfx.standard_tile_size / tile_size;
1008 height_1 = height_final * gfx.standard_tile_size / tile_size;
1010 if (width_1 == old_width)
1011 tmp_bitmap_1 = old_bitmap;
1012 else if (width_1 == width_final)
1013 tmp_bitmap_1 = tmp_bitmap_final;
1014 else if (width_1 == width_0)
1015 tmp_bitmap_1 = tmp_bitmap_0;
1017 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1019 UPDATE_BUSY_STATE();
1022 /* calculate new image dimensions for small images */
1023 width_2 = width_1 / 2;
1024 height_2 = height_1 / 2;
1025 width_4 = width_1 / 4;
1026 height_4 = height_1 / 4;
1027 width_8 = width_1 / 8;
1028 height_8 = height_1 / 8;
1029 width_16 = width_1 / 16;
1030 height_16 = height_1 / 16;
1031 width_32 = width_1 / 32;
1032 height_32 = height_1 / 32;
1034 /* get image with 1/2 of normal size (for use in the level editor) */
1035 if (width_2 == old_width)
1036 tmp_bitmap_2 = old_bitmap;
1038 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1040 UPDATE_BUSY_STATE();
1042 /* get image with 1/4 of normal size (for use in the level editor) */
1043 if (width_4 == old_width)
1044 tmp_bitmap_4 = old_bitmap;
1046 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1048 UPDATE_BUSY_STATE();
1050 /* get image with 1/8 of normal size (for use on the preview screen) */
1051 if (width_8 == old_width)
1052 tmp_bitmap_8 = old_bitmap;
1054 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1056 UPDATE_BUSY_STATE();
1058 /* get image with 1/16 of normal size (for use on the preview screen) */
1059 if (width_16 == old_width)
1060 tmp_bitmap_16 = old_bitmap;
1062 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1064 UPDATE_BUSY_STATE();
1066 /* get image with 1/32 of normal size (for use on the preview screen) */
1067 if (width_32 == old_width)
1068 tmp_bitmap_32 = old_bitmap;
1070 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1072 UPDATE_BUSY_STATE();
1074 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1075 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1076 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1077 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1078 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1079 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1081 if (width_0 != width_1)
1082 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1084 if (bitmaps[IMG_BITMAP_CUSTOM])
1085 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1087 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1089 boolean free_old_bitmap = TRUE;
1091 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1092 if (bitmaps[i] == old_bitmap)
1093 free_old_bitmap = FALSE;
1095 if (free_old_bitmap)
1096 FreeBitmap(old_bitmap);
1100 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1103 // create corresponding bitmaps for masked blitting
1104 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1105 if (bitmaps[i] != NULL &&
1106 bitmaps[i] != old_bitmap)
1107 SetMaskedBitmapSurface(bitmaps[i]);
1109 UPDATE_BUSY_STATE();
1111 print_timestamp_done("CreateScaledBitmaps");
1114 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1117 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1120 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1122 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1126 /* ------------------------------------------------------------------------- */
1127 /* mouse pointer functions */
1128 /* ------------------------------------------------------------------------- */
1130 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1132 /* XPM image definitions */
1133 static const char *cursor_image_none[] =
1135 /* width height num_colors chars_per_pixel */
1165 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1166 static const char *cursor_image_dot[] =
1168 /* width height num_colors chars_per_pixel */
1197 static const char **cursor_image_playfield = cursor_image_dot;
1199 /* some people complained about a "white dot" on the screen and thought it
1200 was a graphical error... OK, let's just remove the whole pointer :-) */
1201 static const char **cursor_image_playfield = cursor_image_none;
1204 static const int cursor_bit_order = BIT_ORDER_MSB;
1206 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1208 struct MouseCursorInfo *cursor;
1209 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1210 int header_lines = 4;
1213 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1215 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1218 for (y = 0; y < cursor->width; y++)
1220 for (x = 0; x < cursor->height; x++)
1223 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1228 cursor->data[i] = cursor->mask[i] = 0;
1231 switch (image[header_lines + y][x])
1234 cursor->data[i] |= bit_mask;
1235 cursor->mask[i] |= bit_mask;
1239 cursor->mask[i] |= bit_mask;
1248 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1253 void SetMouseCursor(int mode)
1255 static struct MouseCursorInfo *cursor_none = NULL;
1256 static struct MouseCursorInfo *cursor_playfield = NULL;
1257 struct MouseCursorInfo *cursor_new;
1259 if (cursor_none == NULL)
1260 cursor_none = get_cursor_from_image(cursor_image_none);
1262 if (cursor_playfield == NULL)
1263 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1265 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1266 mode == CURSOR_NONE ? cursor_none :
1267 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1269 SDLSetMouseCursor(cursor_new);
1271 gfx.cursor_mode = mode;
1275 /* ========================================================================= */
1276 /* audio functions */
1277 /* ========================================================================= */
1279 void OpenAudio(void)
1281 /* always start with reliable default values */
1282 audio.sound_available = FALSE;
1283 audio.music_available = FALSE;
1284 audio.loops_available = FALSE;
1286 audio.sound_enabled = FALSE;
1287 audio.sound_deactivated = FALSE;
1289 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1290 audio.mixer_pid = 0;
1291 audio.device_name = NULL;
1292 audio.device_fd = -1;
1294 audio.num_channels = 0;
1295 audio.music_channel = 0;
1296 audio.first_sound_channel = 0;
1301 void CloseAudio(void)
1305 audio.sound_enabled = FALSE;
1308 void SetAudioMode(boolean enabled)
1310 if (!audio.sound_available)
1313 audio.sound_enabled = enabled;
1317 /* ========================================================================= */
1318 /* event functions */
1319 /* ========================================================================= */
1321 void InitEventFilter(EventFilter filter_function)
1323 /* set event filter to filter out certain events */
1324 #if defined(TARGET_SDL2)
1325 SDL_SetEventFilter(filter_function, NULL);
1327 SDL_SetEventFilter(filter_function);
1331 boolean PendingEvent(void)
1333 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1336 void NextEvent(Event *event)
1338 SDLNextEvent(event);
1341 void PeekEvent(Event *event)
1343 #if defined(TARGET_SDL2)
1344 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1346 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1350 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1352 #if defined(TARGET_SDL2)
1353 /* key up/down events in SDL2 do not return text characters anymore */
1354 return event->keysym.sym;
1357 #if ENABLE_UNUSED_CODE
1358 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1359 (int)event->keysym.unicode,
1360 (int)event->keysym.sym,
1361 (int)SDL_GetModState());
1364 if (with_modifiers &&
1365 event->keysym.unicode > 0x0000 &&
1366 event->keysym.unicode < 0x2000)
1367 return event->keysym.unicode;
1369 return event->keysym.sym;
1374 KeyMod HandleKeyModState(Key key, int key_status)
1376 static KeyMod current_modifiers = KMOD_None;
1378 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1380 KeyMod new_modifier = KMOD_None;
1385 new_modifier = KMOD_Shift_L;
1388 new_modifier = KMOD_Shift_R;
1390 case KSYM_Control_L:
1391 new_modifier = KMOD_Control_L;
1393 case KSYM_Control_R:
1394 new_modifier = KMOD_Control_R;
1397 new_modifier = KMOD_Meta_L;
1400 new_modifier = KMOD_Meta_R;
1403 new_modifier = KMOD_Alt_L;
1406 new_modifier = KMOD_Alt_R;
1412 if (key_status == KEY_PRESSED)
1413 current_modifiers |= new_modifier;
1415 current_modifiers &= ~new_modifier;
1418 return current_modifiers;
1421 KeyMod GetKeyModState()
1423 return (KeyMod)SDL_GetModState();
1426 KeyMod GetKeyModStateFromEvents()
1428 /* always use key modifier state as tracked from key events (this is needed
1429 if the modifier key event was injected into the event queue, but the key
1430 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1431 query the keys as held pressed on the keyboard) -- this case is currently
1432 only used to filter out clipboard insert events from "True X-Mouse" tool */
1434 return HandleKeyModState(KSYM_UNDEFINED, 0);
1437 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1439 if (event->type != EVENT_CLIENTMESSAGE)
1442 return TRUE; /* the only possible message here is SDL_QUIT */
1446 /* ========================================================================= */
1447 /* joystick functions */
1448 /* ========================================================================= */
1450 void InitJoysticks()
1454 #if defined(NO_JOYSTICK)
1455 return; /* joysticks generally deactivated by compile-time directive */
1458 /* always start with reliable default values */
1459 joystick.status = JOYSTICK_NOT_AVAILABLE;
1460 for (i = 0; i < MAX_PLAYERS; i++)
1461 joystick.fd[i] = -1; /* joystick device closed */
1466 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1468 return SDLReadJoystick(nr, x, y, b1, b2);