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 SDLInitVideoBuffer(fullscreen);
391 video.initialized = TRUE;
396 inline static void FreeBitmapPointers(Bitmap *bitmap)
401 SDLFreeBitmapPointers(bitmap);
403 checked_free(bitmap->source_filename);
404 bitmap->source_filename = NULL;
407 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
410 if (src_bitmap == NULL || dst_bitmap == NULL)
413 FreeBitmapPointers(dst_bitmap);
415 *dst_bitmap = *src_bitmap;
418 void FreeBitmap(Bitmap *bitmap)
423 FreeBitmapPointers(bitmap);
428 Bitmap *CreateBitmapStruct(void)
430 return checked_calloc(sizeof(Bitmap));
433 Bitmap *CreateBitmap(int width, int height, int depth)
435 Bitmap *new_bitmap = CreateBitmapStruct();
436 int real_width = MAX(1, width); /* prevent zero bitmap width */
437 int real_height = MAX(1, height); /* prevent zero bitmap height */
438 int real_depth = GetRealDepth(depth);
440 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
442 new_bitmap->width = real_width;
443 new_bitmap->height = real_height;
448 void ReCreateBitmap(Bitmap **bitmap, int width, int height)
450 Bitmap *new_bitmap = CreateBitmap(width, height, DEFAULT_DEPTH);
454 *bitmap = new_bitmap;
458 TransferBitmapPointers(new_bitmap, *bitmap);
463 void CloseWindow(DrawWindow *window)
467 void SetRedrawMaskFromArea(int x, int y, int width, int height)
471 int x2 = x + width - 1;
472 int y2 = y + height - 1;
474 if (width == 0 || height == 0)
477 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
478 redraw_mask |= REDRAW_FIELD;
479 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
480 redraw_mask |= REDRAW_DOOR_1;
481 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
482 redraw_mask |= REDRAW_DOOR_2;
483 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
484 redraw_mask |= REDRAW_DOOR_3;
486 redraw_mask = REDRAW_ALL;
489 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
492 if (draw_mask == REDRAW_NONE)
495 if (draw_mask & REDRAW_ALL)
498 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
501 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
504 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
507 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
513 boolean DrawingDeactivated(int x, int y, int width, int height)
515 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
518 boolean DrawingOnBackground(int x, int y)
520 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
521 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
524 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
525 int *width, int *height, boolean is_dest)
527 int clip_x, clip_y, clip_width, clip_height;
529 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
531 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
532 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
533 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
534 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
540 clip_width = bitmap->width;
541 clip_height = bitmap->height;
544 /* skip if rectangle completely outside bitmap */
546 if (*x + *width <= clip_x ||
547 *y + *height <= clip_y ||
548 *x >= clip_x + clip_width ||
549 *y >= clip_y + clip_height)
552 /* clip if rectangle overlaps bitmap */
556 *width -= clip_x - *x;
559 else if (*x + *width > clip_x + clip_width)
561 *width = clip_x + clip_width - *x;
566 *height -= clip_y - *y;
569 else if (*y + *height > clip_y + clip_height)
571 *height = clip_y + clip_height - *y;
577 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
578 int src_x, int src_y, int width, int height,
579 int dst_x, int dst_y)
581 int dst_x_unclipped = dst_x;
582 int dst_y_unclipped = dst_y;
584 if (src_bitmap == NULL || dst_bitmap == NULL)
587 if (DrawingDeactivated(dst_x, dst_y, width, height))
590 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
591 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
594 /* source x/y might need adjustment if destination x/y was clipped top/left */
595 src_x += dst_x - dst_x_unclipped;
596 src_y += dst_y - dst_y_unclipped;
598 #if defined(TARGET_SDL2)
599 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
600 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
601 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
602 but is already fixed in SVN and should therefore finally be fixed with
603 the next official SDL release, which is probably version 1.2.14.) */
604 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
606 if (src_bitmap == dst_bitmap)
608 /* needed when blitting directly to same bitmap -- should not be needed with
609 recent SDL libraries, but apparently does not work in 1.2.11 directly */
611 static Bitmap *tmp_bitmap = NULL;
612 static int tmp_bitmap_xsize = 0;
613 static int tmp_bitmap_ysize = 0;
615 /* start with largest static bitmaps for initial bitmap size ... */
616 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
618 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
619 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
622 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
623 if (src_bitmap->width > tmp_bitmap_xsize ||
624 src_bitmap->height > tmp_bitmap_ysize)
626 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
627 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
629 FreeBitmap(tmp_bitmap);
634 if (tmp_bitmap == NULL)
635 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
638 sysCopyArea(src_bitmap, tmp_bitmap,
639 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
640 sysCopyArea(tmp_bitmap, dst_bitmap,
641 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
647 sysCopyArea(src_bitmap, dst_bitmap,
648 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
651 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
652 int src_x, int src_y, int src_width, int src_height,
653 int dst_x, int dst_y, int dst_width, int dst_height)
655 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
656 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
657 int dst_xsize = dst_width;
658 int dst_ysize = dst_height;
659 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
660 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
663 for (y = 0; y < src_ysteps; y++)
665 for (x = 0; x < src_xsteps; x++)
667 int draw_x = dst_x + x * src_xsize;
668 int draw_y = dst_y + y * src_ysize;
669 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
670 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
672 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
678 void FadeRectangle(int x, int y, int width, int height,
679 int fade_mode, int fade_delay, int post_delay,
680 void (*draw_border_function)(void))
682 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
683 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
686 SDLFadeRectangle(x, y, width, height,
687 fade_mode, fade_delay, post_delay, draw_border_function);
690 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
693 if (DrawingDeactivated(x, y, width, height))
696 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
699 sysFillRectangle(bitmap, x, y, width, height, color);
702 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
704 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
707 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
708 int width, int height)
710 if (DrawingOnBackground(x, y))
711 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
713 ClearRectangle(bitmap, x, y, width, height);
716 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
717 int src_x, int src_y, int width, int height,
718 int dst_x, int dst_y)
720 if (DrawingDeactivated(dst_x, dst_y, width, height))
723 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
724 dst_x, dst_y, BLIT_MASKED);
727 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
728 int src_x, int src_y, int width, int height,
729 int dst_x, int dst_y)
731 if (DrawingOnBackground(dst_x, dst_y))
733 /* draw background */
734 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
737 /* draw foreground */
738 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
742 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
746 void BlitTexture(Bitmap *bitmap,
747 int src_x, int src_y, int width, int height,
748 int dst_x, int dst_y)
753 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
757 void BlitTextureMasked(Bitmap *bitmap,
758 int src_x, int src_y, int width, int height,
759 int dst_x, int dst_y)
764 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
768 void BlitToScreen(Bitmap *bitmap,
769 int src_x, int src_y, int width, int height,
770 int dst_x, int dst_y)
775 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
776 BlitBitmap(bitmap, gfx.final_screen_bitmap, src_x, src_y,
777 width, height, dst_x, dst_y);
779 BlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y);
782 void BlitToScreenMasked(Bitmap *bitmap,
783 int src_x, int src_y, int width, int height,
784 int dst_x, int dst_y)
789 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
790 BlitBitmapMasked(bitmap, gfx.final_screen_bitmap, src_x, src_y,
791 width, height, dst_x, dst_y);
793 BlitTextureMasked(bitmap, src_x, src_y, width, height, dst_x, dst_y);
796 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
799 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
802 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
805 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
808 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
809 int to_x, int to_y, Pixel pixel, int line_width)
813 for (x = 0; x < line_width; x++)
815 for (y = 0; y < line_width; y++)
817 int dx = x - line_width / 2;
818 int dy = y - line_width / 2;
820 if ((x == 0 && y == 0) ||
821 (x == 0 && y == line_width - 1) ||
822 (x == line_width - 1 && y == 0) ||
823 (x == line_width - 1 && y == line_width - 1))
827 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
832 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
837 for (i = 0; i < num_points - 1; i++)
838 DrawLine(bitmap, points[i].x, points[i].y,
839 points[i + 1].x, points[i + 1].y, pixel, line_width);
842 SDLDrawLines(bitmap->surface, points, num_points, pixel);
846 Pixel GetPixel(Bitmap *bitmap, int x, int y)
848 if (x < 0 || x >= bitmap->width ||
849 y < 0 || y >= bitmap->height)
852 return SDLGetPixel(bitmap, x, y);
855 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
856 unsigned int color_g, unsigned int color_b)
858 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
861 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
863 unsigned int color_r = (color >> 16) & 0xff;
864 unsigned int color_g = (color >> 8) & 0xff;
865 unsigned int color_b = (color >> 0) & 0xff;
867 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
870 void KeyboardAutoRepeatOn(void)
872 #if defined(TARGET_SDL2)
873 keyrepeat_status = TRUE;
875 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
876 SDL_DEFAULT_REPEAT_INTERVAL / 2);
877 SDL_EnableUNICODE(1);
881 void KeyboardAutoRepeatOff(void)
883 #if defined(TARGET_SDL2)
884 keyrepeat_status = FALSE;
886 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
887 SDL_EnableUNICODE(0);
891 boolean SetVideoMode(boolean fullscreen)
893 return SDLSetVideoMode(fullscreen);
896 void SetVideoFrameDelay(unsigned int frame_delay_value)
898 video.frame_delay_value = frame_delay_value;
901 unsigned int GetVideoFrameDelay()
903 return video.frame_delay_value;
906 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
908 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
909 (!fullscreen && video.fullscreen_enabled))
910 fullscreen = SetVideoMode(fullscreen);
915 Bitmap *LoadImage(char *filename)
919 new_bitmap = SDLLoadImage(filename);
922 new_bitmap->source_filename = getStringCopy(filename);
927 Bitmap *LoadCustomImage(char *basename)
929 char *filename = getCustomImageFilename(basename);
932 if (filename == NULL)
933 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
935 if ((new_bitmap = LoadImage(filename)) == NULL)
936 Error(ERR_EXIT, "LoadImage('%s') failed: %s", basename, GetError());
941 void ReloadCustomImage(Bitmap *bitmap, char *basename)
943 char *filename = getCustomImageFilename(basename);
946 if (filename == NULL) /* (should never happen) */
948 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
952 if (strEqual(filename, bitmap->source_filename))
954 /* The old and new image are the same (have the same filename and path).
955 This usually means that this image does not exist in this graphic set
956 and a fallback to the existing image is done. */
961 if ((new_bitmap = LoadImage(filename)) == NULL)
963 Error(ERR_WARN, "LoadImage('%s') failed: %s", basename, GetError());
967 if (bitmap->width != new_bitmap->width ||
968 bitmap->height != new_bitmap->height)
970 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
972 FreeBitmap(new_bitmap);
976 TransferBitmapPointers(new_bitmap, bitmap);
980 Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
982 Bitmap *dst_bitmap = SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
987 static void SetMaskedBitmapSurface(Bitmap *bitmap)
992 SDL_Surface *surface = bitmap->surface;
994 if (bitmap->surface_masked)
995 SDL_FreeSurface(bitmap->surface_masked);
997 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
998 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
1000 if ((bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
1001 Error(ERR_EXIT, "SDL_DisplayFormat() failed");
1003 SDL_SetColorKey(surface, UNSET_TRANSPARENT_PIXEL, 0);
1006 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
1008 if (bitmaps[IMG_BITMAP_CUSTOM])
1010 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
1012 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
1015 if (gfx.game_tile_size == gfx.standard_tile_size)
1017 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1022 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
1023 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
1024 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
1026 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
1028 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
1029 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
1031 SetMaskedBitmapSurface(bitmap_new);
1034 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1035 int tile_size, boolean create_small_bitmaps)
1037 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1038 Bitmap *tmp_bitmap_final = NULL;
1039 Bitmap *tmp_bitmap_0 = NULL;
1040 Bitmap *tmp_bitmap_1 = NULL;
1041 Bitmap *tmp_bitmap_2 = NULL;
1042 Bitmap *tmp_bitmap_4 = NULL;
1043 Bitmap *tmp_bitmap_8 = NULL;
1044 Bitmap *tmp_bitmap_16 = NULL;
1045 Bitmap *tmp_bitmap_32 = NULL;
1046 int width_final, height_final;
1047 int width_0, height_0;
1048 int width_1, height_1;
1049 int width_2, height_2;
1050 int width_4, height_4;
1051 int width_8, height_8;
1052 int width_16, height_16;
1053 int width_32, height_32;
1054 int old_width, old_height;
1057 print_timestamp_init("CreateScaledBitmaps");
1059 old_width = old_bitmap->width;
1060 old_height = old_bitmap->height;
1062 /* calculate new image dimensions for final image size */
1063 width_final = old_width * zoom_factor;
1064 height_final = old_height * zoom_factor;
1066 /* get image with final size (this might require scaling up) */
1067 /* ("final" size may result in non-standard tile size image) */
1068 if (zoom_factor != 1)
1069 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1071 tmp_bitmap_final = old_bitmap;
1073 UPDATE_BUSY_STATE();
1075 width_0 = width_1 = width_final;
1076 height_0 = height_1 = height_final;
1078 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1080 if (create_small_bitmaps)
1082 /* check if we have a non-gameplay tile size image */
1083 if (tile_size != gfx.game_tile_size)
1085 /* get image with gameplay tile size */
1086 width_0 = width_final * gfx.game_tile_size / tile_size;
1087 height_0 = height_final * gfx.game_tile_size / tile_size;
1089 if (width_0 == old_width)
1090 tmp_bitmap_0 = old_bitmap;
1091 else if (width_0 == width_final)
1092 tmp_bitmap_0 = tmp_bitmap_final;
1094 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1096 UPDATE_BUSY_STATE();
1099 /* check if we have a non-standard tile size image */
1100 if (tile_size != gfx.standard_tile_size)
1102 /* get image with standard tile size */
1103 width_1 = width_final * gfx.standard_tile_size / tile_size;
1104 height_1 = height_final * gfx.standard_tile_size / tile_size;
1106 if (width_1 == old_width)
1107 tmp_bitmap_1 = old_bitmap;
1108 else if (width_1 == width_final)
1109 tmp_bitmap_1 = tmp_bitmap_final;
1110 else if (width_1 == width_0)
1111 tmp_bitmap_1 = tmp_bitmap_0;
1113 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1115 UPDATE_BUSY_STATE();
1118 /* calculate new image dimensions for small images */
1119 width_2 = width_1 / 2;
1120 height_2 = height_1 / 2;
1121 width_4 = width_1 / 4;
1122 height_4 = height_1 / 4;
1123 width_8 = width_1 / 8;
1124 height_8 = height_1 / 8;
1125 width_16 = width_1 / 16;
1126 height_16 = height_1 / 16;
1127 width_32 = width_1 / 32;
1128 height_32 = height_1 / 32;
1130 /* get image with 1/2 of normal size (for use in the level editor) */
1131 if (width_2 == old_width)
1132 tmp_bitmap_2 = old_bitmap;
1134 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1136 UPDATE_BUSY_STATE();
1138 /* get image with 1/4 of normal size (for use in the level editor) */
1139 if (width_4 == old_width)
1140 tmp_bitmap_4 = old_bitmap;
1142 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1144 UPDATE_BUSY_STATE();
1146 /* get image with 1/8 of normal size (for use on the preview screen) */
1147 if (width_8 == old_width)
1148 tmp_bitmap_8 = old_bitmap;
1150 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1152 UPDATE_BUSY_STATE();
1154 /* get image with 1/16 of normal size (for use on the preview screen) */
1155 if (width_16 == old_width)
1156 tmp_bitmap_16 = old_bitmap;
1158 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1160 UPDATE_BUSY_STATE();
1162 /* get image with 1/32 of normal size (for use on the preview screen) */
1163 if (width_32 == old_width)
1164 tmp_bitmap_32 = old_bitmap;
1166 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1168 UPDATE_BUSY_STATE();
1170 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1171 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1172 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1173 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1174 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1175 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1177 if (width_0 != width_1)
1178 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1180 if (bitmaps[IMG_BITMAP_CUSTOM])
1181 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1183 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1185 boolean free_old_bitmap = TRUE;
1187 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1188 if (bitmaps[i] == old_bitmap)
1189 free_old_bitmap = FALSE;
1191 if (free_old_bitmap)
1192 FreeBitmap(old_bitmap);
1196 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1199 // create corresponding bitmaps for masked blitting
1200 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1201 if (bitmaps[i] != NULL &&
1202 bitmaps[i] != old_bitmap)
1203 SetMaskedBitmapSurface(bitmaps[i]);
1205 UPDATE_BUSY_STATE();
1207 print_timestamp_done("CreateScaledBitmaps");
1210 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1213 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1216 void CreateBitmapTextures(Bitmap **bitmaps)
1218 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1221 void FreeBitmapTextures(Bitmap **bitmaps)
1223 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1226 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1228 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1232 /* ------------------------------------------------------------------------- */
1233 /* mouse pointer functions */
1234 /* ------------------------------------------------------------------------- */
1236 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1238 /* XPM image definitions */
1239 static const char *cursor_image_none[] =
1241 /* width height num_colors chars_per_pixel */
1271 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1272 static const char *cursor_image_dot[] =
1274 /* width height num_colors chars_per_pixel */
1303 static const char **cursor_image_playfield = cursor_image_dot;
1305 /* some people complained about a "white dot" on the screen and thought it
1306 was a graphical error... OK, let's just remove the whole pointer :-) */
1307 static const char **cursor_image_playfield = cursor_image_none;
1310 static const int cursor_bit_order = BIT_ORDER_MSB;
1312 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1314 struct MouseCursorInfo *cursor;
1315 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1316 int header_lines = 4;
1319 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1321 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1324 for (y = 0; y < cursor->width; y++)
1326 for (x = 0; x < cursor->height; x++)
1329 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1334 cursor->data[i] = cursor->mask[i] = 0;
1337 switch (image[header_lines + y][x])
1340 cursor->data[i] |= bit_mask;
1341 cursor->mask[i] |= bit_mask;
1345 cursor->mask[i] |= bit_mask;
1354 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1359 void SetMouseCursor(int mode)
1361 static struct MouseCursorInfo *cursor_none = NULL;
1362 static struct MouseCursorInfo *cursor_playfield = NULL;
1363 struct MouseCursorInfo *cursor_new;
1365 if (cursor_none == NULL)
1366 cursor_none = get_cursor_from_image(cursor_image_none);
1368 if (cursor_playfield == NULL)
1369 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1371 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1372 mode == CURSOR_NONE ? cursor_none :
1373 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1375 SDLSetMouseCursor(cursor_new);
1377 gfx.cursor_mode = mode;
1381 /* ========================================================================= */
1382 /* audio functions */
1383 /* ========================================================================= */
1385 void OpenAudio(void)
1387 /* always start with reliable default values */
1388 audio.sound_available = FALSE;
1389 audio.music_available = FALSE;
1390 audio.loops_available = FALSE;
1392 audio.sound_enabled = FALSE;
1393 audio.sound_deactivated = FALSE;
1395 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1396 audio.mixer_pid = 0;
1397 audio.device_name = NULL;
1398 audio.device_fd = -1;
1400 audio.num_channels = 0;
1401 audio.music_channel = 0;
1402 audio.first_sound_channel = 0;
1407 void CloseAudio(void)
1411 audio.sound_enabled = FALSE;
1414 void SetAudioMode(boolean enabled)
1416 if (!audio.sound_available)
1419 audio.sound_enabled = enabled;
1423 /* ========================================================================= */
1424 /* event functions */
1425 /* ========================================================================= */
1427 void InitEventFilter(EventFilter filter_function)
1429 /* set event filter to filter out certain events */
1430 #if defined(TARGET_SDL2)
1431 SDL_SetEventFilter(filter_function, NULL);
1433 SDL_SetEventFilter(filter_function);
1437 boolean PendingEvent(void)
1439 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1442 void NextEvent(Event *event)
1444 SDLNextEvent(event);
1447 void PeekEvent(Event *event)
1449 #if defined(TARGET_SDL2)
1450 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1452 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1456 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1458 #if defined(TARGET_SDL2)
1459 /* key up/down events in SDL2 do not return text characters anymore */
1460 return event->keysym.sym;
1463 #if ENABLE_UNUSED_CODE
1464 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1465 (int)event->keysym.unicode,
1466 (int)event->keysym.sym,
1467 (int)SDL_GetModState());
1470 if (with_modifiers &&
1471 event->keysym.unicode > 0x0000 &&
1472 event->keysym.unicode < 0x2000)
1473 return event->keysym.unicode;
1475 return event->keysym.sym;
1480 KeyMod HandleKeyModState(Key key, int key_status)
1482 static KeyMod current_modifiers = KMOD_None;
1484 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1486 KeyMod new_modifier = KMOD_None;
1491 new_modifier = KMOD_Shift_L;
1494 new_modifier = KMOD_Shift_R;
1496 case KSYM_Control_L:
1497 new_modifier = KMOD_Control_L;
1499 case KSYM_Control_R:
1500 new_modifier = KMOD_Control_R;
1503 new_modifier = KMOD_Meta_L;
1506 new_modifier = KMOD_Meta_R;
1509 new_modifier = KMOD_Alt_L;
1512 new_modifier = KMOD_Alt_R;
1518 if (key_status == KEY_PRESSED)
1519 current_modifiers |= new_modifier;
1521 current_modifiers &= ~new_modifier;
1524 return current_modifiers;
1527 KeyMod GetKeyModState()
1529 return (KeyMod)SDL_GetModState();
1532 KeyMod GetKeyModStateFromEvents()
1534 /* always use key modifier state as tracked from key events (this is needed
1535 if the modifier key event was injected into the event queue, but the key
1536 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1537 query the keys as held pressed on the keyboard) -- this case is currently
1538 only used to filter out clipboard insert events from "True X-Mouse" tool */
1540 return HandleKeyModState(KSYM_UNDEFINED, 0);
1543 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1545 if (event->type != EVENT_CLIENTMESSAGE)
1548 return TRUE; /* the only possible message here is SDL_QUIT */
1552 /* ========================================================================= */
1553 /* joystick functions */
1554 /* ========================================================================= */
1556 void InitJoysticks()
1560 #if defined(NO_JOYSTICK)
1561 return; /* joysticks generally deactivated by compile-time directive */
1564 /* always start with reliable default values */
1565 joystick.status = JOYSTICK_NOT_AVAILABLE;
1566 for (i = 0; i < MAX_PLAYERS; i++)
1567 joystick.fd[i] = -1; /* joystick device closed */
1572 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1574 return SDLReadJoystick(nr, x, y, b1, b2);