1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
24 #define ENABLE_UNUSED_CODE 0 /* currently unused functions */
27 /* ========================================================================= */
28 /* exported variables */
29 /* ========================================================================= */
31 struct ProgramInfo program;
32 struct OptionInfo options;
33 struct VideoSystemInfo video;
34 struct AudioSystemInfo audio;
36 struct ArtworkInfo artwork;
37 struct JoystickInfo joystick;
38 struct SetupInfo setup;
40 LevelDirTree *leveldir_first_all = NULL;
41 LevelDirTree *leveldir_first = NULL;
42 LevelDirTree *leveldir_current = NULL;
45 struct LevelStats level_stats[MAX_LEVELS];
47 DrawWindow *window = NULL;
48 DrawBuffer *backbuffer = NULL;
49 DrawBuffer *drawto = NULL;
51 int button_status = MB_NOT_PRESSED;
52 boolean motion_status = FALSE;
53 int wheel_steps = DEFAULT_WHEEL_STEPS;
54 #if defined(TARGET_SDL2)
55 boolean keyrepeat_status = TRUE;
58 int redraw_mask = REDRAW_NONE;
63 /* ========================================================================= */
64 /* init/close functions */
65 /* ========================================================================= */
67 void InitProgramInfo(char *argv0, char *config_filename, char *userdata_subdir,
68 char *program_title, char *icon_title,
69 char *icon_filename, char *cookie_prefix,
72 program.command_basepath = getBasePath(argv0);
73 program.command_basename = getBaseName(argv0);
75 program.config_filename = config_filename;
77 program.userdata_subdir = userdata_subdir;
78 program.userdata_path = getUserGameDataDir();
80 program.program_title = program_title;
81 program.window_title = "(undefined)";
82 program.icon_title = icon_title;
84 program.icon_filename = icon_filename;
86 program.cookie_prefix = cookie_prefix;
88 program.version_major = VERSION_MAJOR(program_version);
89 program.version_minor = VERSION_MINOR(program_version);
90 program.version_patch = VERSION_PATCH(program_version);
91 program.version_build = VERSION_BUILD(program_version);
92 program.version_ident = program_version;
94 program.log_filename[LOG_OUT_ID] = getLogFilename(LOG_OUT_BASENAME);
95 program.log_filename[LOG_ERR_ID] = getLogFilename(LOG_ERR_BASENAME);
96 program.log_file[LOG_OUT_ID] = program.log_file_default[LOG_OUT_ID] = stdout;
97 program.log_file[LOG_ERR_ID] = program.log_file_default[LOG_ERR_ID] = stderr;
100 void SetWindowTitle()
102 program.window_title = program.window_title_function();
107 void InitWindowTitleFunction(char *(*window_title_function)(void))
109 program.window_title_function = window_title_function;
112 void InitExitMessageFunction(void (*exit_message_function)(char *, va_list))
114 program.exit_message_function = exit_message_function;
117 void InitExitFunction(void (*exit_function)(int))
119 program.exit_function = exit_function;
121 /* set signal handlers to custom exit function */
122 // signal(SIGINT, exit_function);
123 signal(SIGTERM, exit_function);
125 /* set exit function to automatically cleanup SDL stuff after exit() */
129 void InitPlatformDependentStuff(void)
131 // this is initialized in GetOptions(), but may already be used before
132 options.verbose = TRUE;
136 #if defined(TARGET_SDL2)
137 int sdl_init_flags = SDL_INIT_EVENTS | SDL_INIT_NOPARACHUTE;
139 int sdl_init_flags = SDL_INIT_EVENTTHREAD | SDL_INIT_NOPARACHUTE;
142 if (SDL_Init(sdl_init_flags) < 0)
143 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
148 void ClosePlatformDependentStuff(void)
153 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
154 int real_sx, int real_sy,
155 int full_sxsize, int full_sysize,
156 Bitmap *field_save_buffer)
162 gfx.real_sx = real_sx;
163 gfx.real_sy = real_sy;
164 gfx.full_sxsize = full_sxsize;
165 gfx.full_sysize = full_sysize;
167 gfx.field_save_buffer = field_save_buffer;
169 SetDrawDeactivationMask(REDRAW_NONE); /* do not deactivate drawing */
170 SetDrawBackgroundMask(REDRAW_NONE); /* deactivate masked drawing */
173 void InitGfxTileSizeInfo(int game_tile_size, int standard_tile_size)
175 gfx.game_tile_size = game_tile_size;
176 gfx.standard_tile_size = standard_tile_size;
179 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
187 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
195 void InitGfxDoor3Info(int ex, int ey, int exsize, int eysize)
203 void InitGfxWindowInfo(int win_xsize, int win_ysize)
205 if (win_xsize != gfx.win_xsize || win_ysize != gfx.win_ysize)
207 ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize);
209 #if defined(TARGET_SDL2)
210 ReCreateBitmap(&gfx.final_screen_bitmap, win_xsize, win_ysize);
213 ReCreateBitmap(&gfx.fade_bitmap_backup, win_xsize, win_ysize);
214 ReCreateBitmap(&gfx.fade_bitmap_source, win_xsize, win_ysize);
215 ReCreateBitmap(&gfx.fade_bitmap_target, win_xsize, win_ysize);
216 ReCreateBitmap(&gfx.fade_bitmap_black, win_xsize, win_ysize);
218 ClearRectangle(gfx.fade_bitmap_black, 0, 0, win_xsize, win_ysize);
221 gfx.win_xsize = win_xsize;
222 gfx.win_ysize = win_ysize;
224 gfx.background_bitmap_mask = REDRAW_NONE;
227 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
229 /* currently only used by MSDOS code to alloc VRAM buffer, if available */
230 /* 2009-03-24: also (temporarily?) used for overlapping blit workaround */
231 gfx.scrollbuffer_width = scrollbuffer_width;
232 gfx.scrollbuffer_height = scrollbuffer_height;
235 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
237 gfx.clipping_enabled = enabled;
240 gfx.clip_width = width;
241 gfx.clip_height = height;
244 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
246 gfx.draw_busy_anim_function = draw_busy_anim_function;
249 void InitGfxDrawGlobalAnimFunction(void (*draw_global_anim_function)(int, int))
251 gfx.draw_global_anim_function = draw_global_anim_function;
254 void InitGfxDrawGlobalBorderFunction(void (*draw_global_border_function)(int))
256 gfx.draw_global_border_function = draw_global_border_function;
259 void InitGfxCustomArtworkInfo()
261 gfx.override_level_graphics = FALSE;
262 gfx.override_level_sounds = FALSE;
263 gfx.override_level_music = FALSE;
265 gfx.draw_init_text = TRUE;
268 void InitGfxOtherSettings()
270 gfx.cursor_mode = CURSOR_DEFAULT;
273 void SetDrawDeactivationMask(int draw_deactivation_mask)
275 gfx.draw_deactivation_mask = draw_deactivation_mask;
278 void SetDrawBackgroundMask(int draw_background_mask)
280 gfx.draw_background_mask = draw_background_mask;
283 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
285 if (background_bitmap_tile != NULL)
286 gfx.background_bitmap_mask |= mask;
288 gfx.background_bitmap_mask &= ~mask;
290 if (background_bitmap_tile == NULL) /* empty background requested */
293 if (mask == REDRAW_ALL)
294 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
295 0, 0, video.width, video.height);
296 else if (mask == REDRAW_FIELD)
297 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
298 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
299 else if (mask == REDRAW_DOOR_1)
300 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
301 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
304 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
306 /* remove every mask before setting mask for window */
307 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
308 SetBackgroundBitmap(NULL, 0xffff); /* !!! FIX THIS !!! */
309 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
312 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
314 /* remove window area mask before setting mask for main area */
315 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
316 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
317 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
320 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
322 /* remove window area mask before setting mask for door area */
323 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
324 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
325 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
329 /* ========================================================================= */
330 /* video functions */
331 /* ========================================================================= */
333 inline static int GetRealDepth(int depth)
335 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
338 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
339 int width, int height, Pixel color)
341 SDLFillRectangle(bitmap, x, y, width, height, color);
343 if (bitmap == backbuffer)
344 SetRedrawMaskFromArea(x, y, width, height);
347 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
348 int src_x, int src_y, int width, int height,
349 int dst_x, int dst_y, int mask_mode)
351 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
352 dst_x, dst_y, mask_mode);
354 if (dst_bitmap == backbuffer)
355 SetRedrawMaskFromArea(dst_x, dst_y, width, height);
358 void LimitScreenUpdates(boolean enable)
360 SDLLimitScreenUpdates(enable);
363 void InitVideoDisplay(void)
365 SDLInitVideoDisplay();
368 void CloseVideoDisplay(void)
370 KeyboardAutoRepeatOn();
372 SDL_QuitSubSystem(SDL_INIT_VIDEO);
375 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
378 video.height = height;
379 video.depth = GetRealDepth(depth);
381 video.fullscreen_available = FULLSCREEN_STATUS;
382 video.fullscreen_enabled = FALSE;
384 video.window_scaling_available = WINDOW_SCALING_STATUS;
386 video.frame_delay = 0;
387 video.frame_delay_value = GAME_FRAME_DELAY;
389 video.shifted_up = FALSE;
390 video.shifted_up_pos = 0;
391 video.shifted_up_pos_last = 0;
392 video.shifted_up_delay = 0;
393 video.shifted_up_delay_value = ONE_SECOND_DELAY / 4;
395 SDLInitVideoBuffer(fullscreen);
397 video.initialized = TRUE;
402 inline static void FreeBitmapPointers(Bitmap *bitmap)
407 SDLFreeBitmapPointers(bitmap);
409 checked_free(bitmap->source_filename);
410 bitmap->source_filename = NULL;
413 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
416 if (src_bitmap == NULL || dst_bitmap == NULL)
419 FreeBitmapPointers(dst_bitmap);
421 *dst_bitmap = *src_bitmap;
424 void FreeBitmap(Bitmap *bitmap)
429 FreeBitmapPointers(bitmap);
434 Bitmap *CreateBitmapStruct(void)
436 return checked_calloc(sizeof(Bitmap));
439 Bitmap *CreateBitmap(int width, int height, int depth)
441 Bitmap *new_bitmap = CreateBitmapStruct();
442 int real_width = MAX(1, width); /* prevent zero bitmap width */
443 int real_height = MAX(1, height); /* prevent zero bitmap height */
444 int real_depth = GetRealDepth(depth);
446 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
448 new_bitmap->width = real_width;
449 new_bitmap->height = real_height;
454 void ReCreateBitmap(Bitmap **bitmap, int width, int height)
456 Bitmap *new_bitmap = CreateBitmap(width, height, DEFAULT_DEPTH);
460 *bitmap = new_bitmap;
464 TransferBitmapPointers(new_bitmap, *bitmap);
469 void CloseWindow(DrawWindow *window)
473 void SetRedrawMaskFromArea(int x, int y, int width, int height)
477 int x2 = x + width - 1;
478 int y2 = y + height - 1;
480 if (width == 0 || height == 0)
483 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
484 redraw_mask |= REDRAW_FIELD;
485 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
486 redraw_mask |= REDRAW_DOOR_1;
487 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
488 redraw_mask |= REDRAW_DOOR_2;
489 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
490 redraw_mask |= REDRAW_DOOR_3;
492 redraw_mask = REDRAW_ALL;
495 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
498 if (draw_mask == REDRAW_NONE)
501 if (draw_mask & REDRAW_ALL)
504 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
507 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
510 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
513 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
519 boolean DrawingDeactivated(int x, int y, int width, int height)
521 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
524 boolean DrawingOnBackground(int x, int y)
526 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
527 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
530 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
531 int *width, int *height, boolean is_dest)
533 int clip_x, clip_y, clip_width, clip_height;
535 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
537 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
538 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
539 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
540 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
546 clip_width = bitmap->width;
547 clip_height = bitmap->height;
550 /* skip if rectangle completely outside bitmap */
552 if (*x + *width <= clip_x ||
553 *y + *height <= clip_y ||
554 *x >= clip_x + clip_width ||
555 *y >= clip_y + clip_height)
558 /* clip if rectangle overlaps bitmap */
562 *width -= clip_x - *x;
565 else if (*x + *width > clip_x + clip_width)
567 *width = clip_x + clip_width - *x;
572 *height -= clip_y - *y;
575 else if (*y + *height > clip_y + clip_height)
577 *height = clip_y + clip_height - *y;
583 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
584 int src_x, int src_y, int width, int height,
585 int dst_x, int dst_y)
587 int dst_x_unclipped = dst_x;
588 int dst_y_unclipped = dst_y;
590 if (src_bitmap == NULL || dst_bitmap == NULL)
593 if (DrawingDeactivated(dst_x, dst_y, width, height))
596 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
597 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
600 /* source x/y might need adjustment if destination x/y was clipped top/left */
601 src_x += dst_x - dst_x_unclipped;
602 src_y += dst_y - dst_y_unclipped;
604 #if defined(TARGET_SDL2)
605 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
606 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
607 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
608 but is already fixed in SVN and should therefore finally be fixed with
609 the next official SDL release, which is probably version 1.2.14.) */
610 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
612 if (src_bitmap == dst_bitmap)
614 /* needed when blitting directly to same bitmap -- should not be needed with
615 recent SDL libraries, but apparently does not work in 1.2.11 directly */
617 static Bitmap *tmp_bitmap = NULL;
618 static int tmp_bitmap_xsize = 0;
619 static int tmp_bitmap_ysize = 0;
621 /* start with largest static bitmaps for initial bitmap size ... */
622 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
624 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
625 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
628 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
629 if (src_bitmap->width > tmp_bitmap_xsize ||
630 src_bitmap->height > tmp_bitmap_ysize)
632 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
633 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
635 FreeBitmap(tmp_bitmap);
640 if (tmp_bitmap == NULL)
641 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
644 sysCopyArea(src_bitmap, tmp_bitmap,
645 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
646 sysCopyArea(tmp_bitmap, dst_bitmap,
647 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
653 sysCopyArea(src_bitmap, dst_bitmap,
654 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
657 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
658 int src_x, int src_y, int src_width, int src_height,
659 int dst_x, int dst_y, int dst_width, int dst_height)
661 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
662 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
663 int dst_xsize = dst_width;
664 int dst_ysize = dst_height;
665 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
666 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
669 for (y = 0; y < src_ysteps; y++)
671 for (x = 0; x < src_xsteps; x++)
673 int draw_x = dst_x + x * src_xsize;
674 int draw_y = dst_y + y * src_ysize;
675 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
676 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
678 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
684 void FadeRectangle(int x, int y, int width, int height,
685 int fade_mode, int fade_delay, int post_delay,
686 void (*draw_border_function)(void))
688 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
689 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
692 SDLFadeRectangle(x, y, width, height,
693 fade_mode, fade_delay, post_delay, draw_border_function);
696 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
699 if (DrawingDeactivated(x, y, width, height))
702 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
705 sysFillRectangle(bitmap, x, y, width, height, color);
708 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
710 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
713 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
714 int width, int height)
716 if (DrawingOnBackground(x, y))
717 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
719 ClearRectangle(bitmap, x, y, width, height);
722 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
723 int src_x, int src_y, int width, int height,
724 int dst_x, int dst_y)
726 if (DrawingDeactivated(dst_x, dst_y, width, height))
729 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
730 dst_x, dst_y, BLIT_MASKED);
733 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
734 int src_x, int src_y, int width, int height,
735 int dst_x, int dst_y)
737 if (DrawingOnBackground(dst_x, dst_y))
739 /* draw background */
740 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
743 /* draw foreground */
744 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
748 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
752 void BlitTexture(Bitmap *bitmap,
753 int src_x, int src_y, int width, int height,
754 int dst_x, int dst_y)
759 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
763 void BlitTextureMasked(Bitmap *bitmap,
764 int src_x, int src_y, int width, int height,
765 int dst_x, int dst_y)
770 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
774 void BlitToScreen(Bitmap *bitmap,
775 int src_x, int src_y, int width, int height,
776 int dst_x, int dst_y)
781 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
782 BlitBitmap(bitmap, gfx.final_screen_bitmap, src_x, src_y,
783 width, height, dst_x, dst_y);
785 BlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y);
788 void BlitToScreenMasked(Bitmap *bitmap,
789 int src_x, int src_y, int width, int height,
790 int dst_x, int dst_y)
795 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
796 BlitBitmapMasked(bitmap, gfx.final_screen_bitmap, src_x, src_y,
797 width, height, dst_x, dst_y);
799 BlitTextureMasked(bitmap, src_x, src_y, width, height, dst_x, dst_y);
802 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
805 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
808 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
811 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
814 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
815 int to_x, int to_y, Pixel pixel, int line_width)
819 for (x = 0; x < line_width; x++)
821 for (y = 0; y < line_width; y++)
823 int dx = x - line_width / 2;
824 int dy = y - line_width / 2;
826 if ((x == 0 && y == 0) ||
827 (x == 0 && y == line_width - 1) ||
828 (x == line_width - 1 && y == 0) ||
829 (x == line_width - 1 && y == line_width - 1))
833 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
838 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
843 for (i = 0; i < num_points - 1; i++)
844 DrawLine(bitmap, points[i].x, points[i].y,
845 points[i + 1].x, points[i + 1].y, pixel, line_width);
848 SDLDrawLines(bitmap->surface, points, num_points, pixel);
852 Pixel GetPixel(Bitmap *bitmap, int x, int y)
854 if (x < 0 || x >= bitmap->width ||
855 y < 0 || y >= bitmap->height)
858 return SDLGetPixel(bitmap, x, y);
861 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
862 unsigned int color_g, unsigned int color_b)
864 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
867 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
869 unsigned int color_r = (color >> 16) & 0xff;
870 unsigned int color_g = (color >> 8) & 0xff;
871 unsigned int color_b = (color >> 0) & 0xff;
873 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
876 void KeyboardAutoRepeatOn(void)
878 #if defined(TARGET_SDL2)
879 keyrepeat_status = TRUE;
881 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
882 SDL_DEFAULT_REPEAT_INTERVAL / 2);
883 SDL_EnableUNICODE(1);
887 void KeyboardAutoRepeatOff(void)
889 #if defined(TARGET_SDL2)
890 keyrepeat_status = FALSE;
892 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
893 SDL_EnableUNICODE(0);
897 boolean SetVideoMode(boolean fullscreen)
899 return SDLSetVideoMode(fullscreen);
902 void SetVideoFrameDelay(unsigned int frame_delay_value)
904 video.frame_delay_value = frame_delay_value;
907 unsigned int GetVideoFrameDelay()
909 return video.frame_delay_value;
912 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
914 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
915 (!fullscreen && video.fullscreen_enabled))
916 fullscreen = SetVideoMode(fullscreen);
921 Bitmap *LoadImage(char *filename)
925 new_bitmap = SDLLoadImage(filename);
928 new_bitmap->source_filename = getStringCopy(filename);
933 Bitmap *LoadCustomImage(char *basename)
935 char *filename = getCustomImageFilename(basename);
938 if (filename == NULL)
939 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
941 if ((new_bitmap = LoadImage(filename)) == NULL)
942 Error(ERR_EXIT, "LoadImage('%s') failed: %s", basename, GetError());
947 void ReloadCustomImage(Bitmap *bitmap, char *basename)
949 char *filename = getCustomImageFilename(basename);
952 if (filename == NULL) /* (should never happen) */
954 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
958 if (strEqual(filename, bitmap->source_filename))
960 /* The old and new image are the same (have the same filename and path).
961 This usually means that this image does not exist in this graphic set
962 and a fallback to the existing image is done. */
967 if ((new_bitmap = LoadImage(filename)) == NULL)
969 Error(ERR_WARN, "LoadImage('%s') failed: %s", basename, GetError());
973 if (bitmap->width != new_bitmap->width ||
974 bitmap->height != new_bitmap->height)
976 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
978 FreeBitmap(new_bitmap);
982 TransferBitmapPointers(new_bitmap, bitmap);
986 static Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
988 return SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
991 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
993 if (bitmaps[IMG_BITMAP_CUSTOM])
995 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
997 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
1000 if (gfx.game_tile_size == gfx.standard_tile_size)
1002 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1007 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
1008 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
1009 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
1011 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
1013 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
1014 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
1017 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1018 int tile_size, boolean create_small_bitmaps)
1020 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1021 Bitmap *tmp_bitmap_final = NULL;
1022 Bitmap *tmp_bitmap_0 = NULL;
1023 Bitmap *tmp_bitmap_1 = NULL;
1024 Bitmap *tmp_bitmap_2 = NULL;
1025 Bitmap *tmp_bitmap_4 = NULL;
1026 Bitmap *tmp_bitmap_8 = NULL;
1027 Bitmap *tmp_bitmap_16 = NULL;
1028 Bitmap *tmp_bitmap_32 = NULL;
1029 int width_final, height_final;
1030 int width_0, height_0;
1031 int width_1, height_1;
1032 int width_2, height_2;
1033 int width_4, height_4;
1034 int width_8, height_8;
1035 int width_16, height_16;
1036 int width_32, height_32;
1037 int old_width, old_height;
1040 print_timestamp_init("CreateScaledBitmaps");
1042 old_width = old_bitmap->width;
1043 old_height = old_bitmap->height;
1045 /* calculate new image dimensions for final image size */
1046 width_final = old_width * zoom_factor;
1047 height_final = old_height * zoom_factor;
1049 /* get image with final size (this might require scaling up) */
1050 /* ("final" size may result in non-standard tile size image) */
1051 if (zoom_factor != 1)
1052 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1054 tmp_bitmap_final = old_bitmap;
1056 UPDATE_BUSY_STATE();
1058 width_0 = width_1 = width_final;
1059 height_0 = height_1 = height_final;
1061 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1063 if (create_small_bitmaps)
1065 /* check if we have a non-gameplay tile size image */
1066 if (tile_size != gfx.game_tile_size)
1068 /* get image with gameplay tile size */
1069 width_0 = width_final * gfx.game_tile_size / tile_size;
1070 height_0 = height_final * gfx.game_tile_size / tile_size;
1072 if (width_0 == old_width)
1073 tmp_bitmap_0 = old_bitmap;
1074 else if (width_0 == width_final)
1075 tmp_bitmap_0 = tmp_bitmap_final;
1077 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1079 UPDATE_BUSY_STATE();
1082 /* check if we have a non-standard tile size image */
1083 if (tile_size != gfx.standard_tile_size)
1085 /* get image with standard tile size */
1086 width_1 = width_final * gfx.standard_tile_size / tile_size;
1087 height_1 = height_final * gfx.standard_tile_size / tile_size;
1089 if (width_1 == old_width)
1090 tmp_bitmap_1 = old_bitmap;
1091 else if (width_1 == width_final)
1092 tmp_bitmap_1 = tmp_bitmap_final;
1093 else if (width_1 == width_0)
1094 tmp_bitmap_1 = tmp_bitmap_0;
1096 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1098 UPDATE_BUSY_STATE();
1101 /* calculate new image dimensions for small images */
1102 width_2 = width_1 / 2;
1103 height_2 = height_1 / 2;
1104 width_4 = width_1 / 4;
1105 height_4 = height_1 / 4;
1106 width_8 = width_1 / 8;
1107 height_8 = height_1 / 8;
1108 width_16 = width_1 / 16;
1109 height_16 = height_1 / 16;
1110 width_32 = width_1 / 32;
1111 height_32 = height_1 / 32;
1113 /* get image with 1/2 of normal size (for use in the level editor) */
1114 if (width_2 == old_width)
1115 tmp_bitmap_2 = old_bitmap;
1117 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1119 UPDATE_BUSY_STATE();
1121 /* get image with 1/4 of normal size (for use in the level editor) */
1122 if (width_4 == old_width)
1123 tmp_bitmap_4 = old_bitmap;
1125 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1127 UPDATE_BUSY_STATE();
1129 /* get image with 1/8 of normal size (for use on the preview screen) */
1130 if (width_8 == old_width)
1131 tmp_bitmap_8 = old_bitmap;
1133 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1135 UPDATE_BUSY_STATE();
1137 /* get image with 1/16 of normal size (for use on the preview screen) */
1138 if (width_16 == old_width)
1139 tmp_bitmap_16 = old_bitmap;
1141 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1143 UPDATE_BUSY_STATE();
1145 /* get image with 1/32 of normal size (for use on the preview screen) */
1146 if (width_32 == old_width)
1147 tmp_bitmap_32 = old_bitmap;
1149 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1151 UPDATE_BUSY_STATE();
1153 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1154 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1155 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1156 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1157 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1158 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1160 if (width_0 != width_1)
1161 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1163 if (bitmaps[IMG_BITMAP_CUSTOM])
1164 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1166 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1168 boolean free_old_bitmap = TRUE;
1170 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1171 if (bitmaps[i] == old_bitmap)
1172 free_old_bitmap = FALSE;
1174 if (free_old_bitmap)
1175 FreeBitmap(old_bitmap);
1179 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1182 UPDATE_BUSY_STATE();
1184 print_timestamp_done("CreateScaledBitmaps");
1187 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1190 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1193 void CreateBitmapTextures(Bitmap **bitmaps)
1195 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1198 void FreeBitmapTextures(Bitmap **bitmaps)
1200 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1203 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1205 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1209 /* ------------------------------------------------------------------------- */
1210 /* mouse pointer functions */
1211 /* ------------------------------------------------------------------------- */
1213 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1215 /* XPM image definitions */
1216 static const char *cursor_image_none[] =
1218 /* width height num_colors chars_per_pixel */
1248 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1249 static const char *cursor_image_dot[] =
1251 /* width height num_colors chars_per_pixel */
1280 static const char **cursor_image_playfield = cursor_image_dot;
1282 /* some people complained about a "white dot" on the screen and thought it
1283 was a graphical error... OK, let's just remove the whole pointer :-) */
1284 static const char **cursor_image_playfield = cursor_image_none;
1287 static const int cursor_bit_order = BIT_ORDER_MSB;
1289 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1291 struct MouseCursorInfo *cursor;
1292 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1293 int header_lines = 4;
1296 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1298 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1301 for (y = 0; y < cursor->width; y++)
1303 for (x = 0; x < cursor->height; x++)
1306 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1311 cursor->data[i] = cursor->mask[i] = 0;
1314 switch (image[header_lines + y][x])
1317 cursor->data[i] |= bit_mask;
1318 cursor->mask[i] |= bit_mask;
1322 cursor->mask[i] |= bit_mask;
1331 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1336 void SetMouseCursor(int mode)
1338 static struct MouseCursorInfo *cursor_none = NULL;
1339 static struct MouseCursorInfo *cursor_playfield = NULL;
1340 struct MouseCursorInfo *cursor_new;
1342 if (cursor_none == NULL)
1343 cursor_none = get_cursor_from_image(cursor_image_none);
1345 if (cursor_playfield == NULL)
1346 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1348 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1349 mode == CURSOR_NONE ? cursor_none :
1350 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1352 SDLSetMouseCursor(cursor_new);
1354 gfx.cursor_mode = mode;
1358 /* ========================================================================= */
1359 /* audio functions */
1360 /* ========================================================================= */
1362 void OpenAudio(void)
1364 /* always start with reliable default values */
1365 audio.sound_available = FALSE;
1366 audio.music_available = FALSE;
1367 audio.loops_available = FALSE;
1369 audio.sound_enabled = FALSE;
1370 audio.sound_deactivated = FALSE;
1372 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1373 audio.mixer_pid = 0;
1374 audio.device_name = NULL;
1375 audio.device_fd = -1;
1377 audio.num_channels = 0;
1378 audio.music_channel = 0;
1379 audio.first_sound_channel = 0;
1384 void CloseAudio(void)
1388 audio.sound_enabled = FALSE;
1391 void SetAudioMode(boolean enabled)
1393 if (!audio.sound_available)
1396 audio.sound_enabled = enabled;
1400 /* ========================================================================= */
1401 /* event functions */
1402 /* ========================================================================= */
1404 void InitEventFilter(EventFilter filter_function)
1406 /* set event filter to filter out certain events */
1407 #if defined(TARGET_SDL2)
1408 SDL_SetEventFilter(filter_function, NULL);
1410 SDL_SetEventFilter(filter_function);
1414 boolean PendingEvent(void)
1416 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1419 void NextEvent(Event *event)
1421 SDLNextEvent(event);
1424 void PeekEvent(Event *event)
1426 #if defined(TARGET_SDL2)
1427 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1429 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1433 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1435 #if defined(TARGET_SDL2)
1436 /* key up/down events in SDL2 do not return text characters anymore */
1437 return event->keysym.sym;
1440 #if ENABLE_UNUSED_CODE
1441 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1442 (int)event->keysym.unicode,
1443 (int)event->keysym.sym,
1444 (int)SDL_GetModState());
1447 if (with_modifiers &&
1448 event->keysym.unicode > 0x0000 &&
1449 event->keysym.unicode < 0x2000)
1450 return event->keysym.unicode;
1452 return event->keysym.sym;
1457 KeyMod HandleKeyModState(Key key, int key_status)
1459 static KeyMod current_modifiers = KMOD_None;
1461 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1463 KeyMod new_modifier = KMOD_None;
1468 new_modifier = KMOD_Shift_L;
1471 new_modifier = KMOD_Shift_R;
1473 case KSYM_Control_L:
1474 new_modifier = KMOD_Control_L;
1476 case KSYM_Control_R:
1477 new_modifier = KMOD_Control_R;
1480 new_modifier = KMOD_Meta_L;
1483 new_modifier = KMOD_Meta_R;
1486 new_modifier = KMOD_Alt_L;
1489 new_modifier = KMOD_Alt_R;
1495 if (key_status == KEY_PRESSED)
1496 current_modifiers |= new_modifier;
1498 current_modifiers &= ~new_modifier;
1501 return current_modifiers;
1504 KeyMod GetKeyModState()
1506 return (KeyMod)SDL_GetModState();
1509 KeyMod GetKeyModStateFromEvents()
1511 /* always use key modifier state as tracked from key events (this is needed
1512 if the modifier key event was injected into the event queue, but the key
1513 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1514 query the keys as held pressed on the keyboard) -- this case is currently
1515 only used to filter out clipboard insert events from "True X-Mouse" tool */
1517 return HandleKeyModState(KSYM_UNDEFINED, 0);
1520 void StartTextInput(int x, int y, int width, int height)
1522 #if defined(TARGET_SDL2)
1523 SDL_StartTextInput();
1525 #if defined(HAS_SCREEN_KEYBOARD)
1526 if (y + height > SCREEN_KEYBOARD_POS(video.height))
1528 video.shifted_up_pos = y + height - SCREEN_KEYBOARD_POS(video.height);
1529 video.shifted_up_delay = SDL_GetTicks();
1530 video.shifted_up = TRUE;
1536 void StopTextInput()
1538 #if defined(TARGET_SDL2)
1539 SDL_StopTextInput();
1541 #if defined(HAS_SCREEN_KEYBOARD)
1542 if (video.shifted_up)
1544 video.shifted_up_pos = 0;
1545 video.shifted_up_delay = SDL_GetTicks();
1546 video.shifted_up = FALSE;
1552 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1554 if (event->type != EVENT_CLIENTMESSAGE)
1557 return TRUE; /* the only possible message here is SDL_QUIT */
1561 /* ========================================================================= */
1562 /* joystick functions */
1563 /* ========================================================================= */
1565 void InitJoysticks()
1569 #if defined(NO_JOYSTICK)
1570 return; /* joysticks generally deactivated by compile-time directive */
1573 /* always start with reliable default values */
1574 joystick.status = JOYSTICK_NOT_AVAILABLE;
1575 for (i = 0; i < MAX_PLAYERS; i++)
1576 joystick.fd[i] = -1; /* joystick device closed */
1581 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1583 return SDLReadJoystick(nr, x, y, b1, b2);