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, char *config_filename,
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.config_filename = config_filename;
78 program.userdata_subdir = userdata_subdir;
79 program.userdata_subdir_unix = userdata_subdir_unix;
80 program.userdata_path = getUserGameDataDir();
82 program.program_title = program_title;
83 program.window_title = "(undefined)";
84 program.icon_title = icon_title;
86 program.sdl_icon_filename = sdl_icon_filename;
88 program.cookie_prefix = cookie_prefix;
90 program.version_major = VERSION_MAJOR(program_version);
91 program.version_minor = VERSION_MINOR(program_version);
92 program.version_patch = VERSION_PATCH(program_version);
93 program.version_build = VERSION_BUILD(program_version);
94 program.version_ident = program_version;
96 program.error_filename = getErrorFilename(ERROR_BASENAME);
97 program.error_file = stderr;
100 void SetWindowTitle()
102 program.window_title = program.window_title_function();
107 void InitWindowTitleFunction(char *(*window_title_function)(void))
109 program.window_title_function = window_title_function;
112 void InitExitMessageFunction(void (*exit_message_function)(char *, va_list))
114 program.exit_message_function = exit_message_function;
117 void InitExitFunction(void (*exit_function)(int))
119 program.exit_function = exit_function;
121 /* set signal handlers to custom exit function */
122 signal(SIGINT, exit_function);
123 signal(SIGTERM, exit_function);
125 /* set exit function to automatically cleanup SDL stuff after exit() */
129 void InitPlatformDependentStuff(void)
131 // this is initialized in GetOptions(), but may already be used before
132 options.verbose = TRUE;
134 #if defined(PLATFORM_MACOSX)
135 updateUserGameDataDir();
140 #if defined(TARGET_SDL2)
141 int sdl_init_flags = SDL_INIT_EVENTS | SDL_INIT_NOPARACHUTE;
143 int sdl_init_flags = SDL_INIT_EVENTTHREAD | SDL_INIT_NOPARACHUTE;
146 if (SDL_Init(sdl_init_flags) < 0)
147 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
152 void ClosePlatformDependentStuff(void)
154 #if defined(PLATFORM_WIN32)
159 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
160 int real_sx, int real_sy,
161 int full_sxsize, int full_sysize,
162 Bitmap *field_save_buffer)
168 gfx.real_sx = real_sx;
169 gfx.real_sy = real_sy;
170 gfx.full_sxsize = full_sxsize;
171 gfx.full_sysize = full_sysize;
173 gfx.field_save_buffer = field_save_buffer;
175 gfx.drawing_area_changed = FALSE;
177 SetDrawDeactivationMask(REDRAW_NONE); /* do not deactivate drawing */
178 SetDrawBackgroundMask(REDRAW_NONE); /* deactivate masked drawing */
181 void InitGfxTileSizeInfo(int game_tile_size, int standard_tile_size)
183 gfx.game_tile_size = game_tile_size;
184 gfx.standard_tile_size = standard_tile_size;
187 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
195 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
203 void InitGfxDoor3Info(int ex, int ey, int exsize, int eysize)
211 void InitGfxWindowInfo(int win_xsize, int win_ysize)
213 gfx.win_xsize = win_xsize;
214 gfx.win_ysize = win_ysize;
216 gfx.background_bitmap_mask = REDRAW_NONE;
218 ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize, DEFAULT_DEPTH);
221 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
223 /* currently only used by MSDOS code to alloc VRAM buffer, if available */
224 /* 2009-03-24: also (temporarily?) used for overlapping blit workaround */
225 gfx.scrollbuffer_width = scrollbuffer_width;
226 gfx.scrollbuffer_height = scrollbuffer_height;
229 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
231 gfx.clipping_enabled = enabled;
234 gfx.clip_width = width;
235 gfx.clip_height = height;
238 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
240 gfx.draw_busy_anim_function = draw_busy_anim_function;
243 void InitGfxCustomArtworkInfo()
245 gfx.override_level_graphics = FALSE;
246 gfx.override_level_sounds = FALSE;
247 gfx.override_level_music = FALSE;
249 gfx.draw_init_text = TRUE;
252 void InitGfxOtherSettings()
254 gfx.cursor_mode = CURSOR_DEFAULT;
257 void SetDrawDeactivationMask(int draw_deactivation_mask)
259 gfx.draw_deactivation_mask = draw_deactivation_mask;
262 void SetDrawBackgroundMask(int draw_background_mask)
264 gfx.draw_background_mask = draw_background_mask;
267 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
269 if (background_bitmap_tile != NULL)
270 gfx.background_bitmap_mask |= mask;
272 gfx.background_bitmap_mask &= ~mask;
274 if (background_bitmap_tile == NULL) /* empty background requested */
277 if (mask == REDRAW_ALL)
278 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
279 0, 0, video.width, video.height);
280 else if (mask == REDRAW_FIELD)
281 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
282 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
283 else if (mask == REDRAW_DOOR_1)
284 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
285 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
288 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
290 /* remove every mask before setting mask for window */
291 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
292 SetBackgroundBitmap(NULL, 0xffff); /* !!! FIX THIS !!! */
293 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
296 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
298 /* remove window area mask before setting mask for main area */
299 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
300 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
301 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
304 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
306 /* remove window area mask before setting mask for door area */
307 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
308 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
309 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
313 /* ========================================================================= */
314 /* video functions */
315 /* ========================================================================= */
317 inline static int GetRealDepth(int depth)
319 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
322 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
323 int width, int height, Pixel color)
325 SDLFillRectangle(bitmap, x, y, width, height, color);
328 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
329 int src_x, int src_y, int width, int height,
330 int dst_x, int dst_y, int mask_mode)
332 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
333 dst_x, dst_y, mask_mode);
336 void LimitScreenUpdates(boolean enable)
338 SDLLimitScreenUpdates(enable);
341 void InitVideoDisplay(void)
343 SDLInitVideoDisplay();
346 void CloseVideoDisplay(void)
348 KeyboardAutoRepeatOn();
350 SDL_QuitSubSystem(SDL_INIT_VIDEO);
353 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
356 video.height = height;
357 video.depth = GetRealDepth(depth);
359 video.fullscreen_available = FULLSCREEN_STATUS;
360 video.fullscreen_enabled = FALSE;
362 video.window_scaling_available = WINDOW_SCALING_STATUS;
364 SDLInitVideoBuffer(&backbuffer, &window, fullscreen);
366 video.initialized = TRUE;
371 inline static void FreeBitmapPointers(Bitmap *bitmap)
376 SDLFreeBitmapPointers(bitmap);
378 checked_free(bitmap->source_filename);
379 bitmap->source_filename = NULL;
382 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
385 if (src_bitmap == NULL || dst_bitmap == NULL)
388 FreeBitmapPointers(dst_bitmap);
390 *dst_bitmap = *src_bitmap;
393 void FreeBitmap(Bitmap *bitmap)
398 FreeBitmapPointers(bitmap);
403 Bitmap *CreateBitmapStruct(void)
405 return checked_calloc(sizeof(struct SDLSurfaceInfo));
408 Bitmap *CreateBitmap(int width, int height, int depth)
410 Bitmap *new_bitmap = CreateBitmapStruct();
411 int real_width = MAX(1, width); /* prevent zero bitmap width */
412 int real_height = MAX(1, height); /* prevent zero bitmap height */
413 int real_depth = GetRealDepth(depth);
415 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
417 new_bitmap->width = real_width;
418 new_bitmap->height = real_height;
423 void ReCreateBitmap(Bitmap **bitmap, int width, int height, int depth)
425 Bitmap *new_bitmap = CreateBitmap(width, height, depth);
429 *bitmap = new_bitmap;
433 TransferBitmapPointers(new_bitmap, *bitmap);
438 void CloseWindow(DrawWindow *window)
442 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
445 if (draw_mask == REDRAW_NONE)
448 if (draw_mask & REDRAW_ALL)
451 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
454 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
457 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
460 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
466 boolean DrawingDeactivated(int x, int y, int width, int height)
468 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
471 boolean DrawingOnBackground(int x, int y)
473 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
474 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
477 boolean DrawingAreaChanged()
479 int drawing_area_changed = gfx.drawing_area_changed;
481 // reset flag for change of drawing area after querying it
482 gfx.drawing_area_changed = FALSE;
484 return drawing_area_changed;
487 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
488 int *width, int *height, boolean is_dest)
490 int clip_x, clip_y, clip_width, clip_height;
492 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
494 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
495 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
496 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
497 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
503 clip_width = bitmap->width;
504 clip_height = bitmap->height;
507 /* skip if rectangle completely outside bitmap */
509 if (*x + *width <= clip_x ||
510 *y + *height <= clip_y ||
511 *x >= clip_x + clip_width ||
512 *y >= clip_y + clip_height)
515 /* clip if rectangle overlaps bitmap */
519 *width -= clip_x - *x;
522 else if (*x + *width > clip_x + clip_width)
524 *width = clip_x + clip_width - *x;
529 *height -= clip_y - *y;
532 else if (*y + *height > clip_y + clip_height)
534 *height = clip_y + clip_height - *y;
540 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
541 int src_x, int src_y, int width, int height,
542 int dst_x, int dst_y)
544 int dst_x_unclipped = dst_x;
545 int dst_y_unclipped = dst_y;
547 if (src_bitmap == NULL || dst_bitmap == NULL)
550 if (DrawingDeactivated(dst_x, dst_y, width, height))
553 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
554 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
557 /* source x/y might need adjustment if destination x/y was clipped top/left */
558 src_x += dst_x - dst_x_unclipped;
559 src_y += dst_y - dst_y_unclipped;
561 #if defined(TARGET_SDL2)
562 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
563 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
564 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
565 but is already fixed in SVN and should therefore finally be fixed with
566 the next official SDL release, which is probably version 1.2.14.) */
567 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
569 if (src_bitmap == dst_bitmap)
571 /* needed when blitting directly to same bitmap -- should not be needed with
572 recent SDL libraries, but apparently does not work in 1.2.11 directly */
574 static Bitmap *tmp_bitmap = NULL;
575 static int tmp_bitmap_xsize = 0;
576 static int tmp_bitmap_ysize = 0;
578 /* start with largest static bitmaps for initial bitmap size ... */
579 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
581 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
582 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
585 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
586 if (src_bitmap->width > tmp_bitmap_xsize ||
587 src_bitmap->height > tmp_bitmap_ysize)
589 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
590 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
592 FreeBitmap(tmp_bitmap);
597 if (tmp_bitmap == NULL)
598 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
601 sysCopyArea(src_bitmap, tmp_bitmap,
602 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
603 sysCopyArea(tmp_bitmap, dst_bitmap,
604 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
610 sysCopyArea(src_bitmap, dst_bitmap,
611 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
614 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
615 int src_x, int src_y, int src_width, int src_height,
616 int dst_x, int dst_y, int dst_width, int dst_height)
618 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
619 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
620 int dst_xsize = dst_width;
621 int dst_ysize = dst_height;
622 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
623 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
626 for (y = 0; y < src_ysteps; y++)
628 for (x = 0; x < src_xsteps; x++)
630 int draw_x = dst_x + x * src_xsize;
631 int draw_y = dst_y + y * src_ysize;
632 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
633 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
635 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
641 void FadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
642 int fade_mode, int fade_delay, int post_delay,
643 void (*draw_border_function)(void))
645 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
646 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
649 SDLFadeRectangle(bitmap_cross, x, y, width, height,
650 fade_mode, fade_delay, post_delay, draw_border_function);
653 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
656 if (DrawingDeactivated(x, y, width, height))
659 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
662 sysFillRectangle(bitmap, x, y, width, height, color);
665 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
667 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
670 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
671 int width, int height)
673 if (DrawingOnBackground(x, y))
674 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
676 ClearRectangle(bitmap, x, y, width, height);
679 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
680 int src_x, int src_y, int width, int height,
681 int dst_x, int dst_y)
683 if (DrawingDeactivated(dst_x, dst_y, width, height))
686 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
687 dst_x, dst_y, BLIT_MASKED);
690 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
691 int src_x, int src_y, int width, int height,
692 int dst_x, int dst_y)
694 if (DrawingOnBackground(dst_x, dst_y))
696 /* draw background */
697 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
700 /* draw foreground */
701 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
705 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
709 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
712 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
715 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
718 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
721 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
722 int to_x, int to_y, Pixel pixel, int line_width)
726 for (x = 0; x < line_width; x++)
728 for (y = 0; y < line_width; y++)
730 int dx = x - line_width / 2;
731 int dy = y - line_width / 2;
733 if ((x == 0 && y == 0) ||
734 (x == 0 && y == line_width - 1) ||
735 (x == line_width - 1 && y == 0) ||
736 (x == line_width - 1 && y == line_width - 1))
740 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
745 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
750 for (i = 0; i < num_points - 1; i++)
751 DrawLine(bitmap, points[i].x, points[i].y,
752 points[i + 1].x, points[i + 1].y, pixel, line_width);
755 SDLDrawLines(bitmap->surface, points, num_points, pixel);
759 Pixel GetPixel(Bitmap *bitmap, int x, int y)
761 if (x < 0 || x >= bitmap->width ||
762 y < 0 || y >= bitmap->height)
765 return SDLGetPixel(bitmap, x, y);
768 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
769 unsigned int color_g, unsigned int color_b)
771 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
774 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
776 unsigned int color_r = (color >> 16) & 0xff;
777 unsigned int color_g = (color >> 8) & 0xff;
778 unsigned int color_b = (color >> 0) & 0xff;
780 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
783 void KeyboardAutoRepeatOn(void)
785 #if defined(TARGET_SDL2)
786 keyrepeat_status = TRUE;
788 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
789 SDL_DEFAULT_REPEAT_INTERVAL / 2);
790 SDL_EnableUNICODE(1);
794 void KeyboardAutoRepeatOff(void)
796 #if defined(TARGET_SDL2)
797 keyrepeat_status = FALSE;
799 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
800 SDL_EnableUNICODE(0);
804 boolean PointerInWindow(DrawWindow *window)
809 boolean SetVideoMode(boolean fullscreen)
811 return SDLSetVideoMode(&backbuffer, fullscreen);
814 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
816 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
817 (!fullscreen && video.fullscreen_enabled))
818 fullscreen = SetVideoMode(fullscreen);
823 Bitmap *LoadImage(char *filename)
827 new_bitmap = SDLLoadImage(filename);
830 new_bitmap->source_filename = getStringCopy(filename);
835 Bitmap *LoadCustomImage(char *basename)
837 char *filename = getCustomImageFilename(basename);
840 if (filename == NULL)
841 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
843 if ((new_bitmap = LoadImage(filename)) == NULL)
844 Error(ERR_EXIT, "LoadImage() failed: %s", GetError());
849 void ReloadCustomImage(Bitmap *bitmap, char *basename)
851 char *filename = getCustomImageFilename(basename);
854 if (filename == NULL) /* (should never happen) */
856 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
860 if (strEqual(filename, bitmap->source_filename))
862 /* The old and new image are the same (have the same filename and path).
863 This usually means that this image does not exist in this graphic set
864 and a fallback to the existing image is done. */
869 if ((new_bitmap = LoadImage(filename)) == NULL)
871 Error(ERR_WARN, "LoadImage() failed: %s", GetError());
875 if (bitmap->width != new_bitmap->width ||
876 bitmap->height != new_bitmap->height)
878 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
880 FreeBitmap(new_bitmap);
884 TransferBitmapPointers(new_bitmap, bitmap);
888 Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
890 Bitmap *dst_bitmap = SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
895 static void SetMaskedBitmapSurface(Bitmap *bitmap)
900 SDL_Surface *surface = bitmap->surface;
902 if (bitmap->surface_masked)
903 SDL_FreeSurface(bitmap->surface_masked);
905 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
906 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
908 if ((bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
909 Error(ERR_EXIT, "SDL_DisplayFormat() failed");
911 SDL_SetColorKey(surface, UNSET_TRANSPARENT_PIXEL, 0);
914 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
916 if (bitmaps[IMG_BITMAP_CUSTOM])
918 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
920 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
923 if (gfx.game_tile_size == gfx.standard_tile_size)
925 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
930 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
931 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
932 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
934 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
936 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
937 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
939 SetMaskedBitmapSurface(bitmap_new);
942 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
943 int tile_size, boolean create_small_bitmaps)
945 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
946 Bitmap *tmp_bitmap_final = NULL;
947 Bitmap *tmp_bitmap_0 = NULL;
948 Bitmap *tmp_bitmap_1 = NULL;
949 Bitmap *tmp_bitmap_2 = NULL;
950 Bitmap *tmp_bitmap_4 = NULL;
951 Bitmap *tmp_bitmap_8 = NULL;
952 Bitmap *tmp_bitmap_16 = NULL;
953 Bitmap *tmp_bitmap_32 = NULL;
954 int width_final, height_final;
955 int width_0, height_0;
956 int width_1, height_1;
957 int width_2, height_2;
958 int width_4, height_4;
959 int width_8, height_8;
960 int width_16, height_16;
961 int width_32, height_32;
962 int old_width, old_height;
965 print_timestamp_init("CreateScaledBitmaps");
967 old_width = old_bitmap->width;
968 old_height = old_bitmap->height;
970 /* calculate new image dimensions for final image size */
971 width_final = old_width * zoom_factor;
972 height_final = old_height * zoom_factor;
974 /* get image with final size (this might require scaling up) */
975 /* ("final" size may result in non-standard tile size image) */
976 if (zoom_factor != 1)
977 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
979 tmp_bitmap_final = old_bitmap;
983 width_0 = width_1 = width_final;
984 height_0 = height_1 = height_final;
986 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
988 if (create_small_bitmaps)
990 /* check if we have a non-gameplay tile size image */
991 if (tile_size != gfx.game_tile_size)
993 /* get image with gameplay tile size */
994 width_0 = width_final * gfx.game_tile_size / tile_size;
995 height_0 = height_final * gfx.game_tile_size / tile_size;
997 if (width_0 == old_width)
998 tmp_bitmap_0 = old_bitmap;
999 else if (width_0 == width_final)
1000 tmp_bitmap_0 = tmp_bitmap_final;
1002 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1004 UPDATE_BUSY_STATE();
1007 /* check if we have a non-standard tile size image */
1008 if (tile_size != gfx.standard_tile_size)
1010 /* get image with standard tile size */
1011 width_1 = width_final * gfx.standard_tile_size / tile_size;
1012 height_1 = height_final * gfx.standard_tile_size / tile_size;
1014 if (width_1 == old_width)
1015 tmp_bitmap_1 = old_bitmap;
1016 else if (width_1 == width_final)
1017 tmp_bitmap_1 = tmp_bitmap_final;
1018 else if (width_1 == width_0)
1019 tmp_bitmap_1 = tmp_bitmap_0;
1021 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1023 UPDATE_BUSY_STATE();
1026 /* calculate new image dimensions for small images */
1027 width_2 = width_1 / 2;
1028 height_2 = height_1 / 2;
1029 width_4 = width_1 / 4;
1030 height_4 = height_1 / 4;
1031 width_8 = width_1 / 8;
1032 height_8 = height_1 / 8;
1033 width_16 = width_1 / 16;
1034 height_16 = height_1 / 16;
1035 width_32 = width_1 / 32;
1036 height_32 = height_1 / 32;
1038 /* get image with 1/2 of normal size (for use in the level editor) */
1039 if (width_2 == old_width)
1040 tmp_bitmap_2 = old_bitmap;
1042 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1044 UPDATE_BUSY_STATE();
1046 /* get image with 1/4 of normal size (for use in the level editor) */
1047 if (width_4 == old_width)
1048 tmp_bitmap_4 = old_bitmap;
1050 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1052 UPDATE_BUSY_STATE();
1054 /* get image with 1/8 of normal size (for use on the preview screen) */
1055 if (width_8 == old_width)
1056 tmp_bitmap_8 = old_bitmap;
1058 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1060 UPDATE_BUSY_STATE();
1062 /* get image with 1/16 of normal size (for use on the preview screen) */
1063 if (width_16 == old_width)
1064 tmp_bitmap_16 = old_bitmap;
1066 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1068 UPDATE_BUSY_STATE();
1070 /* get image with 1/32 of normal size (for use on the preview screen) */
1071 if (width_32 == old_width)
1072 tmp_bitmap_32 = old_bitmap;
1074 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1076 UPDATE_BUSY_STATE();
1078 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1079 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1080 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1081 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1082 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1083 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1085 if (width_0 != width_1)
1086 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1088 if (bitmaps[IMG_BITMAP_CUSTOM])
1089 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1091 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1093 boolean free_old_bitmap = TRUE;
1095 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1096 if (bitmaps[i] == old_bitmap)
1097 free_old_bitmap = FALSE;
1099 if (free_old_bitmap)
1100 FreeBitmap(old_bitmap);
1104 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1107 // create corresponding bitmaps for masked blitting
1108 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1109 if (bitmaps[i] != NULL &&
1110 bitmaps[i] != old_bitmap)
1111 SetMaskedBitmapSurface(bitmaps[i]);
1113 UPDATE_BUSY_STATE();
1115 print_timestamp_done("CreateScaledBitmaps");
1118 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1121 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1124 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1126 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1130 /* ------------------------------------------------------------------------- */
1131 /* mouse pointer functions */
1132 /* ------------------------------------------------------------------------- */
1134 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1136 /* XPM image definitions */
1137 static const char *cursor_image_none[] =
1139 /* width height num_colors chars_per_pixel */
1169 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1170 static const char *cursor_image_dot[] =
1172 /* width height num_colors chars_per_pixel */
1201 static const char **cursor_image_playfield = cursor_image_dot;
1203 /* some people complained about a "white dot" on the screen and thought it
1204 was a graphical error... OK, let's just remove the whole pointer :-) */
1205 static const char **cursor_image_playfield = cursor_image_none;
1208 static const int cursor_bit_order = BIT_ORDER_MSB;
1210 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1212 struct MouseCursorInfo *cursor;
1213 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1214 int header_lines = 4;
1217 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1219 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1222 for (y = 0; y < cursor->width; y++)
1224 for (x = 0; x < cursor->height; x++)
1227 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1232 cursor->data[i] = cursor->mask[i] = 0;
1235 switch (image[header_lines + y][x])
1238 cursor->data[i] |= bit_mask;
1239 cursor->mask[i] |= bit_mask;
1243 cursor->mask[i] |= bit_mask;
1252 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1257 void SetMouseCursor(int mode)
1259 static struct MouseCursorInfo *cursor_none = NULL;
1260 static struct MouseCursorInfo *cursor_playfield = NULL;
1261 struct MouseCursorInfo *cursor_new;
1263 if (cursor_none == NULL)
1264 cursor_none = get_cursor_from_image(cursor_image_none);
1266 if (cursor_playfield == NULL)
1267 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1269 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1270 mode == CURSOR_NONE ? cursor_none :
1271 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1273 SDLSetMouseCursor(cursor_new);
1275 gfx.cursor_mode = mode;
1279 /* ========================================================================= */
1280 /* audio functions */
1281 /* ========================================================================= */
1283 void OpenAudio(void)
1285 /* always start with reliable default values */
1286 audio.sound_available = FALSE;
1287 audio.music_available = FALSE;
1288 audio.loops_available = FALSE;
1290 audio.sound_enabled = FALSE;
1291 audio.sound_deactivated = FALSE;
1293 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1294 audio.mixer_pid = 0;
1295 audio.device_name = NULL;
1296 audio.device_fd = -1;
1298 audio.num_channels = 0;
1299 audio.music_channel = 0;
1300 audio.first_sound_channel = 0;
1305 void CloseAudio(void)
1309 audio.sound_enabled = FALSE;
1312 void SetAudioMode(boolean enabled)
1314 if (!audio.sound_available)
1317 audio.sound_enabled = enabled;
1321 /* ========================================================================= */
1322 /* event functions */
1323 /* ========================================================================= */
1325 void InitEventFilter(EventFilter filter_function)
1327 /* set event filter to filter out certain events */
1328 #if defined(TARGET_SDL2)
1329 SDL_SetEventFilter(filter_function, NULL);
1331 SDL_SetEventFilter(filter_function);
1335 boolean PendingEvent(void)
1337 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1340 void NextEvent(Event *event)
1342 SDLNextEvent(event);
1345 void PeekEvent(Event *event)
1347 #if defined(TARGET_SDL2)
1348 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1350 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1354 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1356 #if defined(TARGET_SDL2)
1357 /* key up/down events in SDL2 do not return text characters anymore */
1358 return event->keysym.sym;
1361 #if ENABLE_UNUSED_CODE
1362 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1363 (int)event->keysym.unicode,
1364 (int)event->keysym.sym,
1365 (int)SDL_GetModState());
1368 if (with_modifiers &&
1369 event->keysym.unicode > 0x0000 &&
1370 event->keysym.unicode < 0x2000)
1371 return event->keysym.unicode;
1373 return event->keysym.sym;
1378 KeyMod HandleKeyModState(Key key, int key_status)
1380 static KeyMod current_modifiers = KMOD_None;
1382 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1384 KeyMod new_modifier = KMOD_None;
1389 new_modifier = KMOD_Shift_L;
1392 new_modifier = KMOD_Shift_R;
1394 case KSYM_Control_L:
1395 new_modifier = KMOD_Control_L;
1397 case KSYM_Control_R:
1398 new_modifier = KMOD_Control_R;
1401 new_modifier = KMOD_Meta_L;
1404 new_modifier = KMOD_Meta_R;
1407 new_modifier = KMOD_Alt_L;
1410 new_modifier = KMOD_Alt_R;
1416 if (key_status == KEY_PRESSED)
1417 current_modifiers |= new_modifier;
1419 current_modifiers &= ~new_modifier;
1422 return current_modifiers;
1425 KeyMod GetKeyModState()
1427 return (KeyMod)SDL_GetModState();
1430 KeyMod GetKeyModStateFromEvents()
1432 /* always use key modifier state as tracked from key events (this is needed
1433 if the modifier key event was injected into the event queue, but the key
1434 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1435 query the keys as held pressed on the keyboard) -- this case is currently
1436 only used to filter out clipboard insert events from "True X-Mouse" tool */
1438 return HandleKeyModState(KSYM_UNDEFINED, 0);
1441 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1443 if (event->type != EVENT_CLIENTMESSAGE)
1446 return TRUE; /* the only possible message here is SDL_QUIT */
1450 /* ========================================================================= */
1451 /* joystick functions */
1452 /* ========================================================================= */
1454 void InitJoysticks()
1458 #if defined(NO_JOYSTICK)
1459 return; /* joysticks generally deactivated by compile-time directive */
1462 /* always start with reliable default values */
1463 joystick.status = JOYSTICK_NOT_AVAILABLE;
1464 for (i = 0; i < MAX_PLAYERS; i++)
1465 joystick.fd[i] = -1; /* joystick device closed */
1470 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1472 return SDLReadJoystick(nr, x, y, b1, b2);