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 SetVideoMode(boolean fullscreen)
805 return SDLSetVideoMode(&backbuffer, fullscreen);
808 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
810 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
811 (!fullscreen && video.fullscreen_enabled))
812 fullscreen = SetVideoMode(fullscreen);
817 Bitmap *LoadImage(char *filename)
821 new_bitmap = SDLLoadImage(filename);
824 new_bitmap->source_filename = getStringCopy(filename);
829 Bitmap *LoadCustomImage(char *basename)
831 char *filename = getCustomImageFilename(basename);
834 if (filename == NULL)
835 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
837 if ((new_bitmap = LoadImage(filename)) == NULL)
838 Error(ERR_EXIT, "LoadImage() failed: %s", GetError());
843 void ReloadCustomImage(Bitmap *bitmap, char *basename)
845 char *filename = getCustomImageFilename(basename);
848 if (filename == NULL) /* (should never happen) */
850 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
854 if (strEqual(filename, bitmap->source_filename))
856 /* The old and new image are the same (have the same filename and path).
857 This usually means that this image does not exist in this graphic set
858 and a fallback to the existing image is done. */
863 if ((new_bitmap = LoadImage(filename)) == NULL)
865 Error(ERR_WARN, "LoadImage() failed: %s", GetError());
869 if (bitmap->width != new_bitmap->width ||
870 bitmap->height != new_bitmap->height)
872 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
874 FreeBitmap(new_bitmap);
878 TransferBitmapPointers(new_bitmap, bitmap);
882 Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
884 Bitmap *dst_bitmap = SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
889 static void SetMaskedBitmapSurface(Bitmap *bitmap)
894 SDL_Surface *surface = bitmap->surface;
896 if (bitmap->surface_masked)
897 SDL_FreeSurface(bitmap->surface_masked);
899 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
900 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
902 if ((bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
903 Error(ERR_EXIT, "SDL_DisplayFormat() failed");
905 SDL_SetColorKey(surface, UNSET_TRANSPARENT_PIXEL, 0);
908 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
910 if (bitmaps[IMG_BITMAP_CUSTOM])
912 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
914 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
917 if (gfx.game_tile_size == gfx.standard_tile_size)
919 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
924 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
925 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
926 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
928 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
930 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
931 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
933 SetMaskedBitmapSurface(bitmap_new);
936 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
937 int tile_size, boolean create_small_bitmaps)
939 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
940 Bitmap *tmp_bitmap_final = NULL;
941 Bitmap *tmp_bitmap_0 = NULL;
942 Bitmap *tmp_bitmap_1 = NULL;
943 Bitmap *tmp_bitmap_2 = NULL;
944 Bitmap *tmp_bitmap_4 = NULL;
945 Bitmap *tmp_bitmap_8 = NULL;
946 Bitmap *tmp_bitmap_16 = NULL;
947 Bitmap *tmp_bitmap_32 = NULL;
948 int width_final, height_final;
949 int width_0, height_0;
950 int width_1, height_1;
951 int width_2, height_2;
952 int width_4, height_4;
953 int width_8, height_8;
954 int width_16, height_16;
955 int width_32, height_32;
956 int old_width, old_height;
959 print_timestamp_init("CreateScaledBitmaps");
961 old_width = old_bitmap->width;
962 old_height = old_bitmap->height;
964 /* calculate new image dimensions for final image size */
965 width_final = old_width * zoom_factor;
966 height_final = old_height * zoom_factor;
968 /* get image with final size (this might require scaling up) */
969 /* ("final" size may result in non-standard tile size image) */
970 if (zoom_factor != 1)
971 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
973 tmp_bitmap_final = old_bitmap;
977 width_0 = width_1 = width_final;
978 height_0 = height_1 = height_final;
980 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
982 if (create_small_bitmaps)
984 /* check if we have a non-gameplay tile size image */
985 if (tile_size != gfx.game_tile_size)
987 /* get image with gameplay tile size */
988 width_0 = width_final * gfx.game_tile_size / tile_size;
989 height_0 = height_final * gfx.game_tile_size / tile_size;
991 if (width_0 == old_width)
992 tmp_bitmap_0 = old_bitmap;
993 else if (width_0 == width_final)
994 tmp_bitmap_0 = tmp_bitmap_final;
996 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1001 /* check if we have a non-standard tile size image */
1002 if (tile_size != gfx.standard_tile_size)
1004 /* get image with standard tile size */
1005 width_1 = width_final * gfx.standard_tile_size / tile_size;
1006 height_1 = height_final * gfx.standard_tile_size / tile_size;
1008 if (width_1 == old_width)
1009 tmp_bitmap_1 = old_bitmap;
1010 else if (width_1 == width_final)
1011 tmp_bitmap_1 = tmp_bitmap_final;
1012 else if (width_1 == width_0)
1013 tmp_bitmap_1 = tmp_bitmap_0;
1015 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1017 UPDATE_BUSY_STATE();
1020 /* calculate new image dimensions for small images */
1021 width_2 = width_1 / 2;
1022 height_2 = height_1 / 2;
1023 width_4 = width_1 / 4;
1024 height_4 = height_1 / 4;
1025 width_8 = width_1 / 8;
1026 height_8 = height_1 / 8;
1027 width_16 = width_1 / 16;
1028 height_16 = height_1 / 16;
1029 width_32 = width_1 / 32;
1030 height_32 = height_1 / 32;
1032 /* get image with 1/2 of normal size (for use in the level editor) */
1033 if (width_2 == old_width)
1034 tmp_bitmap_2 = old_bitmap;
1036 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1038 UPDATE_BUSY_STATE();
1040 /* get image with 1/4 of normal size (for use in the level editor) */
1041 if (width_4 == old_width)
1042 tmp_bitmap_4 = old_bitmap;
1044 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1046 UPDATE_BUSY_STATE();
1048 /* get image with 1/8 of normal size (for use on the preview screen) */
1049 if (width_8 == old_width)
1050 tmp_bitmap_8 = old_bitmap;
1052 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1054 UPDATE_BUSY_STATE();
1056 /* get image with 1/16 of normal size (for use on the preview screen) */
1057 if (width_16 == old_width)
1058 tmp_bitmap_16 = old_bitmap;
1060 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1062 UPDATE_BUSY_STATE();
1064 /* get image with 1/32 of normal size (for use on the preview screen) */
1065 if (width_32 == old_width)
1066 tmp_bitmap_32 = old_bitmap;
1068 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1070 UPDATE_BUSY_STATE();
1072 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1073 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1074 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1075 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1076 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1077 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1079 if (width_0 != width_1)
1080 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1082 if (bitmaps[IMG_BITMAP_CUSTOM])
1083 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1085 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1087 boolean free_old_bitmap = TRUE;
1089 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1090 if (bitmaps[i] == old_bitmap)
1091 free_old_bitmap = FALSE;
1093 if (free_old_bitmap)
1094 FreeBitmap(old_bitmap);
1098 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1101 // create corresponding bitmaps for masked blitting
1102 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1103 if (bitmaps[i] != NULL &&
1104 bitmaps[i] != old_bitmap)
1105 SetMaskedBitmapSurface(bitmaps[i]);
1107 UPDATE_BUSY_STATE();
1109 print_timestamp_done("CreateScaledBitmaps");
1112 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1115 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1118 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1120 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1124 /* ------------------------------------------------------------------------- */
1125 /* mouse pointer functions */
1126 /* ------------------------------------------------------------------------- */
1128 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1130 /* XPM image definitions */
1131 static const char *cursor_image_none[] =
1133 /* width height num_colors chars_per_pixel */
1163 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1164 static const char *cursor_image_dot[] =
1166 /* width height num_colors chars_per_pixel */
1195 static const char **cursor_image_playfield = cursor_image_dot;
1197 /* some people complained about a "white dot" on the screen and thought it
1198 was a graphical error... OK, let's just remove the whole pointer :-) */
1199 static const char **cursor_image_playfield = cursor_image_none;
1202 static const int cursor_bit_order = BIT_ORDER_MSB;
1204 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1206 struct MouseCursorInfo *cursor;
1207 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1208 int header_lines = 4;
1211 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1213 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1216 for (y = 0; y < cursor->width; y++)
1218 for (x = 0; x < cursor->height; x++)
1221 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1226 cursor->data[i] = cursor->mask[i] = 0;
1229 switch (image[header_lines + y][x])
1232 cursor->data[i] |= bit_mask;
1233 cursor->mask[i] |= bit_mask;
1237 cursor->mask[i] |= bit_mask;
1246 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1251 void SetMouseCursor(int mode)
1253 static struct MouseCursorInfo *cursor_none = NULL;
1254 static struct MouseCursorInfo *cursor_playfield = NULL;
1255 struct MouseCursorInfo *cursor_new;
1257 if (cursor_none == NULL)
1258 cursor_none = get_cursor_from_image(cursor_image_none);
1260 if (cursor_playfield == NULL)
1261 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1263 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1264 mode == CURSOR_NONE ? cursor_none :
1265 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1267 SDLSetMouseCursor(cursor_new);
1269 gfx.cursor_mode = mode;
1273 /* ========================================================================= */
1274 /* audio functions */
1275 /* ========================================================================= */
1277 void OpenAudio(void)
1279 /* always start with reliable default values */
1280 audio.sound_available = FALSE;
1281 audio.music_available = FALSE;
1282 audio.loops_available = FALSE;
1284 audio.sound_enabled = FALSE;
1285 audio.sound_deactivated = FALSE;
1287 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1288 audio.mixer_pid = 0;
1289 audio.device_name = NULL;
1290 audio.device_fd = -1;
1292 audio.num_channels = 0;
1293 audio.music_channel = 0;
1294 audio.first_sound_channel = 0;
1299 void CloseAudio(void)
1303 audio.sound_enabled = FALSE;
1306 void SetAudioMode(boolean enabled)
1308 if (!audio.sound_available)
1311 audio.sound_enabled = enabled;
1315 /* ========================================================================= */
1316 /* event functions */
1317 /* ========================================================================= */
1319 void InitEventFilter(EventFilter filter_function)
1321 /* set event filter to filter out certain events */
1322 #if defined(TARGET_SDL2)
1323 SDL_SetEventFilter(filter_function, NULL);
1325 SDL_SetEventFilter(filter_function);
1329 boolean PendingEvent(void)
1331 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1334 void NextEvent(Event *event)
1336 SDLNextEvent(event);
1339 void PeekEvent(Event *event)
1341 #if defined(TARGET_SDL2)
1342 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1344 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1348 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1350 #if defined(TARGET_SDL2)
1351 /* key up/down events in SDL2 do not return text characters anymore */
1352 return event->keysym.sym;
1355 #if ENABLE_UNUSED_CODE
1356 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1357 (int)event->keysym.unicode,
1358 (int)event->keysym.sym,
1359 (int)SDL_GetModState());
1362 if (with_modifiers &&
1363 event->keysym.unicode > 0x0000 &&
1364 event->keysym.unicode < 0x2000)
1365 return event->keysym.unicode;
1367 return event->keysym.sym;
1372 KeyMod HandleKeyModState(Key key, int key_status)
1374 static KeyMod current_modifiers = KMOD_None;
1376 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1378 KeyMod new_modifier = KMOD_None;
1383 new_modifier = KMOD_Shift_L;
1386 new_modifier = KMOD_Shift_R;
1388 case KSYM_Control_L:
1389 new_modifier = KMOD_Control_L;
1391 case KSYM_Control_R:
1392 new_modifier = KMOD_Control_R;
1395 new_modifier = KMOD_Meta_L;
1398 new_modifier = KMOD_Meta_R;
1401 new_modifier = KMOD_Alt_L;
1404 new_modifier = KMOD_Alt_R;
1410 if (key_status == KEY_PRESSED)
1411 current_modifiers |= new_modifier;
1413 current_modifiers &= ~new_modifier;
1416 return current_modifiers;
1419 KeyMod GetKeyModState()
1421 return (KeyMod)SDL_GetModState();
1424 KeyMod GetKeyModStateFromEvents()
1426 /* always use key modifier state as tracked from key events (this is needed
1427 if the modifier key event was injected into the event queue, but the key
1428 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1429 query the keys as held pressed on the keyboard) -- this case is currently
1430 only used to filter out clipboard insert events from "True X-Mouse" tool */
1432 return HandleKeyModState(KSYM_UNDEFINED, 0);
1435 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1437 if (event->type != EVENT_CLIENTMESSAGE)
1440 return TRUE; /* the only possible message here is SDL_QUIT */
1444 /* ========================================================================= */
1445 /* joystick functions */
1446 /* ========================================================================= */
1448 void InitJoysticks()
1452 #if defined(NO_JOYSTICK)
1453 return; /* joysticks generally deactivated by compile-time directive */
1456 /* always start with reliable default values */
1457 joystick.status = JOYSTICK_NOT_AVAILABLE;
1458 for (i = 0; i < MAX_PLAYERS; i++)
1459 joystick.fd[i] = -1; /* joystick device closed */
1464 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1466 return SDLReadJoystick(nr, x, y, b1, b2);