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 static Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
982 return SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
985 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
987 if (bitmaps[IMG_BITMAP_CUSTOM])
989 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
991 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
994 if (gfx.game_tile_size == gfx.standard_tile_size)
996 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1001 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
1002 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
1003 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
1005 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
1007 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
1008 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
1011 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1012 int tile_size, boolean create_small_bitmaps)
1014 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1015 Bitmap *tmp_bitmap_final = NULL;
1016 Bitmap *tmp_bitmap_0 = NULL;
1017 Bitmap *tmp_bitmap_1 = NULL;
1018 Bitmap *tmp_bitmap_2 = NULL;
1019 Bitmap *tmp_bitmap_4 = NULL;
1020 Bitmap *tmp_bitmap_8 = NULL;
1021 Bitmap *tmp_bitmap_16 = NULL;
1022 Bitmap *tmp_bitmap_32 = NULL;
1023 int width_final, height_final;
1024 int width_0, height_0;
1025 int width_1, height_1;
1026 int width_2, height_2;
1027 int width_4, height_4;
1028 int width_8, height_8;
1029 int width_16, height_16;
1030 int width_32, height_32;
1031 int old_width, old_height;
1034 print_timestamp_init("CreateScaledBitmaps");
1036 old_width = old_bitmap->width;
1037 old_height = old_bitmap->height;
1039 /* calculate new image dimensions for final image size */
1040 width_final = old_width * zoom_factor;
1041 height_final = old_height * zoom_factor;
1043 /* get image with final size (this might require scaling up) */
1044 /* ("final" size may result in non-standard tile size image) */
1045 if (zoom_factor != 1)
1046 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1048 tmp_bitmap_final = old_bitmap;
1050 UPDATE_BUSY_STATE();
1052 width_0 = width_1 = width_final;
1053 height_0 = height_1 = height_final;
1055 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1057 if (create_small_bitmaps)
1059 /* check if we have a non-gameplay tile size image */
1060 if (tile_size != gfx.game_tile_size)
1062 /* get image with gameplay tile size */
1063 width_0 = width_final * gfx.game_tile_size / tile_size;
1064 height_0 = height_final * gfx.game_tile_size / tile_size;
1066 if (width_0 == old_width)
1067 tmp_bitmap_0 = old_bitmap;
1068 else if (width_0 == width_final)
1069 tmp_bitmap_0 = tmp_bitmap_final;
1071 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1073 UPDATE_BUSY_STATE();
1076 /* check if we have a non-standard tile size image */
1077 if (tile_size != gfx.standard_tile_size)
1079 /* get image with standard tile size */
1080 width_1 = width_final * gfx.standard_tile_size / tile_size;
1081 height_1 = height_final * gfx.standard_tile_size / tile_size;
1083 if (width_1 == old_width)
1084 tmp_bitmap_1 = old_bitmap;
1085 else if (width_1 == width_final)
1086 tmp_bitmap_1 = tmp_bitmap_final;
1087 else if (width_1 == width_0)
1088 tmp_bitmap_1 = tmp_bitmap_0;
1090 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1092 UPDATE_BUSY_STATE();
1095 /* calculate new image dimensions for small images */
1096 width_2 = width_1 / 2;
1097 height_2 = height_1 / 2;
1098 width_4 = width_1 / 4;
1099 height_4 = height_1 / 4;
1100 width_8 = width_1 / 8;
1101 height_8 = height_1 / 8;
1102 width_16 = width_1 / 16;
1103 height_16 = height_1 / 16;
1104 width_32 = width_1 / 32;
1105 height_32 = height_1 / 32;
1107 /* get image with 1/2 of normal size (for use in the level editor) */
1108 if (width_2 == old_width)
1109 tmp_bitmap_2 = old_bitmap;
1111 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1113 UPDATE_BUSY_STATE();
1115 /* get image with 1/4 of normal size (for use in the level editor) */
1116 if (width_4 == old_width)
1117 tmp_bitmap_4 = old_bitmap;
1119 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1121 UPDATE_BUSY_STATE();
1123 /* get image with 1/8 of normal size (for use on the preview screen) */
1124 if (width_8 == old_width)
1125 tmp_bitmap_8 = old_bitmap;
1127 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1129 UPDATE_BUSY_STATE();
1131 /* get image with 1/16 of normal size (for use on the preview screen) */
1132 if (width_16 == old_width)
1133 tmp_bitmap_16 = old_bitmap;
1135 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1137 UPDATE_BUSY_STATE();
1139 /* get image with 1/32 of normal size (for use on the preview screen) */
1140 if (width_32 == old_width)
1141 tmp_bitmap_32 = old_bitmap;
1143 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1145 UPDATE_BUSY_STATE();
1147 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1148 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1149 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1150 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1151 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1152 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1154 if (width_0 != width_1)
1155 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1157 if (bitmaps[IMG_BITMAP_CUSTOM])
1158 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1160 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1162 boolean free_old_bitmap = TRUE;
1164 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1165 if (bitmaps[i] == old_bitmap)
1166 free_old_bitmap = FALSE;
1168 if (free_old_bitmap)
1169 FreeBitmap(old_bitmap);
1173 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1176 UPDATE_BUSY_STATE();
1178 print_timestamp_done("CreateScaledBitmaps");
1181 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1184 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1187 void CreateBitmapTextures(Bitmap **bitmaps)
1189 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1192 void FreeBitmapTextures(Bitmap **bitmaps)
1194 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1197 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1199 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1203 /* ------------------------------------------------------------------------- */
1204 /* mouse pointer functions */
1205 /* ------------------------------------------------------------------------- */
1207 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1209 /* XPM image definitions */
1210 static const char *cursor_image_none[] =
1212 /* width height num_colors chars_per_pixel */
1242 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1243 static const char *cursor_image_dot[] =
1245 /* width height num_colors chars_per_pixel */
1274 static const char **cursor_image_playfield = cursor_image_dot;
1276 /* some people complained about a "white dot" on the screen and thought it
1277 was a graphical error... OK, let's just remove the whole pointer :-) */
1278 static const char **cursor_image_playfield = cursor_image_none;
1281 static const int cursor_bit_order = BIT_ORDER_MSB;
1283 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1285 struct MouseCursorInfo *cursor;
1286 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1287 int header_lines = 4;
1290 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1292 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1295 for (y = 0; y < cursor->width; y++)
1297 for (x = 0; x < cursor->height; x++)
1300 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1305 cursor->data[i] = cursor->mask[i] = 0;
1308 switch (image[header_lines + y][x])
1311 cursor->data[i] |= bit_mask;
1312 cursor->mask[i] |= bit_mask;
1316 cursor->mask[i] |= bit_mask;
1325 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1330 void SetMouseCursor(int mode)
1332 static struct MouseCursorInfo *cursor_none = NULL;
1333 static struct MouseCursorInfo *cursor_playfield = NULL;
1334 struct MouseCursorInfo *cursor_new;
1336 if (cursor_none == NULL)
1337 cursor_none = get_cursor_from_image(cursor_image_none);
1339 if (cursor_playfield == NULL)
1340 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1342 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1343 mode == CURSOR_NONE ? cursor_none :
1344 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1346 SDLSetMouseCursor(cursor_new);
1348 gfx.cursor_mode = mode;
1352 /* ========================================================================= */
1353 /* audio functions */
1354 /* ========================================================================= */
1356 void OpenAudio(void)
1358 /* always start with reliable default values */
1359 audio.sound_available = FALSE;
1360 audio.music_available = FALSE;
1361 audio.loops_available = FALSE;
1363 audio.sound_enabled = FALSE;
1364 audio.sound_deactivated = FALSE;
1366 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1367 audio.mixer_pid = 0;
1368 audio.device_name = NULL;
1369 audio.device_fd = -1;
1371 audio.num_channels = 0;
1372 audio.music_channel = 0;
1373 audio.first_sound_channel = 0;
1378 void CloseAudio(void)
1382 audio.sound_enabled = FALSE;
1385 void SetAudioMode(boolean enabled)
1387 if (!audio.sound_available)
1390 audio.sound_enabled = enabled;
1394 /* ========================================================================= */
1395 /* event functions */
1396 /* ========================================================================= */
1398 void InitEventFilter(EventFilter filter_function)
1400 /* set event filter to filter out certain events */
1401 #if defined(TARGET_SDL2)
1402 SDL_SetEventFilter(filter_function, NULL);
1404 SDL_SetEventFilter(filter_function);
1408 boolean PendingEvent(void)
1410 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1413 void NextEvent(Event *event)
1415 SDLNextEvent(event);
1418 void PeekEvent(Event *event)
1420 #if defined(TARGET_SDL2)
1421 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1423 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1427 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1429 #if defined(TARGET_SDL2)
1430 /* key up/down events in SDL2 do not return text characters anymore */
1431 return event->keysym.sym;
1434 #if ENABLE_UNUSED_CODE
1435 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1436 (int)event->keysym.unicode,
1437 (int)event->keysym.sym,
1438 (int)SDL_GetModState());
1441 if (with_modifiers &&
1442 event->keysym.unicode > 0x0000 &&
1443 event->keysym.unicode < 0x2000)
1444 return event->keysym.unicode;
1446 return event->keysym.sym;
1451 KeyMod HandleKeyModState(Key key, int key_status)
1453 static KeyMod current_modifiers = KMOD_None;
1455 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1457 KeyMod new_modifier = KMOD_None;
1462 new_modifier = KMOD_Shift_L;
1465 new_modifier = KMOD_Shift_R;
1467 case KSYM_Control_L:
1468 new_modifier = KMOD_Control_L;
1470 case KSYM_Control_R:
1471 new_modifier = KMOD_Control_R;
1474 new_modifier = KMOD_Meta_L;
1477 new_modifier = KMOD_Meta_R;
1480 new_modifier = KMOD_Alt_L;
1483 new_modifier = KMOD_Alt_R;
1489 if (key_status == KEY_PRESSED)
1490 current_modifiers |= new_modifier;
1492 current_modifiers &= ~new_modifier;
1495 return current_modifiers;
1498 KeyMod GetKeyModState()
1500 return (KeyMod)SDL_GetModState();
1503 KeyMod GetKeyModStateFromEvents()
1505 /* always use key modifier state as tracked from key events (this is needed
1506 if the modifier key event was injected into the event queue, but the key
1507 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1508 query the keys as held pressed on the keyboard) -- this case is currently
1509 only used to filter out clipboard insert events from "True X-Mouse" tool */
1511 return HandleKeyModState(KSYM_UNDEFINED, 0);
1514 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1516 if (event->type != EVENT_CLIENTMESSAGE)
1519 return TRUE; /* the only possible message here is SDL_QUIT */
1523 /* ========================================================================= */
1524 /* joystick functions */
1525 /* ========================================================================= */
1527 void InitJoysticks()
1531 #if defined(NO_JOYSTICK)
1532 return; /* joysticks generally deactivated by compile-time directive */
1535 /* always start with reliable default values */
1536 joystick.status = JOYSTICK_NOT_AVAILABLE;
1537 for (i = 0; i < MAX_PLAYERS; i++)
1538 joystick.fd[i] = -1; /* joystick device closed */
1543 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1545 return SDLReadJoystick(nr, x, y, b1, b2);