1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
24 #define ENABLE_UNUSED_CODE 0 /* currently unused functions */
27 /* ========================================================================= */
28 /* exported variables */
29 /* ========================================================================= */
31 struct ProgramInfo program;
32 struct OptionInfo options;
33 struct VideoSystemInfo video;
34 struct AudioSystemInfo audio;
36 struct ArtworkInfo artwork;
37 struct JoystickInfo joystick;
38 struct SetupInfo setup;
40 LevelDirTree *leveldir_first_all = NULL;
41 LevelDirTree *leveldir_first = NULL;
42 LevelDirTree *leveldir_current = NULL;
45 struct LevelStats level_stats[MAX_LEVELS];
47 DrawWindow *window = NULL;
48 DrawBuffer *backbuffer = NULL;
49 DrawBuffer *drawto = NULL;
51 int button_status = MB_NOT_PRESSED;
52 boolean motion_status = FALSE;
53 #if defined(TARGET_SDL2)
54 boolean keyrepeat_status = TRUE;
57 int redraw_mask = REDRAW_NONE;
62 /* ========================================================================= */
63 /* init/close functions */
64 /* ========================================================================= */
66 void InitProgramInfo(char *argv0, char *config_filename, char *userdata_subdir,
67 char *program_title, char *icon_title,
68 char *icon_filename, char *cookie_prefix,
71 program.command_basepath = getBasePath(argv0);
72 program.command_basename = getBaseName(argv0);
74 program.config_filename = config_filename;
76 program.userdata_subdir = userdata_subdir;
77 program.userdata_path = getUserGameDataDir();
79 program.program_title = program_title;
80 program.window_title = "(undefined)";
81 program.icon_title = icon_title;
83 program.icon_filename = icon_filename;
85 program.cookie_prefix = cookie_prefix;
87 program.version_major = VERSION_MAJOR(program_version);
88 program.version_minor = VERSION_MINOR(program_version);
89 program.version_patch = VERSION_PATCH(program_version);
90 program.version_build = VERSION_BUILD(program_version);
91 program.version_ident = program_version;
93 program.log_filename[LOG_OUT_ID] = getLogFilename(LOG_OUT_BASENAME);
94 program.log_filename[LOG_ERR_ID] = getLogFilename(LOG_ERR_BASENAME);
95 program.log_file[LOG_OUT_ID] = program.log_file_default[LOG_OUT_ID] = stdout;
96 program.log_file[LOG_ERR_ID] = program.log_file_default[LOG_ERR_ID] = stderr;
101 program.window_title = program.window_title_function();
106 void InitWindowTitleFunction(char *(*window_title_function)(void))
108 program.window_title_function = window_title_function;
111 void InitExitMessageFunction(void (*exit_message_function)(char *, va_list))
113 program.exit_message_function = exit_message_function;
116 void InitExitFunction(void (*exit_function)(int))
118 program.exit_function = exit_function;
120 /* set signal handlers to custom exit function */
121 // signal(SIGINT, exit_function);
122 signal(SIGTERM, exit_function);
124 /* set exit function to automatically cleanup SDL stuff after exit() */
128 void InitPlatformDependentStuff(void)
130 // this is initialized in GetOptions(), but may already be used before
131 options.verbose = TRUE;
135 #if defined(TARGET_SDL2)
136 int sdl_init_flags = SDL_INIT_EVENTS | SDL_INIT_NOPARACHUTE;
138 int sdl_init_flags = SDL_INIT_EVENTTHREAD | SDL_INIT_NOPARACHUTE;
141 if (SDL_Init(sdl_init_flags) < 0)
142 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
147 void ClosePlatformDependentStuff(void)
152 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
153 int real_sx, int real_sy,
154 int full_sxsize, int full_sysize,
155 Bitmap *field_save_buffer)
161 gfx.real_sx = real_sx;
162 gfx.real_sy = real_sy;
163 gfx.full_sxsize = full_sxsize;
164 gfx.full_sysize = full_sysize;
166 gfx.field_save_buffer = field_save_buffer;
168 SetDrawDeactivationMask(REDRAW_NONE); /* do not deactivate drawing */
169 SetDrawBackgroundMask(REDRAW_NONE); /* deactivate masked drawing */
172 void InitGfxTileSizeInfo(int game_tile_size, int standard_tile_size)
174 gfx.game_tile_size = game_tile_size;
175 gfx.standard_tile_size = standard_tile_size;
178 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
186 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
194 void InitGfxDoor3Info(int ex, int ey, int exsize, int eysize)
202 void InitGfxWindowInfo(int win_xsize, int win_ysize)
204 if (win_xsize != gfx.win_xsize || win_ysize != gfx.win_ysize)
206 ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize, DEFAULT_DEPTH);
208 #if defined(TARGET_SDL2)
209 ReCreateBitmap(&gfx.final_screen_bitmap, win_xsize, win_ysize, DEFAULT_DEPTH);
212 ReCreateBitmap(&gfx.fade_bitmap_backup, win_xsize, win_ysize, DEFAULT_DEPTH);
213 ReCreateBitmap(&gfx.fade_bitmap_source, win_xsize, win_ysize, DEFAULT_DEPTH);
214 ReCreateBitmap(&gfx.fade_bitmap_target, win_xsize, win_ysize, DEFAULT_DEPTH);
215 ReCreateBitmap(&gfx.fade_bitmap_black, win_xsize, win_ysize, DEFAULT_DEPTH);
217 ClearRectangle(gfx.fade_bitmap_black, 0, 0, win_xsize, win_ysize);
220 gfx.win_xsize = win_xsize;
221 gfx.win_ysize = win_ysize;
223 gfx.background_bitmap_mask = REDRAW_NONE;
226 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
228 /* currently only used by MSDOS code to alloc VRAM buffer, if available */
229 /* 2009-03-24: also (temporarily?) used for overlapping blit workaround */
230 gfx.scrollbuffer_width = scrollbuffer_width;
231 gfx.scrollbuffer_height = scrollbuffer_height;
234 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
236 gfx.clipping_enabled = enabled;
239 gfx.clip_width = width;
240 gfx.clip_height = height;
243 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
245 gfx.draw_busy_anim_function = draw_busy_anim_function;
248 void InitGfxDrawGlobalAnimFunction(void (*draw_global_anim_function)(int, int))
250 gfx.draw_global_anim_function = draw_global_anim_function;
253 void InitGfxDrawGlobalBorderFunction(void (*draw_global_border_function)(int))
255 gfx.draw_global_border_function = draw_global_border_function;
258 void InitGfxCustomArtworkInfo()
260 gfx.override_level_graphics = FALSE;
261 gfx.override_level_sounds = FALSE;
262 gfx.override_level_music = FALSE;
264 gfx.draw_init_text = TRUE;
267 void InitGfxOtherSettings()
269 gfx.cursor_mode = CURSOR_DEFAULT;
272 void SetDrawDeactivationMask(int draw_deactivation_mask)
274 gfx.draw_deactivation_mask = draw_deactivation_mask;
277 void SetDrawBackgroundMask(int draw_background_mask)
279 gfx.draw_background_mask = draw_background_mask;
282 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
284 if (background_bitmap_tile != NULL)
285 gfx.background_bitmap_mask |= mask;
287 gfx.background_bitmap_mask &= ~mask;
289 if (background_bitmap_tile == NULL) /* empty background requested */
292 if (mask == REDRAW_ALL)
293 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
294 0, 0, video.width, video.height);
295 else if (mask == REDRAW_FIELD)
296 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
297 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
298 else if (mask == REDRAW_DOOR_1)
299 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
300 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
303 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
305 /* remove every mask before setting mask for window */
306 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
307 SetBackgroundBitmap(NULL, 0xffff); /* !!! FIX THIS !!! */
308 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
311 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
313 /* remove window area mask before setting mask for main area */
314 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
315 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
316 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
319 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
321 /* remove window area mask before setting mask for door area */
322 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
323 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
324 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
328 /* ========================================================================= */
329 /* video functions */
330 /* ========================================================================= */
332 inline static int GetRealDepth(int depth)
334 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
337 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
338 int width, int height, Pixel color)
340 SDLFillRectangle(bitmap, x, y, width, height, color);
342 if (bitmap == backbuffer)
343 SetRedrawMaskFromArea(x, y, width, height);
346 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
347 int src_x, int src_y, int width, int height,
348 int dst_x, int dst_y, int mask_mode)
350 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
351 dst_x, dst_y, mask_mode);
353 if (dst_bitmap == backbuffer)
354 SetRedrawMaskFromArea(dst_x, dst_y, width, height);
357 void LimitScreenUpdates(boolean enable)
359 SDLLimitScreenUpdates(enable);
362 void InitVideoDisplay(void)
364 SDLInitVideoDisplay();
367 void CloseVideoDisplay(void)
369 KeyboardAutoRepeatOn();
371 SDL_QuitSubSystem(SDL_INIT_VIDEO);
374 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
377 video.height = height;
378 video.depth = GetRealDepth(depth);
380 video.fullscreen_available = FULLSCREEN_STATUS;
381 video.fullscreen_enabled = FALSE;
383 video.window_scaling_available = WINDOW_SCALING_STATUS;
385 video.frame_delay = 0;
386 video.frame_delay_value = GAME_FRAME_DELAY;
388 SDLInitVideoBuffer(fullscreen);
390 video.initialized = TRUE;
395 inline static void FreeBitmapPointers(Bitmap *bitmap)
400 SDLFreeBitmapPointers(bitmap);
402 checked_free(bitmap->source_filename);
403 bitmap->source_filename = NULL;
406 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
409 if (src_bitmap == NULL || dst_bitmap == NULL)
412 FreeBitmapPointers(dst_bitmap);
414 *dst_bitmap = *src_bitmap;
417 void FreeBitmap(Bitmap *bitmap)
422 FreeBitmapPointers(bitmap);
427 Bitmap *CreateBitmapStruct(void)
429 return checked_calloc(sizeof(Bitmap));
432 Bitmap *CreateBitmap(int width, int height, int depth)
434 Bitmap *new_bitmap = CreateBitmapStruct();
435 int real_width = MAX(1, width); /* prevent zero bitmap width */
436 int real_height = MAX(1, height); /* prevent zero bitmap height */
437 int real_depth = GetRealDepth(depth);
439 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
441 new_bitmap->width = real_width;
442 new_bitmap->height = real_height;
447 void ReCreateBitmap(Bitmap **bitmap, int width, int height, int depth)
449 Bitmap *new_bitmap = CreateBitmap(width, height, depth);
453 *bitmap = new_bitmap;
457 TransferBitmapPointers(new_bitmap, *bitmap);
462 void CloseWindow(DrawWindow *window)
466 void SetRedrawMaskFromArea(int x, int y, int width, int height)
470 int x2 = x + width - 1;
471 int y2 = y + height - 1;
473 if (width == 0 || height == 0)
476 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
477 redraw_mask |= REDRAW_FIELD;
478 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
479 redraw_mask |= REDRAW_DOOR_1;
480 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
481 redraw_mask |= REDRAW_DOOR_2;
482 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
483 redraw_mask |= REDRAW_DOOR_3;
485 redraw_mask = REDRAW_ALL;
488 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
491 if (draw_mask == REDRAW_NONE)
494 if (draw_mask & REDRAW_ALL)
497 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
500 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
503 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
506 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
512 boolean DrawingDeactivated(int x, int y, int width, int height)
514 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
517 boolean DrawingOnBackground(int x, int y)
519 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
520 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
523 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
524 int *width, int *height, boolean is_dest)
526 int clip_x, clip_y, clip_width, clip_height;
528 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
530 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
531 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
532 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
533 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
539 clip_width = bitmap->width;
540 clip_height = bitmap->height;
543 /* skip if rectangle completely outside bitmap */
545 if (*x + *width <= clip_x ||
546 *y + *height <= clip_y ||
547 *x >= clip_x + clip_width ||
548 *y >= clip_y + clip_height)
551 /* clip if rectangle overlaps bitmap */
555 *width -= clip_x - *x;
558 else if (*x + *width > clip_x + clip_width)
560 *width = clip_x + clip_width - *x;
565 *height -= clip_y - *y;
568 else if (*y + *height > clip_y + clip_height)
570 *height = clip_y + clip_height - *y;
576 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
577 int src_x, int src_y, int width, int height,
578 int dst_x, int dst_y)
580 int dst_x_unclipped = dst_x;
581 int dst_y_unclipped = dst_y;
583 if (src_bitmap == NULL || dst_bitmap == NULL)
586 if (DrawingDeactivated(dst_x, dst_y, width, height))
589 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
590 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
593 /* source x/y might need adjustment if destination x/y was clipped top/left */
594 src_x += dst_x - dst_x_unclipped;
595 src_y += dst_y - dst_y_unclipped;
597 #if defined(TARGET_SDL2)
598 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
599 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
600 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
601 but is already fixed in SVN and should therefore finally be fixed with
602 the next official SDL release, which is probably version 1.2.14.) */
603 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
605 if (src_bitmap == dst_bitmap)
607 /* needed when blitting directly to same bitmap -- should not be needed with
608 recent SDL libraries, but apparently does not work in 1.2.11 directly */
610 static Bitmap *tmp_bitmap = NULL;
611 static int tmp_bitmap_xsize = 0;
612 static int tmp_bitmap_ysize = 0;
614 /* start with largest static bitmaps for initial bitmap size ... */
615 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
617 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
618 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
621 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
622 if (src_bitmap->width > tmp_bitmap_xsize ||
623 src_bitmap->height > tmp_bitmap_ysize)
625 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
626 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
628 FreeBitmap(tmp_bitmap);
633 if (tmp_bitmap == NULL)
634 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
637 sysCopyArea(src_bitmap, tmp_bitmap,
638 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
639 sysCopyArea(tmp_bitmap, dst_bitmap,
640 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
646 sysCopyArea(src_bitmap, dst_bitmap,
647 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
650 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
651 int src_x, int src_y, int src_width, int src_height,
652 int dst_x, int dst_y, int dst_width, int dst_height)
654 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
655 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
656 int dst_xsize = dst_width;
657 int dst_ysize = dst_height;
658 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
659 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
662 for (y = 0; y < src_ysteps; y++)
664 for (x = 0; x < src_xsteps; x++)
666 int draw_x = dst_x + x * src_xsize;
667 int draw_y = dst_y + y * src_ysize;
668 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
669 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
671 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
677 void FadeRectangle(int x, int y, int width, int height,
678 int fade_mode, int fade_delay, int post_delay,
679 void (*draw_border_function)(void))
681 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
682 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
685 SDLFadeRectangle(x, y, width, height,
686 fade_mode, fade_delay, post_delay, draw_border_function);
689 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
692 if (DrawingDeactivated(x, y, width, height))
695 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
698 sysFillRectangle(bitmap, x, y, width, height, color);
701 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
703 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
706 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
707 int width, int height)
709 if (DrawingOnBackground(x, y))
710 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
712 ClearRectangle(bitmap, x, y, width, height);
715 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
716 int src_x, int src_y, int width, int height,
717 int dst_x, int dst_y)
719 if (DrawingDeactivated(dst_x, dst_y, width, height))
722 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
723 dst_x, dst_y, BLIT_MASKED);
726 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
727 int src_x, int src_y, int width, int height,
728 int dst_x, int dst_y)
730 if (DrawingOnBackground(dst_x, dst_y))
732 /* draw background */
733 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
736 /* draw foreground */
737 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
741 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
745 void BlitTexture(Bitmap *bitmap,
746 int src_x, int src_y, int width, int height,
747 int dst_x, int dst_y)
752 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
756 void BlitTextureMasked(Bitmap *bitmap,
757 int src_x, int src_y, int width, int height,
758 int dst_x, int dst_y)
763 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
767 void BlitToScreen(Bitmap *bitmap,
768 int src_x, int src_y, int width, int height,
769 int dst_x, int dst_y)
774 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
775 BlitBitmap(bitmap, gfx.final_screen_bitmap, src_x, src_y,
776 width, height, dst_x, dst_y);
778 BlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y);
781 void BlitToScreenMasked(Bitmap *bitmap,
782 int src_x, int src_y, int width, int height,
783 int dst_x, int dst_y)
788 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
789 BlitBitmapMasked(bitmap, gfx.final_screen_bitmap, src_x, src_y,
790 width, height, dst_x, dst_y);
792 BlitTextureMasked(bitmap, src_x, src_y, width, height, dst_x, dst_y);
795 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
798 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
801 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
804 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
807 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
808 int to_x, int to_y, Pixel pixel, int line_width)
812 for (x = 0; x < line_width; x++)
814 for (y = 0; y < line_width; y++)
816 int dx = x - line_width / 2;
817 int dy = y - line_width / 2;
819 if ((x == 0 && y == 0) ||
820 (x == 0 && y == line_width - 1) ||
821 (x == line_width - 1 && y == 0) ||
822 (x == line_width - 1 && y == line_width - 1))
826 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
831 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
836 for (i = 0; i < num_points - 1; i++)
837 DrawLine(bitmap, points[i].x, points[i].y,
838 points[i + 1].x, points[i + 1].y, pixel, line_width);
841 SDLDrawLines(bitmap->surface, points, num_points, pixel);
845 Pixel GetPixel(Bitmap *bitmap, int x, int y)
847 if (x < 0 || x >= bitmap->width ||
848 y < 0 || y >= bitmap->height)
851 return SDLGetPixel(bitmap, x, y);
854 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
855 unsigned int color_g, unsigned int color_b)
857 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
860 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
862 unsigned int color_r = (color >> 16) & 0xff;
863 unsigned int color_g = (color >> 8) & 0xff;
864 unsigned int color_b = (color >> 0) & 0xff;
866 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
869 void KeyboardAutoRepeatOn(void)
871 #if defined(TARGET_SDL2)
872 keyrepeat_status = TRUE;
874 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
875 SDL_DEFAULT_REPEAT_INTERVAL / 2);
876 SDL_EnableUNICODE(1);
880 void KeyboardAutoRepeatOff(void)
882 #if defined(TARGET_SDL2)
883 keyrepeat_status = FALSE;
885 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
886 SDL_EnableUNICODE(0);
890 boolean SetVideoMode(boolean fullscreen)
892 return SDLSetVideoMode(fullscreen);
895 void SetVideoFrameDelay(unsigned int frame_delay_value)
897 video.frame_delay_value = frame_delay_value;
900 unsigned int GetVideoFrameDelay()
902 return video.frame_delay_value;
905 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
907 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
908 (!fullscreen && video.fullscreen_enabled))
909 fullscreen = SetVideoMode(fullscreen);
914 Bitmap *LoadImage(char *filename)
918 new_bitmap = SDLLoadImage(filename);
921 new_bitmap->source_filename = getStringCopy(filename);
926 Bitmap *LoadCustomImage(char *basename)
928 char *filename = getCustomImageFilename(basename);
931 if (filename == NULL)
932 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
934 if ((new_bitmap = LoadImage(filename)) == NULL)
935 Error(ERR_EXIT, "LoadImage('%s') failed: %s", basename, GetError());
940 void ReloadCustomImage(Bitmap *bitmap, char *basename)
942 char *filename = getCustomImageFilename(basename);
945 if (filename == NULL) /* (should never happen) */
947 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
951 if (strEqual(filename, bitmap->source_filename))
953 /* The old and new image are the same (have the same filename and path).
954 This usually means that this image does not exist in this graphic set
955 and a fallback to the existing image is done. */
960 if ((new_bitmap = LoadImage(filename)) == NULL)
962 Error(ERR_WARN, "LoadImage('%s') failed: %s", basename, GetError());
966 if (bitmap->width != new_bitmap->width ||
967 bitmap->height != new_bitmap->height)
969 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
971 FreeBitmap(new_bitmap);
975 TransferBitmapPointers(new_bitmap, bitmap);
979 Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
981 Bitmap *dst_bitmap = SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
986 static void SetMaskedBitmapSurface(Bitmap *bitmap)
991 SDL_Surface *surface = bitmap->surface;
993 if (bitmap->surface_masked)
994 SDL_FreeSurface(bitmap->surface_masked);
996 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
997 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
999 if ((bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
1000 Error(ERR_EXIT, "SDL_DisplayFormat() failed");
1002 SDL_SetColorKey(surface, UNSET_TRANSPARENT_PIXEL, 0);
1005 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
1007 if (bitmaps[IMG_BITMAP_CUSTOM])
1009 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
1011 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
1014 if (gfx.game_tile_size == gfx.standard_tile_size)
1016 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1021 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
1022 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
1023 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
1025 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
1027 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
1028 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
1030 SetMaskedBitmapSurface(bitmap_new);
1033 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1034 int tile_size, boolean create_small_bitmaps)
1036 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1037 Bitmap *tmp_bitmap_final = NULL;
1038 Bitmap *tmp_bitmap_0 = NULL;
1039 Bitmap *tmp_bitmap_1 = NULL;
1040 Bitmap *tmp_bitmap_2 = NULL;
1041 Bitmap *tmp_bitmap_4 = NULL;
1042 Bitmap *tmp_bitmap_8 = NULL;
1043 Bitmap *tmp_bitmap_16 = NULL;
1044 Bitmap *tmp_bitmap_32 = NULL;
1045 int width_final, height_final;
1046 int width_0, height_0;
1047 int width_1, height_1;
1048 int width_2, height_2;
1049 int width_4, height_4;
1050 int width_8, height_8;
1051 int width_16, height_16;
1052 int width_32, height_32;
1053 int old_width, old_height;
1056 print_timestamp_init("CreateScaledBitmaps");
1058 old_width = old_bitmap->width;
1059 old_height = old_bitmap->height;
1061 /* calculate new image dimensions for final image size */
1062 width_final = old_width * zoom_factor;
1063 height_final = old_height * zoom_factor;
1065 /* get image with final size (this might require scaling up) */
1066 /* ("final" size may result in non-standard tile size image) */
1067 if (zoom_factor != 1)
1068 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1070 tmp_bitmap_final = old_bitmap;
1072 UPDATE_BUSY_STATE();
1074 width_0 = width_1 = width_final;
1075 height_0 = height_1 = height_final;
1077 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1079 if (create_small_bitmaps)
1081 /* check if we have a non-gameplay tile size image */
1082 if (tile_size != gfx.game_tile_size)
1084 /* get image with gameplay tile size */
1085 width_0 = width_final * gfx.game_tile_size / tile_size;
1086 height_0 = height_final * gfx.game_tile_size / tile_size;
1088 if (width_0 == old_width)
1089 tmp_bitmap_0 = old_bitmap;
1090 else if (width_0 == width_final)
1091 tmp_bitmap_0 = tmp_bitmap_final;
1093 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1095 UPDATE_BUSY_STATE();
1098 /* check if we have a non-standard tile size image */
1099 if (tile_size != gfx.standard_tile_size)
1101 /* get image with standard tile size */
1102 width_1 = width_final * gfx.standard_tile_size / tile_size;
1103 height_1 = height_final * gfx.standard_tile_size / tile_size;
1105 if (width_1 == old_width)
1106 tmp_bitmap_1 = old_bitmap;
1107 else if (width_1 == width_final)
1108 tmp_bitmap_1 = tmp_bitmap_final;
1109 else if (width_1 == width_0)
1110 tmp_bitmap_1 = tmp_bitmap_0;
1112 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1114 UPDATE_BUSY_STATE();
1117 /* calculate new image dimensions for small images */
1118 width_2 = width_1 / 2;
1119 height_2 = height_1 / 2;
1120 width_4 = width_1 / 4;
1121 height_4 = height_1 / 4;
1122 width_8 = width_1 / 8;
1123 height_8 = height_1 / 8;
1124 width_16 = width_1 / 16;
1125 height_16 = height_1 / 16;
1126 width_32 = width_1 / 32;
1127 height_32 = height_1 / 32;
1129 /* get image with 1/2 of normal size (for use in the level editor) */
1130 if (width_2 == old_width)
1131 tmp_bitmap_2 = old_bitmap;
1133 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1135 UPDATE_BUSY_STATE();
1137 /* get image with 1/4 of normal size (for use in the level editor) */
1138 if (width_4 == old_width)
1139 tmp_bitmap_4 = old_bitmap;
1141 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1143 UPDATE_BUSY_STATE();
1145 /* get image with 1/8 of normal size (for use on the preview screen) */
1146 if (width_8 == old_width)
1147 tmp_bitmap_8 = old_bitmap;
1149 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1151 UPDATE_BUSY_STATE();
1153 /* get image with 1/16 of normal size (for use on the preview screen) */
1154 if (width_16 == old_width)
1155 tmp_bitmap_16 = old_bitmap;
1157 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1159 UPDATE_BUSY_STATE();
1161 /* get image with 1/32 of normal size (for use on the preview screen) */
1162 if (width_32 == old_width)
1163 tmp_bitmap_32 = old_bitmap;
1165 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1167 UPDATE_BUSY_STATE();
1169 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1170 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1171 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1172 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1173 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1174 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1176 if (width_0 != width_1)
1177 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1179 if (bitmaps[IMG_BITMAP_CUSTOM])
1180 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1182 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1184 boolean free_old_bitmap = TRUE;
1186 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1187 if (bitmaps[i] == old_bitmap)
1188 free_old_bitmap = FALSE;
1190 if (free_old_bitmap)
1191 FreeBitmap(old_bitmap);
1195 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1198 // create corresponding bitmaps for masked blitting
1199 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1200 if (bitmaps[i] != NULL &&
1201 bitmaps[i] != old_bitmap)
1202 SetMaskedBitmapSurface(bitmaps[i]);
1204 UPDATE_BUSY_STATE();
1206 print_timestamp_done("CreateScaledBitmaps");
1209 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1212 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1215 void CreateBitmapTextures(Bitmap **bitmaps)
1217 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1220 void FreeBitmapTextures(Bitmap **bitmaps)
1222 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1225 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1227 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1231 /* ------------------------------------------------------------------------- */
1232 /* mouse pointer functions */
1233 /* ------------------------------------------------------------------------- */
1235 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1237 /* XPM image definitions */
1238 static const char *cursor_image_none[] =
1240 /* width height num_colors chars_per_pixel */
1270 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1271 static const char *cursor_image_dot[] =
1273 /* width height num_colors chars_per_pixel */
1302 static const char **cursor_image_playfield = cursor_image_dot;
1304 /* some people complained about a "white dot" on the screen and thought it
1305 was a graphical error... OK, let's just remove the whole pointer :-) */
1306 static const char **cursor_image_playfield = cursor_image_none;
1309 static const int cursor_bit_order = BIT_ORDER_MSB;
1311 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1313 struct MouseCursorInfo *cursor;
1314 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1315 int header_lines = 4;
1318 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1320 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1323 for (y = 0; y < cursor->width; y++)
1325 for (x = 0; x < cursor->height; x++)
1328 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1333 cursor->data[i] = cursor->mask[i] = 0;
1336 switch (image[header_lines + y][x])
1339 cursor->data[i] |= bit_mask;
1340 cursor->mask[i] |= bit_mask;
1344 cursor->mask[i] |= bit_mask;
1353 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1358 void SetMouseCursor(int mode)
1360 static struct MouseCursorInfo *cursor_none = NULL;
1361 static struct MouseCursorInfo *cursor_playfield = NULL;
1362 struct MouseCursorInfo *cursor_new;
1364 if (cursor_none == NULL)
1365 cursor_none = get_cursor_from_image(cursor_image_none);
1367 if (cursor_playfield == NULL)
1368 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1370 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1371 mode == CURSOR_NONE ? cursor_none :
1372 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1374 SDLSetMouseCursor(cursor_new);
1376 gfx.cursor_mode = mode;
1380 /* ========================================================================= */
1381 /* audio functions */
1382 /* ========================================================================= */
1384 void OpenAudio(void)
1386 /* always start with reliable default values */
1387 audio.sound_available = FALSE;
1388 audio.music_available = FALSE;
1389 audio.loops_available = FALSE;
1391 audio.sound_enabled = FALSE;
1392 audio.sound_deactivated = FALSE;
1394 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1395 audio.mixer_pid = 0;
1396 audio.device_name = NULL;
1397 audio.device_fd = -1;
1399 audio.num_channels = 0;
1400 audio.music_channel = 0;
1401 audio.first_sound_channel = 0;
1406 void CloseAudio(void)
1410 audio.sound_enabled = FALSE;
1413 void SetAudioMode(boolean enabled)
1415 if (!audio.sound_available)
1418 audio.sound_enabled = enabled;
1422 /* ========================================================================= */
1423 /* event functions */
1424 /* ========================================================================= */
1426 void InitEventFilter(EventFilter filter_function)
1428 /* set event filter to filter out certain events */
1429 #if defined(TARGET_SDL2)
1430 SDL_SetEventFilter(filter_function, NULL);
1432 SDL_SetEventFilter(filter_function);
1436 boolean PendingEvent(void)
1438 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1441 void NextEvent(Event *event)
1443 SDLNextEvent(event);
1446 void PeekEvent(Event *event)
1448 #if defined(TARGET_SDL2)
1449 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1451 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1455 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1457 #if defined(TARGET_SDL2)
1458 /* key up/down events in SDL2 do not return text characters anymore */
1459 return event->keysym.sym;
1462 #if ENABLE_UNUSED_CODE
1463 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1464 (int)event->keysym.unicode,
1465 (int)event->keysym.sym,
1466 (int)SDL_GetModState());
1469 if (with_modifiers &&
1470 event->keysym.unicode > 0x0000 &&
1471 event->keysym.unicode < 0x2000)
1472 return event->keysym.unicode;
1474 return event->keysym.sym;
1479 KeyMod HandleKeyModState(Key key, int key_status)
1481 static KeyMod current_modifiers = KMOD_None;
1483 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1485 KeyMod new_modifier = KMOD_None;
1490 new_modifier = KMOD_Shift_L;
1493 new_modifier = KMOD_Shift_R;
1495 case KSYM_Control_L:
1496 new_modifier = KMOD_Control_L;
1498 case KSYM_Control_R:
1499 new_modifier = KMOD_Control_R;
1502 new_modifier = KMOD_Meta_L;
1505 new_modifier = KMOD_Meta_R;
1508 new_modifier = KMOD_Alt_L;
1511 new_modifier = KMOD_Alt_R;
1517 if (key_status == KEY_PRESSED)
1518 current_modifiers |= new_modifier;
1520 current_modifiers &= ~new_modifier;
1523 return current_modifiers;
1526 KeyMod GetKeyModState()
1528 return (KeyMod)SDL_GetModState();
1531 KeyMod GetKeyModStateFromEvents()
1533 /* always use key modifier state as tracked from key events (this is needed
1534 if the modifier key event was injected into the event queue, but the key
1535 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1536 query the keys as held pressed on the keyboard) -- this case is currently
1537 only used to filter out clipboard insert events from "True X-Mouse" tool */
1539 return HandleKeyModState(KSYM_UNDEFINED, 0);
1542 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1544 if (event->type != EVENT_CLIENTMESSAGE)
1547 return TRUE; /* the only possible message here is SDL_QUIT */
1551 /* ========================================================================= */
1552 /* joystick functions */
1553 /* ========================================================================= */
1555 void InitJoysticks()
1559 #if defined(NO_JOYSTICK)
1560 return; /* joysticks generally deactivated by compile-time directive */
1563 /* always start with reliable default values */
1564 joystick.status = JOYSTICK_NOT_AVAILABLE;
1565 for (i = 0; i < MAX_PLAYERS; i++)
1566 joystick.fd[i] = -1; /* joystick device closed */
1571 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1573 return SDLReadJoystick(nr, x, y, b1, b2);