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 int wheel_steps = DEFAULT_WHEEL_STEPS;
54 #if defined(TARGET_SDL2)
55 boolean keyrepeat_status = TRUE;
58 int redraw_mask = REDRAW_NONE;
63 /* ========================================================================= */
64 /* init/close functions */
65 /* ========================================================================= */
67 void InitProgramInfo(char *argv0, char *config_filename, char *userdata_subdir,
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_path = getUserGameDataDir();
80 program.program_title = program_title;
81 program.window_title = "(undefined)";
82 program.icon_title = icon_title;
84 program.icon_filename = icon_filename;
86 program.cookie_prefix = cookie_prefix;
88 program.version_major = VERSION_MAJOR(program_version);
89 program.version_minor = VERSION_MINOR(program_version);
90 program.version_patch = VERSION_PATCH(program_version);
91 program.version_build = VERSION_BUILD(program_version);
92 program.version_ident = program_version;
94 program.log_filename[LOG_OUT_ID] = getLogFilename(LOG_OUT_BASENAME);
95 program.log_filename[LOG_ERR_ID] = getLogFilename(LOG_ERR_BASENAME);
96 program.log_file[LOG_OUT_ID] = program.log_file_default[LOG_OUT_ID] = stdout;
97 program.log_file[LOG_ERR_ID] = program.log_file_default[LOG_ERR_ID] = stderr;
100 void SetWindowTitle()
102 program.window_title = program.window_title_function();
107 void InitWindowTitleFunction(char *(*window_title_function)(void))
109 program.window_title_function = window_title_function;
112 void InitExitMessageFunction(void (*exit_message_function)(char *, va_list))
114 program.exit_message_function = exit_message_function;
117 void InitExitFunction(void (*exit_function)(int))
119 program.exit_function = exit_function;
121 /* set signal handlers to custom exit function */
122 // signal(SIGINT, exit_function);
123 signal(SIGTERM, exit_function);
125 /* set exit function to automatically cleanup SDL stuff after exit() */
129 void InitPlatformDependentStuff(void)
131 // this is initialized in GetOptions(), but may already be used before
132 options.verbose = TRUE;
136 #if defined(TARGET_SDL2)
137 int sdl_init_flags = SDL_INIT_EVENTS | SDL_INIT_NOPARACHUTE;
139 int sdl_init_flags = SDL_INIT_EVENTTHREAD | SDL_INIT_NOPARACHUTE;
142 if (SDL_Init(sdl_init_flags) < 0)
143 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
148 void ClosePlatformDependentStuff(void)
153 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
154 int real_sx, int real_sy,
155 int full_sxsize, int full_sysize,
156 Bitmap *field_save_buffer)
162 gfx.real_sx = real_sx;
163 gfx.real_sy = real_sy;
164 gfx.full_sxsize = full_sxsize;
165 gfx.full_sysize = full_sysize;
167 gfx.field_save_buffer = field_save_buffer;
169 SetDrawDeactivationMask(REDRAW_NONE); /* do not deactivate drawing */
170 SetDrawBackgroundMask(REDRAW_NONE); /* deactivate masked drawing */
173 void InitGfxTileSizeInfo(int game_tile_size, int standard_tile_size)
175 gfx.game_tile_size = game_tile_size;
176 gfx.standard_tile_size = standard_tile_size;
179 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
187 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
195 void InitGfxDoor3Info(int ex, int ey, int exsize, int eysize)
203 void InitGfxWindowInfo(int win_xsize, int win_ysize)
205 if (win_xsize != gfx.win_xsize || win_ysize != gfx.win_ysize)
207 ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize);
209 #if defined(TARGET_SDL2)
210 ReCreateBitmap(&gfx.final_screen_bitmap, win_xsize, win_ysize);
213 ReCreateBitmap(&gfx.fade_bitmap_backup, win_xsize, win_ysize);
214 ReCreateBitmap(&gfx.fade_bitmap_source, win_xsize, win_ysize);
215 ReCreateBitmap(&gfx.fade_bitmap_target, win_xsize, win_ysize);
216 ReCreateBitmap(&gfx.fade_bitmap_black, win_xsize, win_ysize);
218 ClearRectangle(gfx.fade_bitmap_black, 0, 0, win_xsize, win_ysize);
221 gfx.win_xsize = win_xsize;
222 gfx.win_ysize = win_ysize;
224 gfx.background_bitmap_mask = REDRAW_NONE;
227 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
229 /* currently only used by MSDOS code to alloc VRAM buffer, if available */
230 /* 2009-03-24: also (temporarily?) used for overlapping blit workaround */
231 gfx.scrollbuffer_width = scrollbuffer_width;
232 gfx.scrollbuffer_height = scrollbuffer_height;
235 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
237 gfx.clipping_enabled = enabled;
240 gfx.clip_width = width;
241 gfx.clip_height = height;
244 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
246 gfx.draw_busy_anim_function = draw_busy_anim_function;
249 void InitGfxDrawGlobalAnimFunction(void (*draw_global_anim_function)(int, int))
251 gfx.draw_global_anim_function = draw_global_anim_function;
254 void InitGfxDrawGlobalBorderFunction(void (*draw_global_border_function)(int))
256 gfx.draw_global_border_function = draw_global_border_function;
259 void InitGfxCustomArtworkInfo()
261 gfx.override_level_graphics = FALSE;
262 gfx.override_level_sounds = FALSE;
263 gfx.override_level_music = FALSE;
265 gfx.draw_init_text = TRUE;
268 void InitGfxOtherSettings()
270 gfx.cursor_mode = CURSOR_DEFAULT;
273 void SetDrawDeactivationMask(int draw_deactivation_mask)
275 gfx.draw_deactivation_mask = draw_deactivation_mask;
278 void SetDrawBackgroundMask(int draw_background_mask)
280 gfx.draw_background_mask = draw_background_mask;
283 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
285 if (background_bitmap_tile != NULL)
286 gfx.background_bitmap_mask |= mask;
288 gfx.background_bitmap_mask &= ~mask;
290 if (background_bitmap_tile == NULL) /* empty background requested */
293 if (mask == REDRAW_ALL)
294 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
295 0, 0, video.width, video.height);
296 else if (mask == REDRAW_FIELD)
297 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
298 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
299 else if (mask == REDRAW_DOOR_1)
300 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
301 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
304 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
306 /* remove every mask before setting mask for window */
307 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
308 SetBackgroundBitmap(NULL, 0xffff); /* !!! FIX THIS !!! */
309 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
312 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
314 /* remove window area mask before setting mask for main area */
315 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
316 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
317 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
320 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
322 /* remove window area mask before setting mask for door area */
323 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
324 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
325 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
329 /* ========================================================================= */
330 /* video functions */
331 /* ========================================================================= */
333 inline static int GetRealDepth(int depth)
335 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
338 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
339 int width, int height, Pixel color)
341 SDLFillRectangle(bitmap, x, y, width, height, color);
343 if (bitmap == backbuffer)
344 SetRedrawMaskFromArea(x, y, width, height);
347 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
348 int src_x, int src_y, int width, int height,
349 int dst_x, int dst_y, int mask_mode)
351 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
352 dst_x, dst_y, mask_mode);
354 if (dst_bitmap == backbuffer)
355 SetRedrawMaskFromArea(dst_x, dst_y, width, height);
358 void LimitScreenUpdates(boolean enable)
360 SDLLimitScreenUpdates(enable);
363 void InitVideoDisplay(void)
365 SDLInitVideoDisplay();
368 void CloseVideoDisplay(void)
370 KeyboardAutoRepeatOn();
372 SDL_QuitSubSystem(SDL_INIT_VIDEO);
375 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
378 video.height = height;
379 video.depth = GetRealDepth(depth);
381 video.fullscreen_available = FULLSCREEN_STATUS;
382 video.fullscreen_enabled = FALSE;
384 video.window_scaling_available = WINDOW_SCALING_STATUS;
386 video.frame_delay = 0;
387 video.frame_delay_value = GAME_FRAME_DELAY;
389 video.shifted_up = FALSE;
391 SDLInitVideoBuffer(fullscreen);
393 video.initialized = TRUE;
398 inline static void FreeBitmapPointers(Bitmap *bitmap)
403 SDLFreeBitmapPointers(bitmap);
405 checked_free(bitmap->source_filename);
406 bitmap->source_filename = NULL;
409 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
412 if (src_bitmap == NULL || dst_bitmap == NULL)
415 FreeBitmapPointers(dst_bitmap);
417 *dst_bitmap = *src_bitmap;
420 void FreeBitmap(Bitmap *bitmap)
425 FreeBitmapPointers(bitmap);
430 Bitmap *CreateBitmapStruct(void)
432 return checked_calloc(sizeof(Bitmap));
435 Bitmap *CreateBitmap(int width, int height, int depth)
437 Bitmap *new_bitmap = CreateBitmapStruct();
438 int real_width = MAX(1, width); /* prevent zero bitmap width */
439 int real_height = MAX(1, height); /* prevent zero bitmap height */
440 int real_depth = GetRealDepth(depth);
442 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
444 new_bitmap->width = real_width;
445 new_bitmap->height = real_height;
450 void ReCreateBitmap(Bitmap **bitmap, int width, int height)
452 Bitmap *new_bitmap = CreateBitmap(width, height, DEFAULT_DEPTH);
456 *bitmap = new_bitmap;
460 TransferBitmapPointers(new_bitmap, *bitmap);
465 void CloseWindow(DrawWindow *window)
469 void SetRedrawMaskFromArea(int x, int y, int width, int height)
473 int x2 = x + width - 1;
474 int y2 = y + height - 1;
476 if (width == 0 || height == 0)
479 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
480 redraw_mask |= REDRAW_FIELD;
481 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
482 redraw_mask |= REDRAW_DOOR_1;
483 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
484 redraw_mask |= REDRAW_DOOR_2;
485 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
486 redraw_mask |= REDRAW_DOOR_3;
488 redraw_mask = REDRAW_ALL;
491 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
494 if (draw_mask == REDRAW_NONE)
497 if (draw_mask & REDRAW_ALL)
500 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
503 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
506 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
509 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
515 boolean DrawingDeactivated(int x, int y, int width, int height)
517 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
520 boolean DrawingOnBackground(int x, int y)
522 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
523 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
526 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
527 int *width, int *height, boolean is_dest)
529 int clip_x, clip_y, clip_width, clip_height;
531 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
533 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
534 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
535 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
536 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
542 clip_width = bitmap->width;
543 clip_height = bitmap->height;
546 /* skip if rectangle completely outside bitmap */
548 if (*x + *width <= clip_x ||
549 *y + *height <= clip_y ||
550 *x >= clip_x + clip_width ||
551 *y >= clip_y + clip_height)
554 /* clip if rectangle overlaps bitmap */
558 *width -= clip_x - *x;
561 else if (*x + *width > clip_x + clip_width)
563 *width = clip_x + clip_width - *x;
568 *height -= clip_y - *y;
571 else if (*y + *height > clip_y + clip_height)
573 *height = clip_y + clip_height - *y;
579 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
580 int src_x, int src_y, int width, int height,
581 int dst_x, int dst_y)
583 int dst_x_unclipped = dst_x;
584 int dst_y_unclipped = dst_y;
586 if (src_bitmap == NULL || dst_bitmap == NULL)
589 if (DrawingDeactivated(dst_x, dst_y, width, height))
592 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
593 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
596 /* source x/y might need adjustment if destination x/y was clipped top/left */
597 src_x += dst_x - dst_x_unclipped;
598 src_y += dst_y - dst_y_unclipped;
600 #if defined(TARGET_SDL2)
601 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
602 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
603 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
604 but is already fixed in SVN and should therefore finally be fixed with
605 the next official SDL release, which is probably version 1.2.14.) */
606 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
608 if (src_bitmap == dst_bitmap)
610 /* needed when blitting directly to same bitmap -- should not be needed with
611 recent SDL libraries, but apparently does not work in 1.2.11 directly */
613 static Bitmap *tmp_bitmap = NULL;
614 static int tmp_bitmap_xsize = 0;
615 static int tmp_bitmap_ysize = 0;
617 /* start with largest static bitmaps for initial bitmap size ... */
618 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
620 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
621 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
624 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
625 if (src_bitmap->width > tmp_bitmap_xsize ||
626 src_bitmap->height > tmp_bitmap_ysize)
628 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
629 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
631 FreeBitmap(tmp_bitmap);
636 if (tmp_bitmap == NULL)
637 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
640 sysCopyArea(src_bitmap, tmp_bitmap,
641 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
642 sysCopyArea(tmp_bitmap, dst_bitmap,
643 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
649 sysCopyArea(src_bitmap, dst_bitmap,
650 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
653 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
654 int src_x, int src_y, int src_width, int src_height,
655 int dst_x, int dst_y, int dst_width, int dst_height)
657 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
658 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
659 int dst_xsize = dst_width;
660 int dst_ysize = dst_height;
661 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
662 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
665 for (y = 0; y < src_ysteps; y++)
667 for (x = 0; x < src_xsteps; x++)
669 int draw_x = dst_x + x * src_xsize;
670 int draw_y = dst_y + y * src_ysize;
671 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
672 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
674 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
680 void FadeRectangle(int x, int y, int width, int height,
681 int fade_mode, int fade_delay, int post_delay,
682 void (*draw_border_function)(void))
684 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
685 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
688 SDLFadeRectangle(x, y, width, height,
689 fade_mode, fade_delay, post_delay, draw_border_function);
692 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
695 if (DrawingDeactivated(x, y, width, height))
698 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
701 sysFillRectangle(bitmap, x, y, width, height, color);
704 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
706 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
709 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
710 int width, int height)
712 if (DrawingOnBackground(x, y))
713 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
715 ClearRectangle(bitmap, x, y, width, height);
718 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
719 int src_x, int src_y, int width, int height,
720 int dst_x, int dst_y)
722 if (DrawingDeactivated(dst_x, dst_y, width, height))
725 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
726 dst_x, dst_y, BLIT_MASKED);
729 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
730 int src_x, int src_y, int width, int height,
731 int dst_x, int dst_y)
733 if (DrawingOnBackground(dst_x, dst_y))
735 /* draw background */
736 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
739 /* draw foreground */
740 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
744 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
748 void BlitTexture(Bitmap *bitmap,
749 int src_x, int src_y, int width, int height,
750 int dst_x, int dst_y)
755 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
759 void BlitTextureMasked(Bitmap *bitmap,
760 int src_x, int src_y, int width, int height,
761 int dst_x, int dst_y)
766 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
770 void BlitToScreen(Bitmap *bitmap,
771 int src_x, int src_y, int width, int height,
772 int dst_x, int dst_y)
777 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
778 BlitBitmap(bitmap, gfx.final_screen_bitmap, src_x, src_y,
779 width, height, dst_x, dst_y);
781 BlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y);
784 void BlitToScreenMasked(Bitmap *bitmap,
785 int src_x, int src_y, int width, int height,
786 int dst_x, int dst_y)
791 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
792 BlitBitmapMasked(bitmap, gfx.final_screen_bitmap, src_x, src_y,
793 width, height, dst_x, dst_y);
795 BlitTextureMasked(bitmap, src_x, src_y, width, height, dst_x, dst_y);
798 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
801 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
804 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
807 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
810 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
811 int to_x, int to_y, Pixel pixel, int line_width)
815 for (x = 0; x < line_width; x++)
817 for (y = 0; y < line_width; y++)
819 int dx = x - line_width / 2;
820 int dy = y - line_width / 2;
822 if ((x == 0 && y == 0) ||
823 (x == 0 && y == line_width - 1) ||
824 (x == line_width - 1 && y == 0) ||
825 (x == line_width - 1 && y == line_width - 1))
829 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
834 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
839 for (i = 0; i < num_points - 1; i++)
840 DrawLine(bitmap, points[i].x, points[i].y,
841 points[i + 1].x, points[i + 1].y, pixel, line_width);
844 SDLDrawLines(bitmap->surface, points, num_points, pixel);
848 Pixel GetPixel(Bitmap *bitmap, int x, int y)
850 if (x < 0 || x >= bitmap->width ||
851 y < 0 || y >= bitmap->height)
854 return SDLGetPixel(bitmap, x, y);
857 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
858 unsigned int color_g, unsigned int color_b)
860 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
863 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
865 unsigned int color_r = (color >> 16) & 0xff;
866 unsigned int color_g = (color >> 8) & 0xff;
867 unsigned int color_b = (color >> 0) & 0xff;
869 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
872 void KeyboardAutoRepeatOn(void)
874 #if defined(TARGET_SDL2)
875 keyrepeat_status = TRUE;
877 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
878 SDL_DEFAULT_REPEAT_INTERVAL / 2);
879 SDL_EnableUNICODE(1);
883 void KeyboardAutoRepeatOff(void)
885 #if defined(TARGET_SDL2)
886 keyrepeat_status = FALSE;
888 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
889 SDL_EnableUNICODE(0);
893 boolean SetVideoMode(boolean fullscreen)
895 return SDLSetVideoMode(fullscreen);
898 void SetVideoFrameDelay(unsigned int frame_delay_value)
900 video.frame_delay_value = frame_delay_value;
903 unsigned int GetVideoFrameDelay()
905 return video.frame_delay_value;
908 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
910 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
911 (!fullscreen && video.fullscreen_enabled))
912 fullscreen = SetVideoMode(fullscreen);
917 Bitmap *LoadImage(char *filename)
921 new_bitmap = SDLLoadImage(filename);
924 new_bitmap->source_filename = getStringCopy(filename);
929 Bitmap *LoadCustomImage(char *basename)
931 char *filename = getCustomImageFilename(basename);
934 if (filename == NULL)
935 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
937 if ((new_bitmap = LoadImage(filename)) == NULL)
938 Error(ERR_EXIT, "LoadImage('%s') failed: %s", basename, GetError());
943 void ReloadCustomImage(Bitmap *bitmap, char *basename)
945 char *filename = getCustomImageFilename(basename);
948 if (filename == NULL) /* (should never happen) */
950 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
954 if (strEqual(filename, bitmap->source_filename))
956 /* The old and new image are the same (have the same filename and path).
957 This usually means that this image does not exist in this graphic set
958 and a fallback to the existing image is done. */
963 if ((new_bitmap = LoadImage(filename)) == NULL)
965 Error(ERR_WARN, "LoadImage('%s') failed: %s", basename, GetError());
969 if (bitmap->width != new_bitmap->width ||
970 bitmap->height != new_bitmap->height)
972 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
974 FreeBitmap(new_bitmap);
978 TransferBitmapPointers(new_bitmap, bitmap);
982 static Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
984 return SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
987 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
989 if (bitmaps[IMG_BITMAP_CUSTOM])
991 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
993 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
996 if (gfx.game_tile_size == gfx.standard_tile_size)
998 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1003 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
1004 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
1005 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
1007 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
1009 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
1010 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
1013 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1014 int tile_size, boolean create_small_bitmaps)
1016 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1017 Bitmap *tmp_bitmap_final = NULL;
1018 Bitmap *tmp_bitmap_0 = NULL;
1019 Bitmap *tmp_bitmap_1 = NULL;
1020 Bitmap *tmp_bitmap_2 = NULL;
1021 Bitmap *tmp_bitmap_4 = NULL;
1022 Bitmap *tmp_bitmap_8 = NULL;
1023 Bitmap *tmp_bitmap_16 = NULL;
1024 Bitmap *tmp_bitmap_32 = NULL;
1025 int width_final, height_final;
1026 int width_0, height_0;
1027 int width_1, height_1;
1028 int width_2, height_2;
1029 int width_4, height_4;
1030 int width_8, height_8;
1031 int width_16, height_16;
1032 int width_32, height_32;
1033 int old_width, old_height;
1036 print_timestamp_init("CreateScaledBitmaps");
1038 old_width = old_bitmap->width;
1039 old_height = old_bitmap->height;
1041 /* calculate new image dimensions for final image size */
1042 width_final = old_width * zoom_factor;
1043 height_final = old_height * zoom_factor;
1045 /* get image with final size (this might require scaling up) */
1046 /* ("final" size may result in non-standard tile size image) */
1047 if (zoom_factor != 1)
1048 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1050 tmp_bitmap_final = old_bitmap;
1052 UPDATE_BUSY_STATE();
1054 width_0 = width_1 = width_final;
1055 height_0 = height_1 = height_final;
1057 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1059 if (create_small_bitmaps)
1061 /* check if we have a non-gameplay tile size image */
1062 if (tile_size != gfx.game_tile_size)
1064 /* get image with gameplay tile size */
1065 width_0 = width_final * gfx.game_tile_size / tile_size;
1066 height_0 = height_final * gfx.game_tile_size / tile_size;
1068 if (width_0 == old_width)
1069 tmp_bitmap_0 = old_bitmap;
1070 else if (width_0 == width_final)
1071 tmp_bitmap_0 = tmp_bitmap_final;
1073 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1075 UPDATE_BUSY_STATE();
1078 /* check if we have a non-standard tile size image */
1079 if (tile_size != gfx.standard_tile_size)
1081 /* get image with standard tile size */
1082 width_1 = width_final * gfx.standard_tile_size / tile_size;
1083 height_1 = height_final * gfx.standard_tile_size / tile_size;
1085 if (width_1 == old_width)
1086 tmp_bitmap_1 = old_bitmap;
1087 else if (width_1 == width_final)
1088 tmp_bitmap_1 = tmp_bitmap_final;
1089 else if (width_1 == width_0)
1090 tmp_bitmap_1 = tmp_bitmap_0;
1092 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1094 UPDATE_BUSY_STATE();
1097 /* calculate new image dimensions for small images */
1098 width_2 = width_1 / 2;
1099 height_2 = height_1 / 2;
1100 width_4 = width_1 / 4;
1101 height_4 = height_1 / 4;
1102 width_8 = width_1 / 8;
1103 height_8 = height_1 / 8;
1104 width_16 = width_1 / 16;
1105 height_16 = height_1 / 16;
1106 width_32 = width_1 / 32;
1107 height_32 = height_1 / 32;
1109 /* get image with 1/2 of normal size (for use in the level editor) */
1110 if (width_2 == old_width)
1111 tmp_bitmap_2 = old_bitmap;
1113 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1115 UPDATE_BUSY_STATE();
1117 /* get image with 1/4 of normal size (for use in the level editor) */
1118 if (width_4 == old_width)
1119 tmp_bitmap_4 = old_bitmap;
1121 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1123 UPDATE_BUSY_STATE();
1125 /* get image with 1/8 of normal size (for use on the preview screen) */
1126 if (width_8 == old_width)
1127 tmp_bitmap_8 = old_bitmap;
1129 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1131 UPDATE_BUSY_STATE();
1133 /* get image with 1/16 of normal size (for use on the preview screen) */
1134 if (width_16 == old_width)
1135 tmp_bitmap_16 = old_bitmap;
1137 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1139 UPDATE_BUSY_STATE();
1141 /* get image with 1/32 of normal size (for use on the preview screen) */
1142 if (width_32 == old_width)
1143 tmp_bitmap_32 = old_bitmap;
1145 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1147 UPDATE_BUSY_STATE();
1149 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1150 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1151 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1152 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1153 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1154 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1156 if (width_0 != width_1)
1157 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1159 if (bitmaps[IMG_BITMAP_CUSTOM])
1160 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1162 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1164 boolean free_old_bitmap = TRUE;
1166 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1167 if (bitmaps[i] == old_bitmap)
1168 free_old_bitmap = FALSE;
1170 if (free_old_bitmap)
1171 FreeBitmap(old_bitmap);
1175 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1178 UPDATE_BUSY_STATE();
1180 print_timestamp_done("CreateScaledBitmaps");
1183 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1186 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1189 void CreateBitmapTextures(Bitmap **bitmaps)
1191 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1194 void FreeBitmapTextures(Bitmap **bitmaps)
1196 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1199 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1201 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1205 /* ------------------------------------------------------------------------- */
1206 /* mouse pointer functions */
1207 /* ------------------------------------------------------------------------- */
1209 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1211 /* XPM image definitions */
1212 static const char *cursor_image_none[] =
1214 /* width height num_colors chars_per_pixel */
1244 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1245 static const char *cursor_image_dot[] =
1247 /* width height num_colors chars_per_pixel */
1276 static const char **cursor_image_playfield = cursor_image_dot;
1278 /* some people complained about a "white dot" on the screen and thought it
1279 was a graphical error... OK, let's just remove the whole pointer :-) */
1280 static const char **cursor_image_playfield = cursor_image_none;
1283 static const int cursor_bit_order = BIT_ORDER_MSB;
1285 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1287 struct MouseCursorInfo *cursor;
1288 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1289 int header_lines = 4;
1292 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1294 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1297 for (y = 0; y < cursor->width; y++)
1299 for (x = 0; x < cursor->height; x++)
1302 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1307 cursor->data[i] = cursor->mask[i] = 0;
1310 switch (image[header_lines + y][x])
1313 cursor->data[i] |= bit_mask;
1314 cursor->mask[i] |= bit_mask;
1318 cursor->mask[i] |= bit_mask;
1327 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1332 void SetMouseCursor(int mode)
1334 static struct MouseCursorInfo *cursor_none = NULL;
1335 static struct MouseCursorInfo *cursor_playfield = NULL;
1336 struct MouseCursorInfo *cursor_new;
1338 if (cursor_none == NULL)
1339 cursor_none = get_cursor_from_image(cursor_image_none);
1341 if (cursor_playfield == NULL)
1342 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1344 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1345 mode == CURSOR_NONE ? cursor_none :
1346 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1348 SDLSetMouseCursor(cursor_new);
1350 gfx.cursor_mode = mode;
1354 /* ========================================================================= */
1355 /* audio functions */
1356 /* ========================================================================= */
1358 void OpenAudio(void)
1360 /* always start with reliable default values */
1361 audio.sound_available = FALSE;
1362 audio.music_available = FALSE;
1363 audio.loops_available = FALSE;
1365 audio.sound_enabled = FALSE;
1366 audio.sound_deactivated = FALSE;
1368 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1369 audio.mixer_pid = 0;
1370 audio.device_name = NULL;
1371 audio.device_fd = -1;
1373 audio.num_channels = 0;
1374 audio.music_channel = 0;
1375 audio.first_sound_channel = 0;
1380 void CloseAudio(void)
1384 audio.sound_enabled = FALSE;
1387 void SetAudioMode(boolean enabled)
1389 if (!audio.sound_available)
1392 audio.sound_enabled = enabled;
1396 /* ========================================================================= */
1397 /* event functions */
1398 /* ========================================================================= */
1400 void InitEventFilter(EventFilter filter_function)
1402 /* set event filter to filter out certain events */
1403 #if defined(TARGET_SDL2)
1404 SDL_SetEventFilter(filter_function, NULL);
1406 SDL_SetEventFilter(filter_function);
1410 boolean PendingEvent(void)
1412 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1415 void NextEvent(Event *event)
1417 SDLNextEvent(event);
1420 void PeekEvent(Event *event)
1422 #if defined(TARGET_SDL2)
1423 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1425 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1429 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1431 #if defined(TARGET_SDL2)
1432 /* key up/down events in SDL2 do not return text characters anymore */
1433 return event->keysym.sym;
1436 #if ENABLE_UNUSED_CODE
1437 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1438 (int)event->keysym.unicode,
1439 (int)event->keysym.sym,
1440 (int)SDL_GetModState());
1443 if (with_modifiers &&
1444 event->keysym.unicode > 0x0000 &&
1445 event->keysym.unicode < 0x2000)
1446 return event->keysym.unicode;
1448 return event->keysym.sym;
1453 KeyMod HandleKeyModState(Key key, int key_status)
1455 static KeyMod current_modifiers = KMOD_None;
1457 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1459 KeyMod new_modifier = KMOD_None;
1464 new_modifier = KMOD_Shift_L;
1467 new_modifier = KMOD_Shift_R;
1469 case KSYM_Control_L:
1470 new_modifier = KMOD_Control_L;
1472 case KSYM_Control_R:
1473 new_modifier = KMOD_Control_R;
1476 new_modifier = KMOD_Meta_L;
1479 new_modifier = KMOD_Meta_R;
1482 new_modifier = KMOD_Alt_L;
1485 new_modifier = KMOD_Alt_R;
1491 if (key_status == KEY_PRESSED)
1492 current_modifiers |= new_modifier;
1494 current_modifiers &= ~new_modifier;
1497 return current_modifiers;
1500 KeyMod GetKeyModState()
1502 return (KeyMod)SDL_GetModState();
1505 KeyMod GetKeyModStateFromEvents()
1507 /* always use key modifier state as tracked from key events (this is needed
1508 if the modifier key event was injected into the event queue, but the key
1509 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1510 query the keys as held pressed on the keyboard) -- this case is currently
1511 only used to filter out clipboard insert events from "True X-Mouse" tool */
1513 return HandleKeyModState(KSYM_UNDEFINED, 0);
1516 void StartTextInput(int x, int y)
1518 #if defined(TARGET_SDL2)
1519 SDL_StartTextInput();
1521 #if defined(HAS_SCREEN_KEYBOARD)
1522 if (y > video.height / 2)
1523 video.shifted_up = TRUE;
1528 void StopTextInput()
1530 #if defined(TARGET_SDL2)
1531 SDL_StopTextInput();
1533 #if defined(HAS_SCREEN_KEYBOARD)
1534 video.shifted_up = FALSE;
1539 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1541 if (event->type != EVENT_CLIENTMESSAGE)
1544 return TRUE; /* the only possible message here is SDL_QUIT */
1548 /* ========================================================================= */
1549 /* joystick functions */
1550 /* ========================================================================= */
1552 void InitJoysticks()
1556 #if defined(NO_JOYSTICK)
1557 return; /* joysticks generally deactivated by compile-time directive */
1560 /* always start with reliable default values */
1561 joystick.status = JOYSTICK_NOT_AVAILABLE;
1562 for (i = 0; i < MAX_PLAYERS; i++)
1563 joystick.fd[i] = -1; /* joystick device closed */
1568 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1570 return SDLReadJoystick(nr, x, y, b1, b2);