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;
62 /* ========================================================================= */
63 /* init/close functions */
64 /* ========================================================================= */
66 void InitProgramInfo(char *argv0, char *config_filename,
67 char *userdata_subdir, char *userdata_subdir_unix,
68 char *program_title, char *icon_title,
69 char *icon_filename, char *cookie_prefix,
72 program.command_basepath = getBasePath(argv0);
73 program.command_basename = getBaseName(argv0);
75 program.config_filename = config_filename;
77 program.userdata_subdir = userdata_subdir;
78 program.userdata_subdir_unix = userdata_subdir_unix;
79 program.userdata_path = getUserGameDataDir();
81 program.program_title = program_title;
82 program.window_title = "(undefined)";
83 program.icon_title = icon_title;
85 program.icon_filename = icon_filename;
87 program.cookie_prefix = cookie_prefix;
89 program.version_major = VERSION_MAJOR(program_version);
90 program.version_minor = VERSION_MINOR(program_version);
91 program.version_patch = VERSION_PATCH(program_version);
92 program.version_build = VERSION_BUILD(program_version);
93 program.version_ident = program_version;
95 program.log_filename[LOG_OUT_ID] = getLogFilename(LOG_OUT_BASENAME);
96 program.log_filename[LOG_ERR_ID] = getLogFilename(LOG_ERR_BASENAME);
97 program.log_file[LOG_OUT_ID] = program.log_file_default[LOG_OUT_ID] = stdout;
98 program.log_file[LOG_ERR_ID] = program.log_file_default[LOG_ERR_ID] = stderr;
101 void SetWindowTitle()
103 program.window_title = program.window_title_function();
108 void InitWindowTitleFunction(char *(*window_title_function)(void))
110 program.window_title_function = window_title_function;
113 void InitExitMessageFunction(void (*exit_message_function)(char *, va_list))
115 program.exit_message_function = exit_message_function;
118 void InitExitFunction(void (*exit_function)(int))
120 program.exit_function = exit_function;
122 /* set signal handlers to custom exit function */
123 signal(SIGINT, exit_function);
124 signal(SIGTERM, exit_function);
126 /* set exit function to automatically cleanup SDL stuff after exit() */
130 void InitPlatformDependentStuff(void)
132 // this is initialized in GetOptions(), but may already be used before
133 options.verbose = TRUE;
135 #if defined(PLATFORM_MACOSX)
136 updateUserGameDataDir();
141 #if defined(TARGET_SDL2)
142 int sdl_init_flags = SDL_INIT_EVENTS | SDL_INIT_NOPARACHUTE;
144 int sdl_init_flags = SDL_INIT_EVENTTHREAD | SDL_INIT_NOPARACHUTE;
147 if (SDL_Init(sdl_init_flags) < 0)
148 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
153 void ClosePlatformDependentStuff(void)
158 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
159 int real_sx, int real_sy,
160 int full_sxsize, int full_sysize,
161 Bitmap *field_save_buffer)
167 gfx.real_sx = real_sx;
168 gfx.real_sy = real_sy;
169 gfx.full_sxsize = full_sxsize;
170 gfx.full_sysize = full_sysize;
172 gfx.field_save_buffer = field_save_buffer;
174 gfx.drawing_area_changed = FALSE;
176 SetDrawDeactivationMask(REDRAW_NONE); /* do not deactivate drawing */
177 SetDrawBackgroundMask(REDRAW_NONE); /* deactivate masked drawing */
180 void InitGfxTileSizeInfo(int game_tile_size, int standard_tile_size)
182 gfx.game_tile_size = game_tile_size;
183 gfx.standard_tile_size = standard_tile_size;
186 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
194 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
202 void InitGfxDoor3Info(int ex, int ey, int exsize, int eysize)
210 void InitGfxWindowInfo(int win_xsize, int win_ysize)
212 gfx.win_xsize = win_xsize;
213 gfx.win_ysize = win_ysize;
215 gfx.background_bitmap_mask = REDRAW_NONE;
217 ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize, DEFAULT_DEPTH);
220 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
222 /* currently only used by MSDOS code to alloc VRAM buffer, if available */
223 /* 2009-03-24: also (temporarily?) used for overlapping blit workaround */
224 gfx.scrollbuffer_width = scrollbuffer_width;
225 gfx.scrollbuffer_height = scrollbuffer_height;
228 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
230 gfx.clipping_enabled = enabled;
233 gfx.clip_width = width;
234 gfx.clip_height = height;
237 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
239 gfx.draw_busy_anim_function = draw_busy_anim_function;
242 void InitGfxCustomArtworkInfo()
244 gfx.override_level_graphics = FALSE;
245 gfx.override_level_sounds = FALSE;
246 gfx.override_level_music = FALSE;
248 gfx.draw_init_text = TRUE;
251 void InitGfxOtherSettings()
253 gfx.cursor_mode = CURSOR_DEFAULT;
256 void SetDrawDeactivationMask(int draw_deactivation_mask)
258 gfx.draw_deactivation_mask = draw_deactivation_mask;
261 void SetDrawBackgroundMask(int draw_background_mask)
263 gfx.draw_background_mask = draw_background_mask;
266 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
268 if (background_bitmap_tile != NULL)
269 gfx.background_bitmap_mask |= mask;
271 gfx.background_bitmap_mask &= ~mask;
273 if (background_bitmap_tile == NULL) /* empty background requested */
276 if (mask == REDRAW_ALL)
277 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
278 0, 0, video.width, video.height);
279 else if (mask == REDRAW_FIELD)
280 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
281 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
282 else if (mask == REDRAW_DOOR_1)
283 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
284 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
287 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
289 /* remove every mask before setting mask for window */
290 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
291 SetBackgroundBitmap(NULL, 0xffff); /* !!! FIX THIS !!! */
292 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
295 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
297 /* remove window area mask before setting mask for main area */
298 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
299 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
300 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
303 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
305 /* remove window area mask before setting mask for door area */
306 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
307 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
308 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
312 /* ========================================================================= */
313 /* video functions */
314 /* ========================================================================= */
316 inline static int GetRealDepth(int depth)
318 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
321 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
322 int width, int height, Pixel color)
324 SDLFillRectangle(bitmap, x, y, width, height, color);
327 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
328 int src_x, int src_y, int width, int height,
329 int dst_x, int dst_y, int mask_mode)
331 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
332 dst_x, dst_y, mask_mode);
335 void LimitScreenUpdates(boolean enable)
337 SDLLimitScreenUpdates(enable);
340 void InitVideoDisplay(void)
342 SDLInitVideoDisplay();
345 void CloseVideoDisplay(void)
347 KeyboardAutoRepeatOn();
349 SDL_QuitSubSystem(SDL_INIT_VIDEO);
352 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
355 video.height = height;
356 video.depth = GetRealDepth(depth);
358 video.fullscreen_available = FULLSCREEN_STATUS;
359 video.fullscreen_enabled = FALSE;
361 video.window_scaling_available = WINDOW_SCALING_STATUS;
363 SDLInitVideoBuffer(&backbuffer, &window, fullscreen);
365 video.initialized = TRUE;
370 inline static void FreeBitmapPointers(Bitmap *bitmap)
375 SDLFreeBitmapPointers(bitmap);
377 checked_free(bitmap->source_filename);
378 bitmap->source_filename = NULL;
381 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
384 if (src_bitmap == NULL || dst_bitmap == NULL)
387 FreeBitmapPointers(dst_bitmap);
389 *dst_bitmap = *src_bitmap;
392 void FreeBitmap(Bitmap *bitmap)
397 FreeBitmapPointers(bitmap);
402 Bitmap *CreateBitmapStruct(void)
404 return checked_calloc(sizeof(struct SDLSurfaceInfo));
407 Bitmap *CreateBitmap(int width, int height, int depth)
409 Bitmap *new_bitmap = CreateBitmapStruct();
410 int real_width = MAX(1, width); /* prevent zero bitmap width */
411 int real_height = MAX(1, height); /* prevent zero bitmap height */
412 int real_depth = GetRealDepth(depth);
414 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
416 new_bitmap->width = real_width;
417 new_bitmap->height = real_height;
422 void ReCreateBitmap(Bitmap **bitmap, int width, int height, int depth)
424 Bitmap *new_bitmap = CreateBitmap(width, height, depth);
428 *bitmap = new_bitmap;
432 TransferBitmapPointers(new_bitmap, *bitmap);
437 void CloseWindow(DrawWindow *window)
441 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
444 if (draw_mask == REDRAW_NONE)
447 if (draw_mask & REDRAW_ALL)
450 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
453 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
456 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
459 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
465 boolean DrawingDeactivated(int x, int y, int width, int height)
467 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
470 boolean DrawingOnBackground(int x, int y)
472 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
473 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
476 boolean DrawingAreaChanged()
478 int drawing_area_changed = gfx.drawing_area_changed;
480 // reset flag for change of drawing area after querying it
481 gfx.drawing_area_changed = FALSE;
483 return drawing_area_changed;
486 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
487 int *width, int *height, boolean is_dest)
489 int clip_x, clip_y, clip_width, clip_height;
491 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
493 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
494 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
495 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
496 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
502 clip_width = bitmap->width;
503 clip_height = bitmap->height;
506 /* skip if rectangle completely outside bitmap */
508 if (*x + *width <= clip_x ||
509 *y + *height <= clip_y ||
510 *x >= clip_x + clip_width ||
511 *y >= clip_y + clip_height)
514 /* clip if rectangle overlaps bitmap */
518 *width -= clip_x - *x;
521 else if (*x + *width > clip_x + clip_width)
523 *width = clip_x + clip_width - *x;
528 *height -= clip_y - *y;
531 else if (*y + *height > clip_y + clip_height)
533 *height = clip_y + clip_height - *y;
539 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
540 int src_x, int src_y, int width, int height,
541 int dst_x, int dst_y)
543 int dst_x_unclipped = dst_x;
544 int dst_y_unclipped = dst_y;
546 if (src_bitmap == NULL || dst_bitmap == NULL)
549 if (DrawingDeactivated(dst_x, dst_y, width, height))
552 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
553 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
556 /* source x/y might need adjustment if destination x/y was clipped top/left */
557 src_x += dst_x - dst_x_unclipped;
558 src_y += dst_y - dst_y_unclipped;
560 #if defined(TARGET_SDL2)
561 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
562 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
563 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
564 but is already fixed in SVN and should therefore finally be fixed with
565 the next official SDL release, which is probably version 1.2.14.) */
566 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
568 if (src_bitmap == dst_bitmap)
570 /* needed when blitting directly to same bitmap -- should not be needed with
571 recent SDL libraries, but apparently does not work in 1.2.11 directly */
573 static Bitmap *tmp_bitmap = NULL;
574 static int tmp_bitmap_xsize = 0;
575 static int tmp_bitmap_ysize = 0;
577 /* start with largest static bitmaps for initial bitmap size ... */
578 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
580 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
581 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
584 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
585 if (src_bitmap->width > tmp_bitmap_xsize ||
586 src_bitmap->height > tmp_bitmap_ysize)
588 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
589 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
591 FreeBitmap(tmp_bitmap);
596 if (tmp_bitmap == NULL)
597 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
600 sysCopyArea(src_bitmap, tmp_bitmap,
601 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
602 sysCopyArea(tmp_bitmap, dst_bitmap,
603 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
609 sysCopyArea(src_bitmap, dst_bitmap,
610 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
613 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
614 int src_x, int src_y, int src_width, int src_height,
615 int dst_x, int dst_y, int dst_width, int dst_height)
617 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
618 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
619 int dst_xsize = dst_width;
620 int dst_ysize = dst_height;
621 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
622 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
625 for (y = 0; y < src_ysteps; y++)
627 for (x = 0; x < src_xsteps; x++)
629 int draw_x = dst_x + x * src_xsize;
630 int draw_y = dst_y + y * src_ysize;
631 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
632 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
634 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
640 void FadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
641 int fade_mode, int fade_delay, int post_delay,
642 void (*draw_border_function)(void))
644 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
645 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
648 SDLFadeRectangle(bitmap_cross, x, y, width, height,
649 fade_mode, fade_delay, post_delay, draw_border_function);
652 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
655 if (DrawingDeactivated(x, y, width, height))
658 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
661 sysFillRectangle(bitmap, x, y, width, height, color);
664 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
666 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
669 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
670 int width, int height)
672 if (DrawingOnBackground(x, y))
673 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
675 ClearRectangle(bitmap, x, y, width, height);
678 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
679 int src_x, int src_y, int width, int height,
680 int dst_x, int dst_y)
682 if (DrawingDeactivated(dst_x, dst_y, width, height))
685 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
686 dst_x, dst_y, BLIT_MASKED);
689 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
690 int src_x, int src_y, int width, int height,
691 int dst_x, int dst_y)
693 if (DrawingOnBackground(dst_x, dst_y))
695 /* draw background */
696 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
699 /* draw foreground */
700 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
704 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
708 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
711 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
714 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
717 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
720 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
721 int to_x, int to_y, Pixel pixel, int line_width)
725 for (x = 0; x < line_width; x++)
727 for (y = 0; y < line_width; y++)
729 int dx = x - line_width / 2;
730 int dy = y - line_width / 2;
732 if ((x == 0 && y == 0) ||
733 (x == 0 && y == line_width - 1) ||
734 (x == line_width - 1 && y == 0) ||
735 (x == line_width - 1 && y == line_width - 1))
739 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
744 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
749 for (i = 0; i < num_points - 1; i++)
750 DrawLine(bitmap, points[i].x, points[i].y,
751 points[i + 1].x, points[i + 1].y, pixel, line_width);
754 SDLDrawLines(bitmap->surface, points, num_points, pixel);
758 Pixel GetPixel(Bitmap *bitmap, int x, int y)
760 if (x < 0 || x >= bitmap->width ||
761 y < 0 || y >= bitmap->height)
764 return SDLGetPixel(bitmap, x, y);
767 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
768 unsigned int color_g, unsigned int color_b)
770 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
773 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
775 unsigned int color_r = (color >> 16) & 0xff;
776 unsigned int color_g = (color >> 8) & 0xff;
777 unsigned int color_b = (color >> 0) & 0xff;
779 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
782 void KeyboardAutoRepeatOn(void)
784 #if defined(TARGET_SDL2)
785 keyrepeat_status = TRUE;
787 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
788 SDL_DEFAULT_REPEAT_INTERVAL / 2);
789 SDL_EnableUNICODE(1);
793 void KeyboardAutoRepeatOff(void)
795 #if defined(TARGET_SDL2)
796 keyrepeat_status = FALSE;
798 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
799 SDL_EnableUNICODE(0);
803 boolean PointerInWindow(DrawWindow *window)
808 boolean SetVideoMode(boolean fullscreen)
810 return SDLSetVideoMode(&backbuffer, fullscreen);
813 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
815 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
816 (!fullscreen && video.fullscreen_enabled))
817 fullscreen = SetVideoMode(fullscreen);
822 Bitmap *LoadImage(char *filename)
826 new_bitmap = SDLLoadImage(filename);
829 new_bitmap->source_filename = getStringCopy(filename);
834 Bitmap *LoadCustomImage(char *basename)
836 char *filename = getCustomImageFilename(basename);
839 if (filename == NULL)
840 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
842 if ((new_bitmap = LoadImage(filename)) == NULL)
843 Error(ERR_EXIT, "LoadImage() failed: %s", GetError());
848 void ReloadCustomImage(Bitmap *bitmap, char *basename)
850 char *filename = getCustomImageFilename(basename);
853 if (filename == NULL) /* (should never happen) */
855 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
859 if (strEqual(filename, bitmap->source_filename))
861 /* The old and new image are the same (have the same filename and path).
862 This usually means that this image does not exist in this graphic set
863 and a fallback to the existing image is done. */
868 if ((new_bitmap = LoadImage(filename)) == NULL)
870 Error(ERR_WARN, "LoadImage() failed: %s", GetError());
874 if (bitmap->width != new_bitmap->width ||
875 bitmap->height != new_bitmap->height)
877 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
879 FreeBitmap(new_bitmap);
883 TransferBitmapPointers(new_bitmap, bitmap);
887 Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
889 Bitmap *dst_bitmap = SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
894 static void SetMaskedBitmapSurface(Bitmap *bitmap)
899 SDL_Surface *surface = bitmap->surface;
901 if (bitmap->surface_masked)
902 SDL_FreeSurface(bitmap->surface_masked);
904 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
905 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
907 if ((bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
908 Error(ERR_EXIT, "SDL_DisplayFormat() failed");
910 SDL_SetColorKey(surface, UNSET_TRANSPARENT_PIXEL, 0);
913 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
915 if (bitmaps[IMG_BITMAP_CUSTOM])
917 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
919 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
922 if (gfx.game_tile_size == gfx.standard_tile_size)
924 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
929 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
930 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
931 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
933 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
935 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
936 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
938 SetMaskedBitmapSurface(bitmap_new);
941 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
942 int tile_size, boolean create_small_bitmaps)
944 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
945 Bitmap *tmp_bitmap_final = NULL;
946 Bitmap *tmp_bitmap_0 = NULL;
947 Bitmap *tmp_bitmap_1 = NULL;
948 Bitmap *tmp_bitmap_2 = NULL;
949 Bitmap *tmp_bitmap_4 = NULL;
950 Bitmap *tmp_bitmap_8 = NULL;
951 Bitmap *tmp_bitmap_16 = NULL;
952 Bitmap *tmp_bitmap_32 = NULL;
953 int width_final, height_final;
954 int width_0, height_0;
955 int width_1, height_1;
956 int width_2, height_2;
957 int width_4, height_4;
958 int width_8, height_8;
959 int width_16, height_16;
960 int width_32, height_32;
961 int old_width, old_height;
964 print_timestamp_init("CreateScaledBitmaps");
966 old_width = old_bitmap->width;
967 old_height = old_bitmap->height;
969 /* calculate new image dimensions for final image size */
970 width_final = old_width * zoom_factor;
971 height_final = old_height * zoom_factor;
973 /* get image with final size (this might require scaling up) */
974 /* ("final" size may result in non-standard tile size image) */
975 if (zoom_factor != 1)
976 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
978 tmp_bitmap_final = old_bitmap;
982 width_0 = width_1 = width_final;
983 height_0 = height_1 = height_final;
985 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
987 if (create_small_bitmaps)
989 /* check if we have a non-gameplay tile size image */
990 if (tile_size != gfx.game_tile_size)
992 /* get image with gameplay tile size */
993 width_0 = width_final * gfx.game_tile_size / tile_size;
994 height_0 = height_final * gfx.game_tile_size / tile_size;
996 if (width_0 == old_width)
997 tmp_bitmap_0 = old_bitmap;
998 else if (width_0 == width_final)
999 tmp_bitmap_0 = tmp_bitmap_final;
1001 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1003 UPDATE_BUSY_STATE();
1006 /* check if we have a non-standard tile size image */
1007 if (tile_size != gfx.standard_tile_size)
1009 /* get image with standard tile size */
1010 width_1 = width_final * gfx.standard_tile_size / tile_size;
1011 height_1 = height_final * gfx.standard_tile_size / tile_size;
1013 if (width_1 == old_width)
1014 tmp_bitmap_1 = old_bitmap;
1015 else if (width_1 == width_final)
1016 tmp_bitmap_1 = tmp_bitmap_final;
1017 else if (width_1 == width_0)
1018 tmp_bitmap_1 = tmp_bitmap_0;
1020 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1022 UPDATE_BUSY_STATE();
1025 /* calculate new image dimensions for small images */
1026 width_2 = width_1 / 2;
1027 height_2 = height_1 / 2;
1028 width_4 = width_1 / 4;
1029 height_4 = height_1 / 4;
1030 width_8 = width_1 / 8;
1031 height_8 = height_1 / 8;
1032 width_16 = width_1 / 16;
1033 height_16 = height_1 / 16;
1034 width_32 = width_1 / 32;
1035 height_32 = height_1 / 32;
1037 /* get image with 1/2 of normal size (for use in the level editor) */
1038 if (width_2 == old_width)
1039 tmp_bitmap_2 = old_bitmap;
1041 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1043 UPDATE_BUSY_STATE();
1045 /* get image with 1/4 of normal size (for use in the level editor) */
1046 if (width_4 == old_width)
1047 tmp_bitmap_4 = old_bitmap;
1049 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1051 UPDATE_BUSY_STATE();
1053 /* get image with 1/8 of normal size (for use on the preview screen) */
1054 if (width_8 == old_width)
1055 tmp_bitmap_8 = old_bitmap;
1057 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1059 UPDATE_BUSY_STATE();
1061 /* get image with 1/16 of normal size (for use on the preview screen) */
1062 if (width_16 == old_width)
1063 tmp_bitmap_16 = old_bitmap;
1065 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1067 UPDATE_BUSY_STATE();
1069 /* get image with 1/32 of normal size (for use on the preview screen) */
1070 if (width_32 == old_width)
1071 tmp_bitmap_32 = old_bitmap;
1073 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1075 UPDATE_BUSY_STATE();
1077 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1078 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1079 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1080 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1081 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1082 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1084 if (width_0 != width_1)
1085 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1087 if (bitmaps[IMG_BITMAP_CUSTOM])
1088 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1090 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1092 boolean free_old_bitmap = TRUE;
1094 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1095 if (bitmaps[i] == old_bitmap)
1096 free_old_bitmap = FALSE;
1098 if (free_old_bitmap)
1099 FreeBitmap(old_bitmap);
1103 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1106 // create corresponding bitmaps for masked blitting
1107 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1108 if (bitmaps[i] != NULL &&
1109 bitmaps[i] != old_bitmap)
1110 SetMaskedBitmapSurface(bitmaps[i]);
1112 UPDATE_BUSY_STATE();
1114 print_timestamp_done("CreateScaledBitmaps");
1117 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1120 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1123 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1125 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1129 /* ------------------------------------------------------------------------- */
1130 /* mouse pointer functions */
1131 /* ------------------------------------------------------------------------- */
1133 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1135 /* XPM image definitions */
1136 static const char *cursor_image_none[] =
1138 /* width height num_colors chars_per_pixel */
1168 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1169 static const char *cursor_image_dot[] =
1171 /* width height num_colors chars_per_pixel */
1200 static const char **cursor_image_playfield = cursor_image_dot;
1202 /* some people complained about a "white dot" on the screen and thought it
1203 was a graphical error... OK, let's just remove the whole pointer :-) */
1204 static const char **cursor_image_playfield = cursor_image_none;
1207 static const int cursor_bit_order = BIT_ORDER_MSB;
1209 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1211 struct MouseCursorInfo *cursor;
1212 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1213 int header_lines = 4;
1216 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1218 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1221 for (y = 0; y < cursor->width; y++)
1223 for (x = 0; x < cursor->height; x++)
1226 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1231 cursor->data[i] = cursor->mask[i] = 0;
1234 switch (image[header_lines + y][x])
1237 cursor->data[i] |= bit_mask;
1238 cursor->mask[i] |= bit_mask;
1242 cursor->mask[i] |= bit_mask;
1251 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1256 void SetMouseCursor(int mode)
1258 static struct MouseCursorInfo *cursor_none = NULL;
1259 static struct MouseCursorInfo *cursor_playfield = NULL;
1260 struct MouseCursorInfo *cursor_new;
1262 if (cursor_none == NULL)
1263 cursor_none = get_cursor_from_image(cursor_image_none);
1265 if (cursor_playfield == NULL)
1266 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1268 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1269 mode == CURSOR_NONE ? cursor_none :
1270 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1272 SDLSetMouseCursor(cursor_new);
1274 gfx.cursor_mode = mode;
1278 /* ========================================================================= */
1279 /* audio functions */
1280 /* ========================================================================= */
1282 void OpenAudio(void)
1284 /* always start with reliable default values */
1285 audio.sound_available = FALSE;
1286 audio.music_available = FALSE;
1287 audio.loops_available = FALSE;
1289 audio.sound_enabled = FALSE;
1290 audio.sound_deactivated = FALSE;
1292 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1293 audio.mixer_pid = 0;
1294 audio.device_name = NULL;
1295 audio.device_fd = -1;
1297 audio.num_channels = 0;
1298 audio.music_channel = 0;
1299 audio.first_sound_channel = 0;
1304 void CloseAudio(void)
1308 audio.sound_enabled = FALSE;
1311 void SetAudioMode(boolean enabled)
1313 if (!audio.sound_available)
1316 audio.sound_enabled = enabled;
1320 /* ========================================================================= */
1321 /* event functions */
1322 /* ========================================================================= */
1324 void InitEventFilter(EventFilter filter_function)
1326 /* set event filter to filter out certain events */
1327 #if defined(TARGET_SDL2)
1328 SDL_SetEventFilter(filter_function, NULL);
1330 SDL_SetEventFilter(filter_function);
1334 boolean PendingEvent(void)
1336 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1339 void NextEvent(Event *event)
1341 SDLNextEvent(event);
1344 void PeekEvent(Event *event)
1346 #if defined(TARGET_SDL2)
1347 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1349 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1353 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1355 #if defined(TARGET_SDL2)
1356 /* key up/down events in SDL2 do not return text characters anymore */
1357 return event->keysym.sym;
1360 #if ENABLE_UNUSED_CODE
1361 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1362 (int)event->keysym.unicode,
1363 (int)event->keysym.sym,
1364 (int)SDL_GetModState());
1367 if (with_modifiers &&
1368 event->keysym.unicode > 0x0000 &&
1369 event->keysym.unicode < 0x2000)
1370 return event->keysym.unicode;
1372 return event->keysym.sym;
1377 KeyMod HandleKeyModState(Key key, int key_status)
1379 static KeyMod current_modifiers = KMOD_None;
1381 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1383 KeyMod new_modifier = KMOD_None;
1388 new_modifier = KMOD_Shift_L;
1391 new_modifier = KMOD_Shift_R;
1393 case KSYM_Control_L:
1394 new_modifier = KMOD_Control_L;
1396 case KSYM_Control_R:
1397 new_modifier = KMOD_Control_R;
1400 new_modifier = KMOD_Meta_L;
1403 new_modifier = KMOD_Meta_R;
1406 new_modifier = KMOD_Alt_L;
1409 new_modifier = KMOD_Alt_R;
1415 if (key_status == KEY_PRESSED)
1416 current_modifiers |= new_modifier;
1418 current_modifiers &= ~new_modifier;
1421 return current_modifiers;
1424 KeyMod GetKeyModState()
1426 return (KeyMod)SDL_GetModState();
1429 KeyMod GetKeyModStateFromEvents()
1431 /* always use key modifier state as tracked from key events (this is needed
1432 if the modifier key event was injected into the event queue, but the key
1433 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1434 query the keys as held pressed on the keyboard) -- this case is currently
1435 only used to filter out clipboard insert events from "True X-Mouse" tool */
1437 return HandleKeyModState(KSYM_UNDEFINED, 0);
1440 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1442 if (event->type != EVENT_CLIENTMESSAGE)
1445 return TRUE; /* the only possible message here is SDL_QUIT */
1449 /* ========================================================================= */
1450 /* joystick functions */
1451 /* ========================================================================= */
1453 void InitJoysticks()
1457 #if defined(NO_JOYSTICK)
1458 return; /* joysticks generally deactivated by compile-time directive */
1461 /* always start with reliable default values */
1462 joystick.status = JOYSTICK_NOT_AVAILABLE;
1463 for (i = 0; i < MAX_PLAYERS; i++)
1464 joystick.fd[i] = -1; /* joystick device closed */
1469 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1471 return SDLReadJoystick(nr, x, y, b1, b2);