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 SetDrawDeactivationMask(REDRAW_NONE); /* do not deactivate drawing */
174 SetDrawBackgroundMask(REDRAW_NONE); /* deactivate masked drawing */
177 void InitGfxTileSizeInfo(int game_tile_size, int standard_tile_size)
179 gfx.game_tile_size = game_tile_size;
180 gfx.standard_tile_size = standard_tile_size;
183 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
191 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
199 void InitGfxDoor3Info(int ex, int ey, int exsize, int eysize)
207 void InitGfxWindowInfo(int win_xsize, int win_ysize)
209 gfx.win_xsize = win_xsize;
210 gfx.win_ysize = win_ysize;
212 gfx.background_bitmap_mask = REDRAW_NONE;
214 ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize, DEFAULT_DEPTH);
217 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
219 /* currently only used by MSDOS code to alloc VRAM buffer, if available */
220 /* 2009-03-24: also (temporarily?) used for overlapping blit workaround */
221 gfx.scrollbuffer_width = scrollbuffer_width;
222 gfx.scrollbuffer_height = scrollbuffer_height;
225 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
227 gfx.clipping_enabled = enabled;
230 gfx.clip_width = width;
231 gfx.clip_height = height;
234 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
236 gfx.draw_busy_anim_function = draw_busy_anim_function;
239 void InitGfxCustomArtworkInfo()
241 gfx.override_level_graphics = FALSE;
242 gfx.override_level_sounds = FALSE;
243 gfx.override_level_music = FALSE;
245 gfx.draw_init_text = TRUE;
248 void SetDrawDeactivationMask(int draw_deactivation_mask)
250 gfx.draw_deactivation_mask = draw_deactivation_mask;
253 void SetDrawBackgroundMask(int draw_background_mask)
255 gfx.draw_background_mask = draw_background_mask;
258 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
260 if (background_bitmap_tile != NULL)
261 gfx.background_bitmap_mask |= mask;
263 gfx.background_bitmap_mask &= ~mask;
265 if (background_bitmap_tile == NULL) /* empty background requested */
268 if (mask == REDRAW_ALL)
269 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
270 0, 0, video.width, video.height);
271 else if (mask == REDRAW_FIELD)
272 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
273 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
274 else if (mask == REDRAW_DOOR_1)
275 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
276 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
279 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
281 /* remove every mask before setting mask for window */
282 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
283 SetBackgroundBitmap(NULL, 0xffff); /* !!! FIX THIS !!! */
284 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
287 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
289 /* remove window area mask before setting mask for main area */
290 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
291 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
292 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
295 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
297 /* remove window area mask before setting mask for door area */
298 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
299 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
300 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
304 /* ========================================================================= */
305 /* video functions */
306 /* ========================================================================= */
308 inline static int GetRealDepth(int depth)
310 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
313 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
314 int width, int height, Pixel color)
316 SDLFillRectangle(bitmap, x, y, width, height, color);
319 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
320 int src_x, int src_y, int width, int height,
321 int dst_x, int dst_y, int mask_mode)
323 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
324 dst_x, dst_y, mask_mode);
327 void LimitScreenUpdates(boolean enable)
329 SDLLimitScreenUpdates(enable);
332 void InitVideoDisplay(void)
334 SDLInitVideoDisplay();
337 void CloseVideoDisplay(void)
339 KeyboardAutoRepeatOn();
341 SDL_QuitSubSystem(SDL_INIT_VIDEO);
344 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
347 video.height = height;
348 video.depth = GetRealDepth(depth);
350 video.fullscreen_available = FULLSCREEN_STATUS;
351 video.fullscreen_enabled = FALSE;
353 video.window_scaling_available = WINDOW_SCALING_STATUS;
355 SDLInitVideoBuffer(&backbuffer, &window, fullscreen);
360 inline static void FreeBitmapPointers(Bitmap *bitmap)
365 SDLFreeBitmapPointers(bitmap);
367 checked_free(bitmap->source_filename);
368 bitmap->source_filename = NULL;
371 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
374 if (src_bitmap == NULL || dst_bitmap == NULL)
377 FreeBitmapPointers(dst_bitmap);
379 *dst_bitmap = *src_bitmap;
382 void FreeBitmap(Bitmap *bitmap)
387 FreeBitmapPointers(bitmap);
392 Bitmap *CreateBitmapStruct(void)
394 return checked_calloc(sizeof(struct SDLSurfaceInfo));
397 Bitmap *CreateBitmap(int width, int height, int depth)
399 Bitmap *new_bitmap = CreateBitmapStruct();
400 int real_width = MAX(1, width); /* prevent zero bitmap width */
401 int real_height = MAX(1, height); /* prevent zero bitmap height */
402 int real_depth = GetRealDepth(depth);
404 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
406 new_bitmap->width = real_width;
407 new_bitmap->height = real_height;
412 void ReCreateBitmap(Bitmap **bitmap, int width, int height, int depth)
414 Bitmap *new_bitmap = CreateBitmap(width, height, depth);
418 *bitmap = new_bitmap;
422 TransferBitmapPointers(new_bitmap, *bitmap);
427 void CloseWindow(DrawWindow *window)
431 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
434 if (draw_mask == REDRAW_NONE)
437 if (draw_mask & REDRAW_ALL)
440 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
443 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
446 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
449 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
455 boolean DrawingDeactivated(int x, int y, int width, int height)
457 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
460 boolean DrawingOnBackground(int x, int y)
462 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
463 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
466 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
467 int *width, int *height, boolean is_dest)
469 int clip_x, clip_y, clip_width, clip_height;
471 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
473 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
474 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
475 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
476 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
482 clip_width = bitmap->width;
483 clip_height = bitmap->height;
486 /* skip if rectangle completely outside bitmap */
488 if (*x + *width <= clip_x ||
489 *y + *height <= clip_y ||
490 *x >= clip_x + clip_width ||
491 *y >= clip_y + clip_height)
494 /* clip if rectangle overlaps bitmap */
498 *width -= clip_x - *x;
501 else if (*x + *width > clip_x + clip_width)
503 *width = clip_x + clip_width - *x;
508 *height -= clip_y - *y;
511 else if (*y + *height > clip_y + clip_height)
513 *height = clip_y + clip_height - *y;
519 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
520 int src_x, int src_y, int width, int height,
521 int dst_x, int dst_y)
523 int dst_x_unclipped = dst_x;
524 int dst_y_unclipped = dst_y;
526 if (src_bitmap == NULL || dst_bitmap == NULL)
529 if (DrawingDeactivated(dst_x, dst_y, width, height))
532 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
533 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
536 /* source x/y might need adjustment if destination x/y was clipped top/left */
537 src_x += dst_x - dst_x_unclipped;
538 src_y += dst_y - dst_y_unclipped;
540 #if defined(TARGET_SDL2)
541 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
542 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
543 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
544 but is already fixed in SVN and should therefore finally be fixed with
545 the next official SDL release, which is probably version 1.2.14.) */
546 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
548 if (src_bitmap == dst_bitmap)
550 /* needed when blitting directly to same bitmap -- should not be needed with
551 recent SDL libraries, but apparently does not work in 1.2.11 directly */
553 static Bitmap *tmp_bitmap = NULL;
554 static int tmp_bitmap_xsize = 0;
555 static int tmp_bitmap_ysize = 0;
557 /* start with largest static bitmaps for initial bitmap size ... */
558 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
560 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
561 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
564 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
565 if (src_bitmap->width > tmp_bitmap_xsize ||
566 src_bitmap->height > tmp_bitmap_ysize)
568 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
569 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
571 FreeBitmap(tmp_bitmap);
576 if (tmp_bitmap == NULL)
577 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
580 sysCopyArea(src_bitmap, tmp_bitmap,
581 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
582 sysCopyArea(tmp_bitmap, dst_bitmap,
583 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
589 sysCopyArea(src_bitmap, dst_bitmap,
590 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
593 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
594 int src_x, int src_y, int src_width, int src_height,
595 int dst_x, int dst_y, int dst_width, int dst_height)
597 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
598 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
599 int dst_xsize = dst_width;
600 int dst_ysize = dst_height;
601 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
602 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
605 for (y = 0; y < src_ysteps; y++)
607 for (x = 0; x < src_xsteps; x++)
609 int draw_x = dst_x + x * src_xsize;
610 int draw_y = dst_y + y * src_ysize;
611 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
612 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
614 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
620 void FadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
621 int fade_mode, int fade_delay, int post_delay,
622 void (*draw_border_function)(void))
624 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
625 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
628 SDLFadeRectangle(bitmap_cross, x, y, width, height,
629 fade_mode, fade_delay, post_delay, draw_border_function);
632 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
635 if (DrawingDeactivated(x, y, width, height))
638 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
641 sysFillRectangle(bitmap, x, y, width, height, color);
644 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
646 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
649 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
650 int width, int height)
652 if (DrawingOnBackground(x, y))
653 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
655 ClearRectangle(bitmap, x, y, width, height);
658 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
659 int src_x, int src_y, int width, int height,
660 int dst_x, int dst_y)
662 if (DrawingDeactivated(dst_x, dst_y, width, height))
665 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
666 dst_x, dst_y, BLIT_MASKED);
669 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
670 int src_x, int src_y, int width, int height,
671 int dst_x, int dst_y)
673 if (DrawingOnBackground(dst_x, dst_y))
675 /* draw background */
676 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
679 /* draw foreground */
680 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
684 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
688 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
691 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
694 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
697 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
700 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
701 int to_x, int to_y, Pixel pixel, int line_width)
705 for (x = 0; x < line_width; x++)
707 for (y = 0; y < line_width; y++)
709 int dx = x - line_width / 2;
710 int dy = y - line_width / 2;
712 if ((x == 0 && y == 0) ||
713 (x == 0 && y == line_width - 1) ||
714 (x == line_width - 1 && y == 0) ||
715 (x == line_width - 1 && y == line_width - 1))
719 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
724 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
729 for (i = 0; i < num_points - 1; i++)
730 DrawLine(bitmap, points[i].x, points[i].y,
731 points[i + 1].x, points[i + 1].y, pixel, line_width);
734 SDLDrawLines(bitmap->surface, points, num_points, pixel);
738 Pixel GetPixel(Bitmap *bitmap, int x, int y)
740 if (x < 0 || x >= bitmap->width ||
741 y < 0 || y >= bitmap->height)
744 return SDLGetPixel(bitmap, x, y);
747 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
748 unsigned int color_g, unsigned int color_b)
750 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
753 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
755 unsigned int color_r = (color >> 16) & 0xff;
756 unsigned int color_g = (color >> 8) & 0xff;
757 unsigned int color_b = (color >> 0) & 0xff;
759 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
762 void KeyboardAutoRepeatOn(void)
764 #if defined(TARGET_SDL2)
765 keyrepeat_status = TRUE;
767 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
768 SDL_DEFAULT_REPEAT_INTERVAL / 2);
769 SDL_EnableUNICODE(1);
773 void KeyboardAutoRepeatOff(void)
775 #if defined(TARGET_SDL2)
776 keyrepeat_status = FALSE;
778 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
779 SDL_EnableUNICODE(0);
783 boolean PointerInWindow(DrawWindow *window)
788 boolean SetVideoMode(boolean fullscreen)
790 return SDLSetVideoMode(&backbuffer, fullscreen);
793 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
795 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
796 (!fullscreen && video.fullscreen_enabled))
797 fullscreen = SetVideoMode(fullscreen);
802 Bitmap *LoadImage(char *filename)
806 new_bitmap = SDLLoadImage(filename);
809 new_bitmap->source_filename = getStringCopy(filename);
814 Bitmap *LoadCustomImage(char *basename)
816 char *filename = getCustomImageFilename(basename);
819 if (filename == NULL)
820 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
822 if ((new_bitmap = LoadImage(filename)) == NULL)
823 Error(ERR_EXIT, "LoadImage() failed: %s", GetError());
828 void ReloadCustomImage(Bitmap *bitmap, char *basename)
830 char *filename = getCustomImageFilename(basename);
833 if (filename == NULL) /* (should never happen) */
835 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
839 if (strEqual(filename, bitmap->source_filename))
841 /* The old and new image are the same (have the same filename and path).
842 This usually means that this image does not exist in this graphic set
843 and a fallback to the existing image is done. */
848 if ((new_bitmap = LoadImage(filename)) == NULL)
850 Error(ERR_WARN, "LoadImage() failed: %s", GetError());
854 if (bitmap->width != new_bitmap->width ||
855 bitmap->height != new_bitmap->height)
857 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
859 FreeBitmap(new_bitmap);
863 TransferBitmapPointers(new_bitmap, bitmap);
867 Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
869 Bitmap *dst_bitmap = SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
874 static void SetMaskedBitmapSurface(Bitmap *bitmap)
879 SDL_Surface *surface = bitmap->surface;
881 if (bitmap->surface_masked)
882 SDL_FreeSurface(bitmap->surface_masked);
884 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
885 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
887 if ((bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
888 Error(ERR_EXIT, "SDL_DisplayFormat() failed");
890 SDL_SetColorKey(surface, UNSET_TRANSPARENT_PIXEL, 0);
893 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
895 if (bitmaps[IMG_BITMAP_CUSTOM])
897 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
899 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
902 if (gfx.game_tile_size == gfx.standard_tile_size)
904 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
909 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
910 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
911 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
913 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
915 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
916 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
918 SetMaskedBitmapSurface(bitmap_new);
921 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
922 int tile_size, boolean create_small_bitmaps)
924 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
925 Bitmap *tmp_bitmap_final = NULL;
926 Bitmap *tmp_bitmap_0 = NULL;
927 Bitmap *tmp_bitmap_1 = NULL;
928 Bitmap *tmp_bitmap_2 = NULL;
929 Bitmap *tmp_bitmap_4 = NULL;
930 Bitmap *tmp_bitmap_8 = NULL;
931 Bitmap *tmp_bitmap_16 = NULL;
932 Bitmap *tmp_bitmap_32 = NULL;
933 int width_final, height_final;
934 int width_0, height_0;
935 int width_1, height_1;
936 int width_2, height_2;
937 int width_4, height_4;
938 int width_8, height_8;
939 int width_16, height_16;
940 int width_32, height_32;
941 int old_width, old_height;
944 print_timestamp_init("CreateScaledBitmaps");
946 old_width = old_bitmap->width;
947 old_height = old_bitmap->height;
949 /* calculate new image dimensions for final image size */
950 width_final = old_width * zoom_factor;
951 height_final = old_height * zoom_factor;
953 /* get image with final size (this might require scaling up) */
954 /* ("final" size may result in non-standard tile size image) */
955 if (zoom_factor != 1)
956 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
958 tmp_bitmap_final = old_bitmap;
962 width_0 = width_1 = width_final;
963 height_0 = height_1 = height_final;
965 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
967 if (create_small_bitmaps)
969 /* check if we have a non-gameplay tile size image */
970 if (tile_size != gfx.game_tile_size)
972 /* get image with gameplay tile size */
973 width_0 = width_final * gfx.game_tile_size / tile_size;
974 height_0 = height_final * gfx.game_tile_size / tile_size;
976 if (width_0 == old_width)
977 tmp_bitmap_0 = old_bitmap;
978 else if (width_0 == width_final)
979 tmp_bitmap_0 = tmp_bitmap_final;
981 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
986 /* check if we have a non-standard tile size image */
987 if (tile_size != gfx.standard_tile_size)
989 /* get image with standard tile size */
990 width_1 = width_final * gfx.standard_tile_size / tile_size;
991 height_1 = height_final * gfx.standard_tile_size / tile_size;
993 if (width_1 == old_width)
994 tmp_bitmap_1 = old_bitmap;
995 else if (width_1 == width_final)
996 tmp_bitmap_1 = tmp_bitmap_final;
997 else if (width_1 == width_0)
998 tmp_bitmap_1 = tmp_bitmap_0;
1000 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1002 UPDATE_BUSY_STATE();
1005 /* calculate new image dimensions for small images */
1006 width_2 = width_1 / 2;
1007 height_2 = height_1 / 2;
1008 width_4 = width_1 / 4;
1009 height_4 = height_1 / 4;
1010 width_8 = width_1 / 8;
1011 height_8 = height_1 / 8;
1012 width_16 = width_1 / 16;
1013 height_16 = height_1 / 16;
1014 width_32 = width_1 / 32;
1015 height_32 = height_1 / 32;
1017 /* get image with 1/2 of normal size (for use in the level editor) */
1018 if (width_2 == old_width)
1019 tmp_bitmap_2 = old_bitmap;
1021 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1023 UPDATE_BUSY_STATE();
1025 /* get image with 1/4 of normal size (for use in the level editor) */
1026 if (width_4 == old_width)
1027 tmp_bitmap_4 = old_bitmap;
1029 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1031 UPDATE_BUSY_STATE();
1033 /* get image with 1/8 of normal size (for use on the preview screen) */
1034 if (width_8 == old_width)
1035 tmp_bitmap_8 = old_bitmap;
1037 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1039 UPDATE_BUSY_STATE();
1041 /* get image with 1/16 of normal size (for use on the preview screen) */
1042 if (width_16 == old_width)
1043 tmp_bitmap_16 = old_bitmap;
1045 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1047 UPDATE_BUSY_STATE();
1049 /* get image with 1/32 of normal size (for use on the preview screen) */
1050 if (width_32 == old_width)
1051 tmp_bitmap_32 = old_bitmap;
1053 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1055 UPDATE_BUSY_STATE();
1057 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1058 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1059 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1060 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1061 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1062 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1064 if (width_0 != width_1)
1065 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1067 if (bitmaps[IMG_BITMAP_CUSTOM])
1068 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1070 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1072 boolean free_old_bitmap = TRUE;
1074 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1075 if (bitmaps[i] == old_bitmap)
1076 free_old_bitmap = FALSE;
1078 if (free_old_bitmap)
1079 FreeBitmap(old_bitmap);
1083 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1086 // create corresponding bitmaps for masked blitting
1087 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1088 if (bitmaps[i] != NULL &&
1089 bitmaps[i] != old_bitmap)
1090 SetMaskedBitmapSurface(bitmaps[i]);
1092 UPDATE_BUSY_STATE();
1094 print_timestamp_done("CreateScaledBitmaps");
1097 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1100 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1103 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1105 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1109 /* ------------------------------------------------------------------------- */
1110 /* mouse pointer functions */
1111 /* ------------------------------------------------------------------------- */
1113 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1115 /* XPM image definitions */
1116 static const char *cursor_image_none[] =
1118 /* width height num_colors chars_per_pixel */
1148 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1149 static const char *cursor_image_dot[] =
1151 /* width height num_colors chars_per_pixel */
1180 static const char **cursor_image_playfield = cursor_image_dot;
1182 /* some people complained about a "white dot" on the screen and thought it
1183 was a graphical error... OK, let's just remove the whole pointer :-) */
1184 static const char **cursor_image_playfield = cursor_image_none;
1187 static const int cursor_bit_order = BIT_ORDER_MSB;
1189 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1191 struct MouseCursorInfo *cursor;
1192 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1193 int header_lines = 4;
1196 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1198 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1201 for (y = 0; y < cursor->width; y++)
1203 for (x = 0; x < cursor->height; x++)
1206 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1211 cursor->data[i] = cursor->mask[i] = 0;
1214 switch (image[header_lines + y][x])
1217 cursor->data[i] |= bit_mask;
1218 cursor->mask[i] |= bit_mask;
1222 cursor->mask[i] |= bit_mask;
1231 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1236 void SetMouseCursor(int mode)
1238 static struct MouseCursorInfo *cursor_none = NULL;
1239 static struct MouseCursorInfo *cursor_playfield = NULL;
1240 struct MouseCursorInfo *cursor_new;
1242 if (cursor_none == NULL)
1243 cursor_none = get_cursor_from_image(cursor_image_none);
1245 if (cursor_playfield == NULL)
1246 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1248 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1249 mode == CURSOR_NONE ? cursor_none :
1250 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1252 SDLSetMouseCursor(cursor_new);
1256 /* ========================================================================= */
1257 /* audio functions */
1258 /* ========================================================================= */
1260 void OpenAudio(void)
1262 /* always start with reliable default values */
1263 audio.sound_available = FALSE;
1264 audio.music_available = FALSE;
1265 audio.loops_available = FALSE;
1267 audio.sound_enabled = FALSE;
1268 audio.sound_deactivated = FALSE;
1270 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1271 audio.mixer_pid = 0;
1272 audio.device_name = NULL;
1273 audio.device_fd = -1;
1275 audio.num_channels = 0;
1276 audio.music_channel = 0;
1277 audio.first_sound_channel = 0;
1282 void CloseAudio(void)
1286 audio.sound_enabled = FALSE;
1289 void SetAudioMode(boolean enabled)
1291 if (!audio.sound_available)
1294 audio.sound_enabled = enabled;
1298 /* ========================================================================= */
1299 /* event functions */
1300 /* ========================================================================= */
1302 void InitEventFilter(EventFilter filter_function)
1304 /* set event filter to filter out certain events */
1305 #if defined(TARGET_SDL2)
1306 SDL_SetEventFilter(filter_function, NULL);
1308 SDL_SetEventFilter(filter_function);
1312 boolean PendingEvent(void)
1314 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1317 void NextEvent(Event *event)
1319 SDLNextEvent(event);
1322 void PeekEvent(Event *event)
1324 #if defined(TARGET_SDL2)
1325 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1327 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1331 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1333 #if defined(TARGET_SDL2)
1334 /* key up/down events in SDL2 do not return text characters anymore */
1335 return event->keysym.sym;
1338 #if ENABLE_UNUSED_CODE
1339 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1340 (int)event->keysym.unicode,
1341 (int)event->keysym.sym,
1342 (int)SDL_GetModState());
1345 if (with_modifiers &&
1346 event->keysym.unicode > 0x0000 &&
1347 event->keysym.unicode < 0x2000)
1348 return event->keysym.unicode;
1350 return event->keysym.sym;
1355 KeyMod HandleKeyModState(Key key, int key_status)
1357 static KeyMod current_modifiers = KMOD_None;
1359 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1361 KeyMod new_modifier = KMOD_None;
1366 new_modifier = KMOD_Shift_L;
1369 new_modifier = KMOD_Shift_R;
1371 case KSYM_Control_L:
1372 new_modifier = KMOD_Control_L;
1374 case KSYM_Control_R:
1375 new_modifier = KMOD_Control_R;
1378 new_modifier = KMOD_Meta_L;
1381 new_modifier = KMOD_Meta_R;
1384 new_modifier = KMOD_Alt_L;
1387 new_modifier = KMOD_Alt_R;
1393 if (key_status == KEY_PRESSED)
1394 current_modifiers |= new_modifier;
1396 current_modifiers &= ~new_modifier;
1399 return current_modifiers;
1402 KeyMod GetKeyModState()
1404 return (KeyMod)SDL_GetModState();
1407 KeyMod GetKeyModStateFromEvents()
1409 /* always use key modifier state as tracked from key events (this is needed
1410 if the modifier key event was injected into the event queue, but the key
1411 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1412 query the keys as held pressed on the keyboard) -- this case is currently
1413 only used to filter out clipboard insert events from "True X-Mouse" tool */
1415 return HandleKeyModState(KSYM_UNDEFINED, 0);
1418 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1420 if (event->type != EVENT_CLIENTMESSAGE)
1423 return TRUE; /* the only possible message here is SDL_QUIT */
1427 /* ========================================================================= */
1428 /* joystick functions */
1429 /* ========================================================================= */
1431 void InitJoysticks()
1435 #if defined(NO_JOYSTICK)
1436 return; /* joysticks generally deactivated by compile-time directive */
1439 /* always start with reliable default values */
1440 joystick.status = JOYSTICK_NOT_AVAILABLE;
1441 for (i = 0; i < MAX_PLAYERS; i++)
1442 joystick.fd[i] = -1; /* joystick device closed */
1447 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1449 return SDLReadJoystick(nr, x, y, b1, b2);