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 CreateScaledBitmaps(Bitmap *old_bitmap, int zoom_factor,
900 int tile_size, boolean create_small_bitmaps)
904 Bitmap *tmp_bitmap_final = NULL;
905 Bitmap *tmp_bitmap_0 = NULL;
906 Bitmap *tmp_bitmap_1 = NULL;
907 Bitmap *tmp_bitmap_2 = NULL;
908 Bitmap *tmp_bitmap_4 = NULL;
909 Bitmap *tmp_bitmap_8 = NULL;
910 Bitmap *tmp_bitmap_16 = NULL;
911 Bitmap *tmp_bitmap_32 = NULL;
912 int width_final, height_final;
913 int width_0, height_0;
914 int width_1, height_1;
915 int width_2, height_2;
916 int width_4, height_4;
917 int width_8, height_8;
918 int width_16, height_16;
919 int width_32, height_32;
920 int old_width, old_height;
921 int new_width, new_height;
923 print_timestamp_init("CreateScaledBitmaps");
925 old_width = old_bitmap->width;
926 old_height = old_bitmap->height;
928 /* calculate new image dimensions for final image size */
929 width_final = old_width * zoom_factor;
930 height_final = old_height * zoom_factor;
932 /* get image with final size (this might require scaling up) */
933 /* ("final" size may result in non-standard tile size image) */
934 if (zoom_factor != 1)
935 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
937 tmp_bitmap_final = old_bitmap;
941 width_0 = width_1 = width_final;
942 height_0 = height_1 = height_final;
944 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
946 if (create_small_bitmaps)
948 /* check if we have a non-gameplay tile size image */
949 if (tile_size != gfx.game_tile_size)
951 /* get image with gameplay tile size */
952 width_0 = width_final * gfx.game_tile_size / tile_size;
953 height_0 = height_final * gfx.game_tile_size / tile_size;
955 if (width_0 == old_width)
956 tmp_bitmap_0 = old_bitmap;
957 else if (width_0 == width_final)
958 tmp_bitmap_0 = tmp_bitmap_final;
960 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
965 /* check if we have a non-standard tile size image */
966 if (tile_size != gfx.standard_tile_size)
968 /* get image with standard tile size */
969 width_1 = width_final * gfx.standard_tile_size / tile_size;
970 height_1 = height_final * gfx.standard_tile_size / tile_size;
972 if (width_1 == old_width)
973 tmp_bitmap_1 = old_bitmap;
974 else if (width_1 == width_final)
975 tmp_bitmap_1 = tmp_bitmap_final;
976 else if (width_1 == width_0)
977 tmp_bitmap_1 = tmp_bitmap_0;
979 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
985 if (create_small_bitmaps)
987 /* calculate new image dimensions for small images */
988 width_2 = width_1 / 2;
989 height_2 = height_1 / 2;
990 width_4 = width_1 / 4;
991 height_4 = height_1 / 4;
992 width_8 = width_1 / 8;
993 height_8 = height_1 / 8;
994 width_16 = width_1 / 16;
995 height_16 = height_1 / 16;
996 width_32 = width_1 / 32;
997 height_32 = height_1 / 32;
999 /* get image with 1/2 of normal size (for use in the level editor) */
1000 if (width_2 == old_width)
1001 tmp_bitmap_2 = old_bitmap;
1003 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1005 UPDATE_BUSY_STATE();
1007 /* get image with 1/4 of normal size (for use in the level editor) */
1008 if (width_4 == old_width)
1009 tmp_bitmap_4 = old_bitmap;
1011 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1013 UPDATE_BUSY_STATE();
1015 /* get image with 1/8 of normal size (for use on the preview screen) */
1016 if (width_8 == old_width)
1017 tmp_bitmap_8 = old_bitmap;
1019 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1021 UPDATE_BUSY_STATE();
1023 /* get image with 1/16 of normal size (for use on the preview screen) */
1024 if (width_16 == old_width)
1025 tmp_bitmap_16 = old_bitmap;
1027 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1029 UPDATE_BUSY_STATE();
1031 /* get image with 1/32 of normal size (for use on the preview screen) */
1032 if (width_32 == old_width)
1033 tmp_bitmap_32 = old_bitmap;
1035 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1037 UPDATE_BUSY_STATE();
1040 if (create_small_bitmaps)
1042 new_width = width_1;
1043 new_height = height_1 + (height_1 + 1) / 2; /* prevent odd height */
1045 if (width_0 != width_1)
1047 new_width += width_0;
1048 new_height = MAX(new_height, height_0);
1051 new_bitmap = CreateBitmap(new_width, new_height, DEFAULT_DEPTH);
1053 if (width_0 != width_1)
1054 BlitBitmap(tmp_bitmap_0, new_bitmap, 0, 0, width_0, height_0, width_1, 0);
1056 BlitBitmap(tmp_bitmap_1, new_bitmap, 0, 0, width_1, height_1, 0, 0);
1057 BlitBitmap(tmp_bitmap_2, new_bitmap, 0, 0, width_1 / 2, height_1 / 2,
1059 BlitBitmap(tmp_bitmap_4, new_bitmap, 0, 0, width_1 / 4, height_1 / 4,
1060 width_1 / 2, height_1);
1061 BlitBitmap(tmp_bitmap_8, new_bitmap, 0, 0, width_1 / 8, height_1 / 8,
1062 3 * width_1 / 4, height_1);
1063 BlitBitmap(tmp_bitmap_16, new_bitmap, 0, 0, width_1 / 16, height_1 / 16,
1064 7 * width_1 / 8, height_1);
1065 BlitBitmap(tmp_bitmap_32, new_bitmap, 0, 0, width_1 / 32, height_1 / 32,
1066 15 * width_1 / 16, height_1);
1068 UPDATE_BUSY_STATE();
1072 new_width = width_1;
1073 new_height = height_1;
1075 new_bitmap = tmp_bitmap_1; /* directly use tmp_bitmap_1 as new bitmap */
1078 if (create_small_bitmaps)
1080 /* if no small bitmaps created, tmp_bitmap_1 is used as new bitmap now */
1082 if (tmp_bitmap_final != old_bitmap)
1083 FreeBitmap(tmp_bitmap_final);
1085 if (tmp_bitmap_0 != old_bitmap &&
1086 tmp_bitmap_0 != tmp_bitmap_final)
1087 FreeBitmap(tmp_bitmap_0);
1089 if (tmp_bitmap_1 != old_bitmap &&
1090 tmp_bitmap_1 != tmp_bitmap_final &&
1091 tmp_bitmap_1 != tmp_bitmap_0)
1092 FreeBitmap(tmp_bitmap_1);
1094 if (tmp_bitmap_2 != old_bitmap)
1095 FreeBitmap(tmp_bitmap_2);
1097 if (tmp_bitmap_4 != old_bitmap)
1098 FreeBitmap(tmp_bitmap_4);
1100 if (tmp_bitmap_8 != old_bitmap)
1101 FreeBitmap(tmp_bitmap_8);
1103 if (tmp_bitmap_16 != old_bitmap)
1104 FreeBitmap(tmp_bitmap_16);
1106 if (tmp_bitmap_32 != old_bitmap)
1107 FreeBitmap(tmp_bitmap_32);
1110 /* replace image with extended image (containing 1/1, 1/2, 1/4, 1/8 size) */
1111 swap_bitmap.surface = old_bitmap->surface;
1112 old_bitmap->surface = new_bitmap->surface;
1113 new_bitmap->surface = swap_bitmap.surface;
1115 old_bitmap->width = new_bitmap->width;
1116 old_bitmap->height = new_bitmap->height;
1118 /* this replaces all blit masks created when loading -- maybe optimize this */
1120 SDL_Surface *old_surface = old_bitmap->surface;
1122 if (old_bitmap->surface_masked)
1123 SDL_FreeSurface(old_bitmap->surface_masked);
1125 SDL_SetColorKey(old_surface, SET_TRANSPARENT_PIXEL,
1126 SDL_MapRGB(old_surface->format, 0x00, 0x00, 0x00));
1128 if ((old_bitmap->surface_masked = SDLGetNativeSurface(old_surface)) == NULL)
1129 Error(ERR_EXIT, "SDL_DisplayFormat() failed");
1131 SDL_SetColorKey(old_surface, UNSET_TRANSPARENT_PIXEL, 0);
1134 UPDATE_BUSY_STATE();
1136 FreeBitmap(new_bitmap); /* this actually frees the _old_ bitmap now */
1138 print_timestamp_done("CreateScaledBitmaps");
1141 void CreateBitmapWithSmallBitmaps(Bitmap *old_bitmap, int zoom_factor,
1144 CreateScaledBitmaps(old_bitmap, zoom_factor, tile_size, TRUE);
1147 void ScaleBitmap(Bitmap *old_bitmap, int zoom_factor)
1149 CreateScaledBitmaps(old_bitmap, zoom_factor, 0, FALSE);
1153 /* ------------------------------------------------------------------------- */
1154 /* mouse pointer functions */
1155 /* ------------------------------------------------------------------------- */
1157 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1159 /* XPM image definitions */
1160 static const char *cursor_image_none[] =
1162 /* width height num_colors chars_per_pixel */
1192 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1193 static const char *cursor_image_dot[] =
1195 /* width height num_colors chars_per_pixel */
1224 static const char **cursor_image_playfield = cursor_image_dot;
1226 /* some people complained about a "white dot" on the screen and thought it
1227 was a graphical error... OK, let's just remove the whole pointer :-) */
1228 static const char **cursor_image_playfield = cursor_image_none;
1231 static const int cursor_bit_order = BIT_ORDER_MSB;
1233 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1235 struct MouseCursorInfo *cursor;
1236 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1237 int header_lines = 4;
1240 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1242 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1245 for (y = 0; y < cursor->width; y++)
1247 for (x = 0; x < cursor->height; x++)
1250 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1255 cursor->data[i] = cursor->mask[i] = 0;
1258 switch (image[header_lines + y][x])
1261 cursor->data[i] |= bit_mask;
1262 cursor->mask[i] |= bit_mask;
1266 cursor->mask[i] |= bit_mask;
1275 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1280 void SetMouseCursor(int mode)
1282 static struct MouseCursorInfo *cursor_none = NULL;
1283 static struct MouseCursorInfo *cursor_playfield = NULL;
1284 struct MouseCursorInfo *cursor_new;
1286 if (cursor_none == NULL)
1287 cursor_none = get_cursor_from_image(cursor_image_none);
1289 if (cursor_playfield == NULL)
1290 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1292 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1293 mode == CURSOR_NONE ? cursor_none :
1294 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1296 SDLSetMouseCursor(cursor_new);
1300 /* ========================================================================= */
1301 /* audio functions */
1302 /* ========================================================================= */
1304 void OpenAudio(void)
1306 /* always start with reliable default values */
1307 audio.sound_available = FALSE;
1308 audio.music_available = FALSE;
1309 audio.loops_available = FALSE;
1311 audio.sound_enabled = FALSE;
1312 audio.sound_deactivated = FALSE;
1314 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1315 audio.mixer_pid = 0;
1316 audio.device_name = NULL;
1317 audio.device_fd = -1;
1319 audio.num_channels = 0;
1320 audio.music_channel = 0;
1321 audio.first_sound_channel = 0;
1326 void CloseAudio(void)
1330 audio.sound_enabled = FALSE;
1333 void SetAudioMode(boolean enabled)
1335 if (!audio.sound_available)
1338 audio.sound_enabled = enabled;
1342 /* ========================================================================= */
1343 /* event functions */
1344 /* ========================================================================= */
1346 void InitEventFilter(EventFilter filter_function)
1348 /* set event filter to filter out certain events */
1349 #if defined(TARGET_SDL2)
1350 SDL_SetEventFilter(filter_function, NULL);
1352 SDL_SetEventFilter(filter_function);
1356 boolean PendingEvent(void)
1358 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1361 void NextEvent(Event *event)
1363 SDLNextEvent(event);
1366 void PeekEvent(Event *event)
1368 #if defined(TARGET_SDL2)
1369 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1371 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1375 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1377 #if defined(TARGET_SDL2)
1378 /* key up/down events in SDL2 do not return text characters anymore */
1379 return event->keysym.sym;
1382 #if ENABLE_UNUSED_CODE
1383 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1384 (int)event->keysym.unicode,
1385 (int)event->keysym.sym,
1386 (int)SDL_GetModState());
1389 if (with_modifiers &&
1390 event->keysym.unicode > 0x0000 &&
1391 event->keysym.unicode < 0x2000)
1392 return event->keysym.unicode;
1394 return event->keysym.sym;
1399 KeyMod HandleKeyModState(Key key, int key_status)
1401 static KeyMod current_modifiers = KMOD_None;
1403 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1405 KeyMod new_modifier = KMOD_None;
1410 new_modifier = KMOD_Shift_L;
1413 new_modifier = KMOD_Shift_R;
1415 case KSYM_Control_L:
1416 new_modifier = KMOD_Control_L;
1418 case KSYM_Control_R:
1419 new_modifier = KMOD_Control_R;
1422 new_modifier = KMOD_Meta_L;
1425 new_modifier = KMOD_Meta_R;
1428 new_modifier = KMOD_Alt_L;
1431 new_modifier = KMOD_Alt_R;
1437 if (key_status == KEY_PRESSED)
1438 current_modifiers |= new_modifier;
1440 current_modifiers &= ~new_modifier;
1443 return current_modifiers;
1446 KeyMod GetKeyModState()
1448 return (KeyMod)SDL_GetModState();
1451 KeyMod GetKeyModStateFromEvents()
1453 /* always use key modifier state as tracked from key events (this is needed
1454 if the modifier key event was injected into the event queue, but the key
1455 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1456 query the keys as held pressed on the keyboard) -- this case is currently
1457 only used to filter out clipboard insert events from "True X-Mouse" tool */
1459 return HandleKeyModState(KSYM_UNDEFINED, 0);
1462 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1464 if (event->type != EVENT_CLIENTMESSAGE)
1467 return TRUE; /* the only possible message here is SDL_QUIT */
1471 /* ========================================================================= */
1472 /* joystick functions */
1473 /* ========================================================================= */
1475 void InitJoysticks()
1479 #if defined(NO_JOYSTICK)
1480 return; /* joysticks generally deactivated by compile-time directive */
1483 /* always start with reliable default values */
1484 joystick.status = JOYSTICK_NOT_AVAILABLE;
1485 for (i = 0; i < MAX_PLAYERS; i++)
1486 joystick.fd[i] = -1; /* joystick device closed */
1491 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1493 return SDLReadJoystick(nr, x, y, b1, b2);