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 Display *display = NULL;
48 Visual *visual = NULL;
52 DrawWindow *window = NULL;
53 DrawBuffer *backbuffer = NULL;
54 DrawBuffer *drawto = NULL;
56 int button_status = MB_NOT_PRESSED;
57 boolean motion_status = FALSE;
58 #if defined(TARGET_SDL2)
59 boolean keyrepeat_status = TRUE;
62 int redraw_mask = REDRAW_NONE;
68 /* ========================================================================= */
69 /* init/close functions */
70 /* ========================================================================= */
72 void InitProgramInfo(char *argv0,
73 char *userdata_subdir, char *userdata_subdir_unix,
74 char *program_title, char *icon_title,
75 char *sdl_icon_filename, char *cookie_prefix,
78 program.command_basepath = getBasePath(argv0);
79 program.command_basename = getBaseName(argv0);
81 program.userdata_subdir = userdata_subdir;
82 program.userdata_subdir_unix = userdata_subdir_unix;
83 program.userdata_path = getUserGameDataDir();
85 program.program_title = program_title;
86 program.window_title = "(undefined)";
87 program.icon_title = icon_title;
89 program.sdl_icon_filename = sdl_icon_filename;
91 program.cookie_prefix = cookie_prefix;
93 program.version_major = VERSION_MAJOR(program_version);
94 program.version_minor = VERSION_MINOR(program_version);
95 program.version_patch = VERSION_PATCH(program_version);
96 program.version_build = VERSION_BUILD(program_version);
97 program.version_ident = program_version;
99 program.error_filename = getErrorFilename(ERROR_BASENAME);
100 program.error_file = stderr;
103 void SetWindowTitle()
105 program.window_title = program.window_title_function();
110 void InitWindowTitleFunction(char *(*window_title_function)(void))
112 program.window_title_function = window_title_function;
115 void InitExitMessageFunction(void (*exit_message_function)(char *, va_list))
117 program.exit_message_function = exit_message_function;
120 void InitExitFunction(void (*exit_function)(int))
122 program.exit_function = exit_function;
124 /* set signal handlers to custom exit function */
125 signal(SIGINT, exit_function);
126 signal(SIGTERM, exit_function);
128 /* set exit function to automatically cleanup SDL stuff after exit() */
132 void InitPlatformDependentStuff(void)
134 // this is initialized in GetOptions(), but may already be used before
135 options.verbose = TRUE;
137 #if defined(PLATFORM_MACOSX)
138 updateUserGameDataDir();
143 #if defined(TARGET_SDL2)
144 int sdl_init_flags = SDL_INIT_EVENTS | SDL_INIT_NOPARACHUTE;
146 int sdl_init_flags = SDL_INIT_EVENTTHREAD | SDL_INIT_NOPARACHUTE;
149 if (SDL_Init(sdl_init_flags) < 0)
150 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
155 void ClosePlatformDependentStuff(void)
157 #if defined(PLATFORM_WIN32)
162 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
163 int real_sx, int real_sy,
164 int full_sxsize, int full_sysize,
165 Bitmap *field_save_buffer)
171 gfx.real_sx = real_sx;
172 gfx.real_sy = real_sy;
173 gfx.full_sxsize = full_sxsize;
174 gfx.full_sysize = full_sysize;
176 gfx.field_save_buffer = field_save_buffer;
178 SetDrawDeactivationMask(REDRAW_NONE); /* do not deactivate drawing */
179 SetDrawBackgroundMask(REDRAW_NONE); /* deactivate masked drawing */
182 void InitGfxTileSizeInfo(int game_tile_size, int standard_tile_size)
184 gfx.game_tile_size = game_tile_size;
185 gfx.standard_tile_size = standard_tile_size;
188 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
196 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
204 void InitGfxDoor3Info(int ex, int ey, int exsize, int eysize)
212 void InitGfxWindowInfo(int win_xsize, int win_ysize)
214 gfx.win_xsize = win_xsize;
215 gfx.win_ysize = win_ysize;
217 gfx.background_bitmap_mask = REDRAW_NONE;
219 ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize, DEFAULT_DEPTH);
222 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
224 /* currently only used by MSDOS code to alloc VRAM buffer, if available */
225 /* 2009-03-24: also (temporarily?) used for overlapping blit workaround */
226 gfx.scrollbuffer_width = scrollbuffer_width;
227 gfx.scrollbuffer_height = scrollbuffer_height;
230 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
232 gfx.clipping_enabled = enabled;
235 gfx.clip_width = width;
236 gfx.clip_height = height;
239 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
241 gfx.draw_busy_anim_function = draw_busy_anim_function;
244 void InitGfxCustomArtworkInfo()
246 gfx.override_level_graphics = FALSE;
247 gfx.override_level_sounds = FALSE;
248 gfx.override_level_music = FALSE;
250 gfx.draw_init_text = TRUE;
253 void SetDrawDeactivationMask(int draw_deactivation_mask)
255 gfx.draw_deactivation_mask = draw_deactivation_mask;
258 void SetDrawBackgroundMask(int draw_background_mask)
260 gfx.draw_background_mask = draw_background_mask;
263 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
265 if (background_bitmap_tile != NULL)
266 gfx.background_bitmap_mask |= mask;
268 gfx.background_bitmap_mask &= ~mask;
270 if (background_bitmap_tile == NULL) /* empty background requested */
273 if (mask == REDRAW_ALL)
274 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
275 0, 0, video.width, video.height);
276 else if (mask == REDRAW_FIELD)
277 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
278 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
279 else if (mask == REDRAW_DOOR_1)
280 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
281 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
284 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
286 /* remove every mask before setting mask for window */
287 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
288 SetBackgroundBitmap(NULL, 0xffff); /* !!! FIX THIS !!! */
289 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
292 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
294 /* remove window area mask before setting mask for main area */
295 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
296 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
297 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
300 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
302 /* remove window area mask before setting mask for door area */
303 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
304 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
305 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
309 /* ========================================================================= */
310 /* video functions */
311 /* ========================================================================= */
313 inline static int GetRealDepth(int depth)
315 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
318 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
319 int width, int height, Pixel color)
321 SDLFillRectangle(bitmap, x, y, width, height, color);
324 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
325 int src_x, int src_y, int width, int height,
326 int dst_x, int dst_y, int mask_mode)
328 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
329 dst_x, dst_y, mask_mode);
332 void LimitScreenUpdates(boolean enable)
334 SDLLimitScreenUpdates(enable);
337 void InitVideoDisplay(void)
339 SDLInitVideoDisplay();
342 void CloseVideoDisplay(void)
344 KeyboardAutoRepeatOn();
346 SDL_QuitSubSystem(SDL_INIT_VIDEO);
349 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
352 video.height = height;
353 video.depth = GetRealDepth(depth);
355 video.fullscreen_available = FULLSCREEN_STATUS;
356 video.fullscreen_enabled = FALSE;
358 video.window_scaling_available = WINDOW_SCALING_STATUS;
360 SDLInitVideoBuffer(&backbuffer, &window, fullscreen);
365 inline static void FreeBitmapPointers(Bitmap *bitmap)
370 SDLFreeBitmapPointers(bitmap);
372 checked_free(bitmap->source_filename);
373 bitmap->source_filename = NULL;
376 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
379 if (src_bitmap == NULL || dst_bitmap == NULL)
382 FreeBitmapPointers(dst_bitmap);
384 *dst_bitmap = *src_bitmap;
387 void FreeBitmap(Bitmap *bitmap)
392 FreeBitmapPointers(bitmap);
397 Bitmap *CreateBitmapStruct(void)
399 return checked_calloc(sizeof(struct SDLSurfaceInfo));
402 Bitmap *CreateBitmap(int width, int height, int depth)
404 Bitmap *new_bitmap = CreateBitmapStruct();
405 int real_width = MAX(1, width); /* prevent zero bitmap width */
406 int real_height = MAX(1, height); /* prevent zero bitmap height */
407 int real_depth = GetRealDepth(depth);
409 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
411 new_bitmap->width = real_width;
412 new_bitmap->height = real_height;
417 void ReCreateBitmap(Bitmap **bitmap, int width, int height, int depth)
419 Bitmap *new_bitmap = CreateBitmap(width, height, depth);
423 *bitmap = new_bitmap;
427 TransferBitmapPointers(new_bitmap, *bitmap);
432 void CloseWindow(DrawWindow *window)
436 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
439 if (draw_mask == REDRAW_NONE)
442 if (draw_mask & REDRAW_ALL)
445 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
448 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
451 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
454 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
460 boolean DrawingDeactivated(int x, int y, int width, int height)
462 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
465 boolean DrawingOnBackground(int x, int y)
467 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
468 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
471 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
472 int *width, int *height, boolean is_dest)
474 int clip_x, clip_y, clip_width, clip_height;
476 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
478 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
479 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
480 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
481 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
487 clip_width = bitmap->width;
488 clip_height = bitmap->height;
491 /* skip if rectangle completely outside bitmap */
493 if (*x + *width <= clip_x ||
494 *y + *height <= clip_y ||
495 *x >= clip_x + clip_width ||
496 *y >= clip_y + clip_height)
499 /* clip if rectangle overlaps bitmap */
503 *width -= clip_x - *x;
506 else if (*x + *width > clip_x + clip_width)
508 *width = clip_x + clip_width - *x;
513 *height -= clip_y - *y;
516 else if (*y + *height > clip_y + clip_height)
518 *height = clip_y + clip_height - *y;
524 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
525 int src_x, int src_y, int width, int height,
526 int dst_x, int dst_y)
528 int dst_x_unclipped = dst_x;
529 int dst_y_unclipped = dst_y;
531 if (src_bitmap == NULL || dst_bitmap == NULL)
534 if (DrawingDeactivated(dst_x, dst_y, width, height))
537 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
538 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
541 /* source x/y might need adjustment if destination x/y was clipped top/left */
542 src_x += dst_x - dst_x_unclipped;
543 src_y += dst_y - dst_y_unclipped;
545 #if defined(TARGET_SDL2)
546 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
547 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
548 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
549 but is already fixed in SVN and should therefore finally be fixed with
550 the next official SDL release, which is probably version 1.2.14.) */
551 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
553 if (src_bitmap == dst_bitmap)
555 /* needed when blitting directly to same bitmap -- should not be needed with
556 recent SDL libraries, but apparently does not work in 1.2.11 directly */
558 static Bitmap *tmp_bitmap = NULL;
559 static int tmp_bitmap_xsize = 0;
560 static int tmp_bitmap_ysize = 0;
562 /* start with largest static bitmaps for initial bitmap size ... */
563 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
565 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
566 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
569 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
570 if (src_bitmap->width > tmp_bitmap_xsize ||
571 src_bitmap->height > tmp_bitmap_ysize)
573 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
574 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
576 FreeBitmap(tmp_bitmap);
581 if (tmp_bitmap == NULL)
582 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
585 sysCopyArea(src_bitmap, tmp_bitmap,
586 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
587 sysCopyArea(tmp_bitmap, dst_bitmap,
588 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
594 sysCopyArea(src_bitmap, dst_bitmap,
595 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
598 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
599 int src_x, int src_y, int src_width, int src_height,
600 int dst_x, int dst_y, int dst_width, int dst_height)
602 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
603 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
604 int dst_xsize = dst_width;
605 int dst_ysize = dst_height;
606 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
607 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
610 for (y = 0; y < src_ysteps; y++)
612 for (x = 0; x < src_xsteps; x++)
614 int draw_x = dst_x + x * src_xsize;
615 int draw_y = dst_y + y * src_ysize;
616 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
617 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
619 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
625 void FadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
626 int fade_mode, int fade_delay, int post_delay,
627 void (*draw_border_function)(void))
629 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
630 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
633 SDLFadeRectangle(bitmap_cross, x, y, width, height,
634 fade_mode, fade_delay, post_delay, draw_border_function);
637 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
640 if (DrawingDeactivated(x, y, width, height))
643 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
646 sysFillRectangle(bitmap, x, y, width, height, color);
649 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
651 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
654 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
655 int width, int height)
657 if (DrawingOnBackground(x, y))
658 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
660 ClearRectangle(bitmap, x, y, width, height);
663 void SetClipMask(Bitmap *bitmap, GC clip_gc, Pixmap clip_pixmap)
667 void SetClipOrigin(Bitmap *bitmap, GC clip_gc, int clip_x, int clip_y)
671 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
672 int src_x, int src_y, int width, int height,
673 int dst_x, int dst_y)
675 if (DrawingDeactivated(dst_x, dst_y, width, height))
678 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
679 dst_x, dst_y, BLIT_MASKED);
682 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
683 int src_x, int src_y, int width, int height,
684 int dst_x, int dst_y)
686 if (DrawingOnBackground(dst_x, dst_y))
688 /* draw background */
689 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
692 /* draw foreground */
693 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
694 dst_x - src_x, dst_y - src_y);
695 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
699 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
703 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
706 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
709 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
712 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
715 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
716 int to_x, int to_y, Pixel pixel, int line_width)
720 for (x = 0; x < line_width; x++)
722 for (y = 0; y < line_width; y++)
724 int dx = x - line_width / 2;
725 int dy = y - line_width / 2;
727 if ((x == 0 && y == 0) ||
728 (x == 0 && y == line_width - 1) ||
729 (x == line_width - 1 && y == 0) ||
730 (x == line_width - 1 && y == line_width - 1))
734 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
739 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
744 for (i = 0; i < num_points - 1; i++)
745 DrawLine(bitmap, points[i].x, points[i].y,
746 points[i + 1].x, points[i + 1].y, pixel, line_width);
749 SDLDrawLines(bitmap->surface, points, num_points, pixel);
753 Pixel GetPixel(Bitmap *bitmap, int x, int y)
755 if (x < 0 || x >= bitmap->width ||
756 y < 0 || y >= bitmap->height)
759 return SDLGetPixel(bitmap, x, y);
762 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
763 unsigned int color_g, unsigned int color_b)
765 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
768 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
770 unsigned int color_r = (color >> 16) & 0xff;
771 unsigned int color_g = (color >> 8) & 0xff;
772 unsigned int color_b = (color >> 0) & 0xff;
774 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
777 /* execute all pending screen drawing operations */
778 void FlushDisplay(void)
782 /* execute and wait for all pending screen drawing operations */
783 void SyncDisplay(void)
787 void KeyboardAutoRepeatOn(void)
789 #if defined(TARGET_SDL2)
790 keyrepeat_status = TRUE;
792 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
793 SDL_DEFAULT_REPEAT_INTERVAL / 2);
794 SDL_EnableUNICODE(1);
798 void KeyboardAutoRepeatOff(void)
800 #if defined(TARGET_SDL2)
801 keyrepeat_status = FALSE;
803 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
804 SDL_EnableUNICODE(0);
808 boolean PointerInWindow(DrawWindow *window)
813 boolean SetVideoMode(boolean fullscreen)
815 return SDLSetVideoMode(&backbuffer, fullscreen);
818 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
820 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
821 (!fullscreen && video.fullscreen_enabled))
822 fullscreen = SetVideoMode(fullscreen);
827 Bitmap *LoadImage(char *filename)
831 new_bitmap = SDLLoadImage(filename);
834 new_bitmap->source_filename = getStringCopy(filename);
839 Bitmap *LoadCustomImage(char *basename)
841 char *filename = getCustomImageFilename(basename);
844 if (filename == NULL)
845 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
847 if ((new_bitmap = LoadImage(filename)) == NULL)
848 Error(ERR_EXIT, "LoadImage() failed: %s", GetError());
853 void ReloadCustomImage(Bitmap *bitmap, char *basename)
855 char *filename = getCustomImageFilename(basename);
858 if (filename == NULL) /* (should never happen) */
860 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
864 if (strEqual(filename, bitmap->source_filename))
866 /* The old and new image are the same (have the same filename and path).
867 This usually means that this image does not exist in this graphic set
868 and a fallback to the existing image is done. */
873 if ((new_bitmap = LoadImage(filename)) == NULL)
875 Error(ERR_WARN, "LoadImage() failed: %s", GetError());
879 if (bitmap->width != new_bitmap->width ||
880 bitmap->height != new_bitmap->height)
882 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
884 FreeBitmap(new_bitmap);
888 TransferBitmapPointers(new_bitmap, bitmap);
892 Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
894 Bitmap *dst_bitmap = SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
899 static void SetMaskedBitmapSurface(Bitmap *bitmap)
904 SDL_Surface *surface = bitmap->surface;
906 if (bitmap->surface_masked)
907 SDL_FreeSurface(bitmap->surface_masked);
909 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
910 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
912 if ((bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
913 Error(ERR_EXIT, "SDL_DisplayFormat() failed");
915 SDL_SetColorKey(surface, UNSET_TRANSPARENT_PIXEL, 0);
918 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
920 if (bitmaps[IMG_BITMAP_CUSTOM])
922 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
924 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
927 if (gfx.game_tile_size == gfx.standard_tile_size)
929 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
934 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
935 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
936 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
938 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
940 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
941 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
943 SetMaskedBitmapSurface(bitmap_new);
946 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
947 int tile_size, boolean create_small_bitmaps)
949 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
950 Bitmap *tmp_bitmap_final = NULL;
951 Bitmap *tmp_bitmap_0 = NULL;
952 Bitmap *tmp_bitmap_1 = NULL;
953 Bitmap *tmp_bitmap_2 = NULL;
954 Bitmap *tmp_bitmap_4 = NULL;
955 Bitmap *tmp_bitmap_8 = NULL;
956 Bitmap *tmp_bitmap_16 = NULL;
957 Bitmap *tmp_bitmap_32 = NULL;
958 int width_final, height_final;
959 int width_0, height_0;
960 int width_1, height_1;
961 int width_2, height_2;
962 int width_4, height_4;
963 int width_8, height_8;
964 int width_16, height_16;
965 int width_32, height_32;
966 int old_width, old_height;
969 print_timestamp_init("CreateScaledBitmaps");
971 old_width = old_bitmap->width;
972 old_height = old_bitmap->height;
974 /* calculate new image dimensions for final image size */
975 width_final = old_width * zoom_factor;
976 height_final = old_height * zoom_factor;
978 /* get image with final size (this might require scaling up) */
979 /* ("final" size may result in non-standard tile size image) */
980 if (zoom_factor != 1)
981 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
983 tmp_bitmap_final = old_bitmap;
987 width_0 = width_1 = width_final;
988 height_0 = height_1 = height_final;
990 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
992 if (create_small_bitmaps)
994 /* check if we have a non-gameplay tile size image */
995 if (tile_size != gfx.game_tile_size)
997 /* get image with gameplay tile size */
998 width_0 = width_final * gfx.game_tile_size / tile_size;
999 height_0 = height_final * gfx.game_tile_size / tile_size;
1001 if (width_0 == old_width)
1002 tmp_bitmap_0 = old_bitmap;
1003 else if (width_0 == width_final)
1004 tmp_bitmap_0 = tmp_bitmap_final;
1006 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1008 UPDATE_BUSY_STATE();
1011 /* check if we have a non-standard tile size image */
1012 if (tile_size != gfx.standard_tile_size)
1014 /* get image with standard tile size */
1015 width_1 = width_final * gfx.standard_tile_size / tile_size;
1016 height_1 = height_final * gfx.standard_tile_size / tile_size;
1018 if (width_1 == old_width)
1019 tmp_bitmap_1 = old_bitmap;
1020 else if (width_1 == width_final)
1021 tmp_bitmap_1 = tmp_bitmap_final;
1022 else if (width_1 == width_0)
1023 tmp_bitmap_1 = tmp_bitmap_0;
1025 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1027 UPDATE_BUSY_STATE();
1030 /* calculate new image dimensions for small images */
1031 width_2 = width_1 / 2;
1032 height_2 = height_1 / 2;
1033 width_4 = width_1 / 4;
1034 height_4 = height_1 / 4;
1035 width_8 = width_1 / 8;
1036 height_8 = height_1 / 8;
1037 width_16 = width_1 / 16;
1038 height_16 = height_1 / 16;
1039 width_32 = width_1 / 32;
1040 height_32 = height_1 / 32;
1042 /* get image with 1/2 of normal size (for use in the level editor) */
1043 if (width_2 == old_width)
1044 tmp_bitmap_2 = old_bitmap;
1046 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1048 UPDATE_BUSY_STATE();
1050 /* get image with 1/4 of normal size (for use in the level editor) */
1051 if (width_4 == old_width)
1052 tmp_bitmap_4 = old_bitmap;
1054 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1056 UPDATE_BUSY_STATE();
1058 /* get image with 1/8 of normal size (for use on the preview screen) */
1059 if (width_8 == old_width)
1060 tmp_bitmap_8 = old_bitmap;
1062 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1064 UPDATE_BUSY_STATE();
1066 /* get image with 1/16 of normal size (for use on the preview screen) */
1067 if (width_16 == old_width)
1068 tmp_bitmap_16 = old_bitmap;
1070 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1072 UPDATE_BUSY_STATE();
1074 /* get image with 1/32 of normal size (for use on the preview screen) */
1075 if (width_32 == old_width)
1076 tmp_bitmap_32 = old_bitmap;
1078 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1080 UPDATE_BUSY_STATE();
1082 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1083 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1084 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1085 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1086 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1087 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1089 if (width_0 != width_1)
1090 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1092 if (bitmaps[IMG_BITMAP_CUSTOM])
1093 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1095 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1097 boolean free_old_bitmap = TRUE;
1099 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1100 if (bitmaps[i] == old_bitmap)
1101 free_old_bitmap = FALSE;
1103 if (free_old_bitmap)
1104 FreeBitmap(old_bitmap);
1108 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1111 // create corresponding bitmaps for masked blitting
1112 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1113 if (bitmaps[i] != NULL &&
1114 bitmaps[i] != old_bitmap)
1115 SetMaskedBitmapSurface(bitmaps[i]);
1117 UPDATE_BUSY_STATE();
1119 print_timestamp_done("CreateScaledBitmaps");
1122 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1125 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1128 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1130 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1134 /* ------------------------------------------------------------------------- */
1135 /* mouse pointer functions */
1136 /* ------------------------------------------------------------------------- */
1138 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1140 /* XPM image definitions */
1141 static const char *cursor_image_none[] =
1143 /* width height num_colors chars_per_pixel */
1173 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1174 static const char *cursor_image_dot[] =
1176 /* width height num_colors chars_per_pixel */
1205 static const char **cursor_image_playfield = cursor_image_dot;
1207 /* some people complained about a "white dot" on the screen and thought it
1208 was a graphical error... OK, let's just remove the whole pointer :-) */
1209 static const char **cursor_image_playfield = cursor_image_none;
1212 static const int cursor_bit_order = BIT_ORDER_MSB;
1214 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1216 struct MouseCursorInfo *cursor;
1217 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1218 int header_lines = 4;
1221 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1223 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1226 for (y = 0; y < cursor->width; y++)
1228 for (x = 0; x < cursor->height; x++)
1231 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1236 cursor->data[i] = cursor->mask[i] = 0;
1239 switch (image[header_lines + y][x])
1242 cursor->data[i] |= bit_mask;
1243 cursor->mask[i] |= bit_mask;
1247 cursor->mask[i] |= bit_mask;
1256 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1261 void SetMouseCursor(int mode)
1263 static struct MouseCursorInfo *cursor_none = NULL;
1264 static struct MouseCursorInfo *cursor_playfield = NULL;
1265 struct MouseCursorInfo *cursor_new;
1267 if (cursor_none == NULL)
1268 cursor_none = get_cursor_from_image(cursor_image_none);
1270 if (cursor_playfield == NULL)
1271 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1273 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1274 mode == CURSOR_NONE ? cursor_none :
1275 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1277 SDLSetMouseCursor(cursor_new);
1281 /* ========================================================================= */
1282 /* audio functions */
1283 /* ========================================================================= */
1285 void OpenAudio(void)
1287 /* always start with reliable default values */
1288 audio.sound_available = FALSE;
1289 audio.music_available = FALSE;
1290 audio.loops_available = FALSE;
1292 audio.sound_enabled = FALSE;
1293 audio.sound_deactivated = FALSE;
1295 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1296 audio.mixer_pid = 0;
1297 audio.device_name = NULL;
1298 audio.device_fd = -1;
1300 audio.num_channels = 0;
1301 audio.music_channel = 0;
1302 audio.first_sound_channel = 0;
1307 void CloseAudio(void)
1311 audio.sound_enabled = FALSE;
1314 void SetAudioMode(boolean enabled)
1316 if (!audio.sound_available)
1319 audio.sound_enabled = enabled;
1323 /* ========================================================================= */
1324 /* event functions */
1325 /* ========================================================================= */
1327 void InitEventFilter(EventFilter filter_function)
1329 /* set event filter to filter out certain events */
1330 #if defined(TARGET_SDL2)
1331 SDL_SetEventFilter(filter_function, NULL);
1333 SDL_SetEventFilter(filter_function);
1337 boolean PendingEvent(void)
1339 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1342 void NextEvent(Event *event)
1344 SDLNextEvent(event);
1347 void PeekEvent(Event *event)
1349 #if defined(TARGET_SDL2)
1350 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1352 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1356 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1358 #if defined(TARGET_SDL2)
1359 /* key up/down events in SDL2 do not return text characters anymore */
1360 return event->keysym.sym;
1363 #if ENABLE_UNUSED_CODE
1364 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1365 (int)event->keysym.unicode,
1366 (int)event->keysym.sym,
1367 (int)SDL_GetModState());
1370 if (with_modifiers &&
1371 event->keysym.unicode > 0x0000 &&
1372 event->keysym.unicode < 0x2000)
1373 return event->keysym.unicode;
1375 return event->keysym.sym;
1380 KeyMod HandleKeyModState(Key key, int key_status)
1382 static KeyMod current_modifiers = KMOD_None;
1384 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1386 KeyMod new_modifier = KMOD_None;
1391 new_modifier = KMOD_Shift_L;
1394 new_modifier = KMOD_Shift_R;
1396 case KSYM_Control_L:
1397 new_modifier = KMOD_Control_L;
1399 case KSYM_Control_R:
1400 new_modifier = KMOD_Control_R;
1403 new_modifier = KMOD_Meta_L;
1406 new_modifier = KMOD_Meta_R;
1409 new_modifier = KMOD_Alt_L;
1412 new_modifier = KMOD_Alt_R;
1418 if (key_status == KEY_PRESSED)
1419 current_modifiers |= new_modifier;
1421 current_modifiers &= ~new_modifier;
1424 return current_modifiers;
1427 KeyMod GetKeyModState()
1429 return (KeyMod)SDL_GetModState();
1432 KeyMod GetKeyModStateFromEvents()
1434 /* always use key modifier state as tracked from key events (this is needed
1435 if the modifier key event was injected into the event queue, but the key
1436 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1437 query the keys as held pressed on the keyboard) -- this case is currently
1438 only used to filter out clipboard insert events from "True X-Mouse" tool */
1440 return HandleKeyModState(KSYM_UNDEFINED, 0);
1443 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1445 if (event->type != EVENT_CLIENTMESSAGE)
1448 return TRUE; /* the only possible message here is SDL_QUIT */
1452 /* ========================================================================= */
1453 /* joystick functions */
1454 /* ========================================================================= */
1456 void InitJoysticks()
1460 #if defined(NO_JOYSTICK)
1461 return; /* joysticks generally deactivated by compile-time directive */
1464 /* always start with reliable default values */
1465 joystick.status = JOYSTICK_NOT_AVAILABLE;
1466 for (i = 0; i < MAX_PLAYERS; i++)
1467 joystick.fd[i] = -1; /* joystick device closed */
1472 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1474 return SDLReadJoystick(nr, x, y, b1, b2);