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);
364 video.initialized = TRUE;
369 inline static void FreeBitmapPointers(Bitmap *bitmap)
374 SDLFreeBitmapPointers(bitmap);
376 checked_free(bitmap->source_filename);
377 bitmap->source_filename = NULL;
380 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
383 if (src_bitmap == NULL || dst_bitmap == NULL)
386 FreeBitmapPointers(dst_bitmap);
388 *dst_bitmap = *src_bitmap;
391 void FreeBitmap(Bitmap *bitmap)
396 FreeBitmapPointers(bitmap);
401 Bitmap *CreateBitmapStruct(void)
403 return checked_calloc(sizeof(struct SDLSurfaceInfo));
406 Bitmap *CreateBitmap(int width, int height, int depth)
408 Bitmap *new_bitmap = CreateBitmapStruct();
409 int real_width = MAX(1, width); /* prevent zero bitmap width */
410 int real_height = MAX(1, height); /* prevent zero bitmap height */
411 int real_depth = GetRealDepth(depth);
413 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
415 new_bitmap->width = real_width;
416 new_bitmap->height = real_height;
421 void ReCreateBitmap(Bitmap **bitmap, int width, int height, int depth)
423 Bitmap *new_bitmap = CreateBitmap(width, height, depth);
427 *bitmap = new_bitmap;
431 TransferBitmapPointers(new_bitmap, *bitmap);
436 void CloseWindow(DrawWindow *window)
440 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
443 if (draw_mask == REDRAW_NONE)
446 if (draw_mask & REDRAW_ALL)
449 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
452 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
455 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
458 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
464 boolean DrawingDeactivated(int x, int y, int width, int height)
466 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
469 boolean DrawingOnBackground(int x, int y)
471 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
472 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
475 boolean DrawingAreaChanged()
477 int drawing_area_changed = gfx.drawing_area_changed;
479 // reset flag for change of drawing area after querying it
480 gfx.drawing_area_changed = FALSE;
482 return drawing_area_changed;
485 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
486 int *width, int *height, boolean is_dest)
488 int clip_x, clip_y, clip_width, clip_height;
490 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
492 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
493 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
494 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
495 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
501 clip_width = bitmap->width;
502 clip_height = bitmap->height;
505 /* skip if rectangle completely outside bitmap */
507 if (*x + *width <= clip_x ||
508 *y + *height <= clip_y ||
509 *x >= clip_x + clip_width ||
510 *y >= clip_y + clip_height)
513 /* clip if rectangle overlaps bitmap */
517 *width -= clip_x - *x;
520 else if (*x + *width > clip_x + clip_width)
522 *width = clip_x + clip_width - *x;
527 *height -= clip_y - *y;
530 else if (*y + *height > clip_y + clip_height)
532 *height = clip_y + clip_height - *y;
538 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
539 int src_x, int src_y, int width, int height,
540 int dst_x, int dst_y)
542 int dst_x_unclipped = dst_x;
543 int dst_y_unclipped = dst_y;
545 if (src_bitmap == NULL || dst_bitmap == NULL)
548 if (DrawingDeactivated(dst_x, dst_y, width, height))
551 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
552 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
555 /* source x/y might need adjustment if destination x/y was clipped top/left */
556 src_x += dst_x - dst_x_unclipped;
557 src_y += dst_y - dst_y_unclipped;
559 #if defined(TARGET_SDL2)
560 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
561 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
562 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
563 but is already fixed in SVN and should therefore finally be fixed with
564 the next official SDL release, which is probably version 1.2.14.) */
565 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
567 if (src_bitmap == dst_bitmap)
569 /* needed when blitting directly to same bitmap -- should not be needed with
570 recent SDL libraries, but apparently does not work in 1.2.11 directly */
572 static Bitmap *tmp_bitmap = NULL;
573 static int tmp_bitmap_xsize = 0;
574 static int tmp_bitmap_ysize = 0;
576 /* start with largest static bitmaps for initial bitmap size ... */
577 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
579 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
580 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
583 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
584 if (src_bitmap->width > tmp_bitmap_xsize ||
585 src_bitmap->height > tmp_bitmap_ysize)
587 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
588 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
590 FreeBitmap(tmp_bitmap);
595 if (tmp_bitmap == NULL)
596 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
599 sysCopyArea(src_bitmap, tmp_bitmap,
600 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
601 sysCopyArea(tmp_bitmap, dst_bitmap,
602 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
608 sysCopyArea(src_bitmap, dst_bitmap,
609 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
612 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
613 int src_x, int src_y, int src_width, int src_height,
614 int dst_x, int dst_y, int dst_width, int dst_height)
616 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
617 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
618 int dst_xsize = dst_width;
619 int dst_ysize = dst_height;
620 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
621 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
624 for (y = 0; y < src_ysteps; y++)
626 for (x = 0; x < src_xsteps; x++)
628 int draw_x = dst_x + x * src_xsize;
629 int draw_y = dst_y + y * src_ysize;
630 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
631 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
633 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
639 void FadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
640 int fade_mode, int fade_delay, int post_delay,
641 void (*draw_border_function)(void))
643 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
644 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
647 SDLFadeRectangle(bitmap_cross, x, y, width, height,
648 fade_mode, fade_delay, post_delay, draw_border_function);
651 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
654 if (DrawingDeactivated(x, y, width, height))
657 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
660 sysFillRectangle(bitmap, x, y, width, height, color);
663 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
665 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
668 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
669 int width, int height)
671 if (DrawingOnBackground(x, y))
672 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
674 ClearRectangle(bitmap, x, y, width, height);
677 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
678 int src_x, int src_y, int width, int height,
679 int dst_x, int dst_y)
681 if (DrawingDeactivated(dst_x, dst_y, width, height))
684 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
685 dst_x, dst_y, BLIT_MASKED);
688 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
689 int src_x, int src_y, int width, int height,
690 int dst_x, int dst_y)
692 if (DrawingOnBackground(dst_x, dst_y))
694 /* draw background */
695 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
698 /* draw foreground */
699 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
703 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
707 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
710 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
713 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
716 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
719 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
720 int to_x, int to_y, Pixel pixel, int line_width)
724 for (x = 0; x < line_width; x++)
726 for (y = 0; y < line_width; y++)
728 int dx = x - line_width / 2;
729 int dy = y - line_width / 2;
731 if ((x == 0 && y == 0) ||
732 (x == 0 && y == line_width - 1) ||
733 (x == line_width - 1 && y == 0) ||
734 (x == line_width - 1 && y == line_width - 1))
738 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
743 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
748 for (i = 0; i < num_points - 1; i++)
749 DrawLine(bitmap, points[i].x, points[i].y,
750 points[i + 1].x, points[i + 1].y, pixel, line_width);
753 SDLDrawLines(bitmap->surface, points, num_points, pixel);
757 Pixel GetPixel(Bitmap *bitmap, int x, int y)
759 if (x < 0 || x >= bitmap->width ||
760 y < 0 || y >= bitmap->height)
763 return SDLGetPixel(bitmap, x, y);
766 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
767 unsigned int color_g, unsigned int color_b)
769 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
772 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
774 unsigned int color_r = (color >> 16) & 0xff;
775 unsigned int color_g = (color >> 8) & 0xff;
776 unsigned int color_b = (color >> 0) & 0xff;
778 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
781 void KeyboardAutoRepeatOn(void)
783 #if defined(TARGET_SDL2)
784 keyrepeat_status = TRUE;
786 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
787 SDL_DEFAULT_REPEAT_INTERVAL / 2);
788 SDL_EnableUNICODE(1);
792 void KeyboardAutoRepeatOff(void)
794 #if defined(TARGET_SDL2)
795 keyrepeat_status = FALSE;
797 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
798 SDL_EnableUNICODE(0);
802 boolean PointerInWindow(DrawWindow *window)
807 boolean SetVideoMode(boolean fullscreen)
809 return SDLSetVideoMode(&backbuffer, fullscreen);
812 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
814 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
815 (!fullscreen && video.fullscreen_enabled))
816 fullscreen = SetVideoMode(fullscreen);
821 Bitmap *LoadImage(char *filename)
825 new_bitmap = SDLLoadImage(filename);
828 new_bitmap->source_filename = getStringCopy(filename);
833 Bitmap *LoadCustomImage(char *basename)
835 char *filename = getCustomImageFilename(basename);
838 if (filename == NULL)
839 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
841 if ((new_bitmap = LoadImage(filename)) == NULL)
842 Error(ERR_EXIT, "LoadImage() failed: %s", GetError());
847 void ReloadCustomImage(Bitmap *bitmap, char *basename)
849 char *filename = getCustomImageFilename(basename);
852 if (filename == NULL) /* (should never happen) */
854 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
858 if (strEqual(filename, bitmap->source_filename))
860 /* The old and new image are the same (have the same filename and path).
861 This usually means that this image does not exist in this graphic set
862 and a fallback to the existing image is done. */
867 if ((new_bitmap = LoadImage(filename)) == NULL)
869 Error(ERR_WARN, "LoadImage() failed: %s", GetError());
873 if (bitmap->width != new_bitmap->width ||
874 bitmap->height != new_bitmap->height)
876 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
878 FreeBitmap(new_bitmap);
882 TransferBitmapPointers(new_bitmap, bitmap);
886 Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
888 Bitmap *dst_bitmap = SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
893 static void SetMaskedBitmapSurface(Bitmap *bitmap)
898 SDL_Surface *surface = bitmap->surface;
900 if (bitmap->surface_masked)
901 SDL_FreeSurface(bitmap->surface_masked);
903 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
904 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
906 if ((bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
907 Error(ERR_EXIT, "SDL_DisplayFormat() failed");
909 SDL_SetColorKey(surface, UNSET_TRANSPARENT_PIXEL, 0);
912 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
914 if (bitmaps[IMG_BITMAP_CUSTOM])
916 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
918 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
921 if (gfx.game_tile_size == gfx.standard_tile_size)
923 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
928 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
929 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
930 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
932 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
934 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
935 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
937 SetMaskedBitmapSurface(bitmap_new);
940 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
941 int tile_size, boolean create_small_bitmaps)
943 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
944 Bitmap *tmp_bitmap_final = NULL;
945 Bitmap *tmp_bitmap_0 = NULL;
946 Bitmap *tmp_bitmap_1 = NULL;
947 Bitmap *tmp_bitmap_2 = NULL;
948 Bitmap *tmp_bitmap_4 = NULL;
949 Bitmap *tmp_bitmap_8 = NULL;
950 Bitmap *tmp_bitmap_16 = NULL;
951 Bitmap *tmp_bitmap_32 = NULL;
952 int width_final, height_final;
953 int width_0, height_0;
954 int width_1, height_1;
955 int width_2, height_2;
956 int width_4, height_4;
957 int width_8, height_8;
958 int width_16, height_16;
959 int width_32, height_32;
960 int old_width, old_height;
963 print_timestamp_init("CreateScaledBitmaps");
965 old_width = old_bitmap->width;
966 old_height = old_bitmap->height;
968 /* calculate new image dimensions for final image size */
969 width_final = old_width * zoom_factor;
970 height_final = old_height * zoom_factor;
972 /* get image with final size (this might require scaling up) */
973 /* ("final" size may result in non-standard tile size image) */
974 if (zoom_factor != 1)
975 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
977 tmp_bitmap_final = old_bitmap;
981 width_0 = width_1 = width_final;
982 height_0 = height_1 = height_final;
984 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
986 if (create_small_bitmaps)
988 /* check if we have a non-gameplay tile size image */
989 if (tile_size != gfx.game_tile_size)
991 /* get image with gameplay tile size */
992 width_0 = width_final * gfx.game_tile_size / tile_size;
993 height_0 = height_final * gfx.game_tile_size / tile_size;
995 if (width_0 == old_width)
996 tmp_bitmap_0 = old_bitmap;
997 else if (width_0 == width_final)
998 tmp_bitmap_0 = tmp_bitmap_final;
1000 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1002 UPDATE_BUSY_STATE();
1005 /* check if we have a non-standard tile size image */
1006 if (tile_size != gfx.standard_tile_size)
1008 /* get image with standard tile size */
1009 width_1 = width_final * gfx.standard_tile_size / tile_size;
1010 height_1 = height_final * gfx.standard_tile_size / tile_size;
1012 if (width_1 == old_width)
1013 tmp_bitmap_1 = old_bitmap;
1014 else if (width_1 == width_final)
1015 tmp_bitmap_1 = tmp_bitmap_final;
1016 else if (width_1 == width_0)
1017 tmp_bitmap_1 = tmp_bitmap_0;
1019 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1021 UPDATE_BUSY_STATE();
1024 /* calculate new image dimensions for small images */
1025 width_2 = width_1 / 2;
1026 height_2 = height_1 / 2;
1027 width_4 = width_1 / 4;
1028 height_4 = height_1 / 4;
1029 width_8 = width_1 / 8;
1030 height_8 = height_1 / 8;
1031 width_16 = width_1 / 16;
1032 height_16 = height_1 / 16;
1033 width_32 = width_1 / 32;
1034 height_32 = height_1 / 32;
1036 /* get image with 1/2 of normal size (for use in the level editor) */
1037 if (width_2 == old_width)
1038 tmp_bitmap_2 = old_bitmap;
1040 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1042 UPDATE_BUSY_STATE();
1044 /* get image with 1/4 of normal size (for use in the level editor) */
1045 if (width_4 == old_width)
1046 tmp_bitmap_4 = old_bitmap;
1048 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1050 UPDATE_BUSY_STATE();
1052 /* get image with 1/8 of normal size (for use on the preview screen) */
1053 if (width_8 == old_width)
1054 tmp_bitmap_8 = old_bitmap;
1056 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1058 UPDATE_BUSY_STATE();
1060 /* get image with 1/16 of normal size (for use on the preview screen) */
1061 if (width_16 == old_width)
1062 tmp_bitmap_16 = old_bitmap;
1064 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1066 UPDATE_BUSY_STATE();
1068 /* get image with 1/32 of normal size (for use on the preview screen) */
1069 if (width_32 == old_width)
1070 tmp_bitmap_32 = old_bitmap;
1072 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1074 UPDATE_BUSY_STATE();
1076 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1077 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1078 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1079 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1080 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1081 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1083 if (width_0 != width_1)
1084 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1086 if (bitmaps[IMG_BITMAP_CUSTOM])
1087 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1089 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1091 boolean free_old_bitmap = TRUE;
1093 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1094 if (bitmaps[i] == old_bitmap)
1095 free_old_bitmap = FALSE;
1097 if (free_old_bitmap)
1098 FreeBitmap(old_bitmap);
1102 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1105 // create corresponding bitmaps for masked blitting
1106 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1107 if (bitmaps[i] != NULL &&
1108 bitmaps[i] != old_bitmap)
1109 SetMaskedBitmapSurface(bitmaps[i]);
1111 UPDATE_BUSY_STATE();
1113 print_timestamp_done("CreateScaledBitmaps");
1116 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1119 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1122 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1124 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1128 /* ------------------------------------------------------------------------- */
1129 /* mouse pointer functions */
1130 /* ------------------------------------------------------------------------- */
1132 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1134 /* XPM image definitions */
1135 static const char *cursor_image_none[] =
1137 /* width height num_colors chars_per_pixel */
1167 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1168 static const char *cursor_image_dot[] =
1170 /* width height num_colors chars_per_pixel */
1199 static const char **cursor_image_playfield = cursor_image_dot;
1201 /* some people complained about a "white dot" on the screen and thought it
1202 was a graphical error... OK, let's just remove the whole pointer :-) */
1203 static const char **cursor_image_playfield = cursor_image_none;
1206 static const int cursor_bit_order = BIT_ORDER_MSB;
1208 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1210 struct MouseCursorInfo *cursor;
1211 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1212 int header_lines = 4;
1215 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1217 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1220 for (y = 0; y < cursor->width; y++)
1222 for (x = 0; x < cursor->height; x++)
1225 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1230 cursor->data[i] = cursor->mask[i] = 0;
1233 switch (image[header_lines + y][x])
1236 cursor->data[i] |= bit_mask;
1237 cursor->mask[i] |= bit_mask;
1241 cursor->mask[i] |= bit_mask;
1250 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1255 void SetMouseCursor(int mode)
1257 static struct MouseCursorInfo *cursor_none = NULL;
1258 static struct MouseCursorInfo *cursor_playfield = NULL;
1259 struct MouseCursorInfo *cursor_new;
1261 if (cursor_none == NULL)
1262 cursor_none = get_cursor_from_image(cursor_image_none);
1264 if (cursor_playfield == NULL)
1265 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1267 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1268 mode == CURSOR_NONE ? cursor_none :
1269 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1271 SDLSetMouseCursor(cursor_new);
1273 gfx.cursor_mode = mode;
1277 /* ========================================================================= */
1278 /* audio functions */
1279 /* ========================================================================= */
1281 void OpenAudio(void)
1283 /* always start with reliable default values */
1284 audio.sound_available = FALSE;
1285 audio.music_available = FALSE;
1286 audio.loops_available = FALSE;
1288 audio.sound_enabled = FALSE;
1289 audio.sound_deactivated = FALSE;
1291 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1292 audio.mixer_pid = 0;
1293 audio.device_name = NULL;
1294 audio.device_fd = -1;
1296 audio.num_channels = 0;
1297 audio.music_channel = 0;
1298 audio.first_sound_channel = 0;
1303 void CloseAudio(void)
1307 audio.sound_enabled = FALSE;
1310 void SetAudioMode(boolean enabled)
1312 if (!audio.sound_available)
1315 audio.sound_enabled = enabled;
1319 /* ========================================================================= */
1320 /* event functions */
1321 /* ========================================================================= */
1323 void InitEventFilter(EventFilter filter_function)
1325 /* set event filter to filter out certain events */
1326 #if defined(TARGET_SDL2)
1327 SDL_SetEventFilter(filter_function, NULL);
1329 SDL_SetEventFilter(filter_function);
1333 boolean PendingEvent(void)
1335 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1338 void NextEvent(Event *event)
1340 SDLNextEvent(event);
1343 void PeekEvent(Event *event)
1345 #if defined(TARGET_SDL2)
1346 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1348 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1352 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1354 #if defined(TARGET_SDL2)
1355 /* key up/down events in SDL2 do not return text characters anymore */
1356 return event->keysym.sym;
1359 #if ENABLE_UNUSED_CODE
1360 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1361 (int)event->keysym.unicode,
1362 (int)event->keysym.sym,
1363 (int)SDL_GetModState());
1366 if (with_modifiers &&
1367 event->keysym.unicode > 0x0000 &&
1368 event->keysym.unicode < 0x2000)
1369 return event->keysym.unicode;
1371 return event->keysym.sym;
1376 KeyMod HandleKeyModState(Key key, int key_status)
1378 static KeyMod current_modifiers = KMOD_None;
1380 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1382 KeyMod new_modifier = KMOD_None;
1387 new_modifier = KMOD_Shift_L;
1390 new_modifier = KMOD_Shift_R;
1392 case KSYM_Control_L:
1393 new_modifier = KMOD_Control_L;
1395 case KSYM_Control_R:
1396 new_modifier = KMOD_Control_R;
1399 new_modifier = KMOD_Meta_L;
1402 new_modifier = KMOD_Meta_R;
1405 new_modifier = KMOD_Alt_L;
1408 new_modifier = KMOD_Alt_R;
1414 if (key_status == KEY_PRESSED)
1415 current_modifiers |= new_modifier;
1417 current_modifiers &= ~new_modifier;
1420 return current_modifiers;
1423 KeyMod GetKeyModState()
1425 return (KeyMod)SDL_GetModState();
1428 KeyMod GetKeyModStateFromEvents()
1430 /* always use key modifier state as tracked from key events (this is needed
1431 if the modifier key event was injected into the event queue, but the key
1432 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1433 query the keys as held pressed on the keyboard) -- this case is currently
1434 only used to filter out clipboard insert events from "True X-Mouse" tool */
1436 return HandleKeyModState(KSYM_UNDEFINED, 0);
1439 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1441 if (event->type != EVENT_CLIENTMESSAGE)
1444 return TRUE; /* the only possible message here is SDL_QUIT */
1448 /* ========================================================================= */
1449 /* joystick functions */
1450 /* ========================================================================= */
1452 void InitJoysticks()
1456 #if defined(NO_JOYSTICK)
1457 return; /* joysticks generally deactivated by compile-time directive */
1460 /* always start with reliable default values */
1461 joystick.status = JOYSTICK_NOT_AVAILABLE;
1462 for (i = 0; i < MAX_PLAYERS; i++)
1463 joystick.fd[i] = -1; /* joystick device closed */
1468 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1470 return SDLReadJoystick(nr, x, y, b1, b2);