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 gfx.win_xsize = win_xsize;
205 gfx.win_ysize = win_ysize;
207 gfx.background_bitmap_mask = REDRAW_NONE;
209 ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize, DEFAULT_DEPTH);
211 #if defined(TARGET_SDL2)
212 ReCreateBitmap(&gfx.final_screen_bitmap, win_xsize, win_ysize, DEFAULT_DEPTH);
215 ReCreateBitmap(&gfx.fade_bitmap_source, win_xsize, win_ysize, DEFAULT_DEPTH);
216 ReCreateBitmap(&gfx.fade_bitmap_target, win_xsize, win_ysize, DEFAULT_DEPTH);
217 ReCreateBitmap(&gfx.fade_bitmap_black, win_xsize, win_ysize, DEFAULT_DEPTH);
219 ClearRectangle(gfx.fade_bitmap_black, 0, 0, win_xsize, win_ysize);
222 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
224 /* currently only used by MSDOS code to alloc VRAM buffer, if available */
225 /* 2009-03-24: also (temporarily?) used for overlapping blit workaround */
226 gfx.scrollbuffer_width = scrollbuffer_width;
227 gfx.scrollbuffer_height = scrollbuffer_height;
230 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
232 gfx.clipping_enabled = enabled;
235 gfx.clip_width = width;
236 gfx.clip_height = height;
239 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
241 gfx.draw_busy_anim_function = draw_busy_anim_function;
244 void InitGfxDrawGlobalAnimFunction(void (*draw_global_anim_function)(int))
246 gfx.draw_global_anim_function = draw_global_anim_function;
249 void InitGfxDrawGlobalBorderFunction(void (*draw_global_border_function)(int))
251 gfx.draw_global_border_function = draw_global_border_function;
254 void InitGfxCustomArtworkInfo()
256 gfx.override_level_graphics = FALSE;
257 gfx.override_level_sounds = FALSE;
258 gfx.override_level_music = FALSE;
260 gfx.draw_init_text = TRUE;
263 void InitGfxOtherSettings()
265 gfx.cursor_mode = CURSOR_DEFAULT;
268 void SetDrawDeactivationMask(int draw_deactivation_mask)
270 gfx.draw_deactivation_mask = draw_deactivation_mask;
273 void SetDrawBackgroundMask(int draw_background_mask)
275 gfx.draw_background_mask = draw_background_mask;
278 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
280 if (background_bitmap_tile != NULL)
281 gfx.background_bitmap_mask |= mask;
283 gfx.background_bitmap_mask &= ~mask;
285 if (background_bitmap_tile == NULL) /* empty background requested */
288 if (mask == REDRAW_ALL)
289 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
290 0, 0, video.width, video.height);
291 else if (mask == REDRAW_FIELD)
292 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
293 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
294 else if (mask == REDRAW_DOOR_1)
295 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
296 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
299 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
301 /* remove every mask before setting mask for window */
302 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
303 SetBackgroundBitmap(NULL, 0xffff); /* !!! FIX THIS !!! */
304 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
307 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
309 /* remove window area mask before setting mask for main area */
310 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
311 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
312 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
315 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
317 /* remove window area mask before setting mask for door area */
318 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
319 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
320 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
324 /* ========================================================================= */
325 /* video functions */
326 /* ========================================================================= */
328 inline static int GetRealDepth(int depth)
330 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
333 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
334 int width, int height, Pixel color)
336 SDLFillRectangle(bitmap, x, y, width, height, color);
338 if (bitmap == backbuffer)
339 SetRedrawMaskFromArea(x, y, width, height);
342 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
343 int src_x, int src_y, int width, int height,
344 int dst_x, int dst_y, int mask_mode)
346 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
347 dst_x, dst_y, mask_mode);
349 if (dst_bitmap == backbuffer)
350 SetRedrawMaskFromArea(dst_x, dst_y, width, height);
353 void LimitScreenUpdates(boolean enable)
355 SDLLimitScreenUpdates(enable);
358 void InitVideoDisplay(void)
360 SDLInitVideoDisplay();
363 void CloseVideoDisplay(void)
365 KeyboardAutoRepeatOn();
367 SDL_QuitSubSystem(SDL_INIT_VIDEO);
370 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
373 video.height = height;
374 video.depth = GetRealDepth(depth);
376 video.fullscreen_available = FULLSCREEN_STATUS;
377 video.fullscreen_enabled = FALSE;
379 video.window_scaling_available = WINDOW_SCALING_STATUS;
381 video.frame_delay = 0;
382 video.frame_delay_value = GAME_FRAME_DELAY;
384 SDLInitVideoBuffer(fullscreen);
386 video.initialized = TRUE;
391 inline static void FreeBitmapPointers(Bitmap *bitmap)
396 SDLFreeBitmapPointers(bitmap);
398 checked_free(bitmap->source_filename);
399 bitmap->source_filename = NULL;
402 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
405 if (src_bitmap == NULL || dst_bitmap == NULL)
408 FreeBitmapPointers(dst_bitmap);
410 *dst_bitmap = *src_bitmap;
413 void FreeBitmap(Bitmap *bitmap)
418 FreeBitmapPointers(bitmap);
423 Bitmap *CreateBitmapStruct(void)
425 return checked_calloc(sizeof(Bitmap));
428 Bitmap *CreateBitmap(int width, int height, int depth)
430 Bitmap *new_bitmap = CreateBitmapStruct();
431 int real_width = MAX(1, width); /* prevent zero bitmap width */
432 int real_height = MAX(1, height); /* prevent zero bitmap height */
433 int real_depth = GetRealDepth(depth);
435 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
437 new_bitmap->width = real_width;
438 new_bitmap->height = real_height;
443 void ReCreateBitmap(Bitmap **bitmap, int width, int height, int depth)
445 Bitmap *new_bitmap = CreateBitmap(width, height, depth);
449 *bitmap = new_bitmap;
453 TransferBitmapPointers(new_bitmap, *bitmap);
458 void CloseWindow(DrawWindow *window)
462 void SetRedrawMaskFromArea(int x, int y, int width, int height)
466 int x2 = x + width - 1;
467 int y2 = y + height - 1;
469 if (width == 0 || height == 0)
472 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
473 redraw_mask |= REDRAW_FIELD;
474 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
475 redraw_mask |= REDRAW_DOOR_1;
476 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
477 redraw_mask |= REDRAW_DOOR_2;
478 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
479 redraw_mask |= REDRAW_DOOR_3;
481 redraw_mask = REDRAW_ALL;
484 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
487 if (draw_mask == REDRAW_NONE)
490 if (draw_mask & REDRAW_ALL)
493 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
496 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
499 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
502 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
508 boolean DrawingDeactivated(int x, int y, int width, int height)
510 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
513 boolean DrawingOnBackground(int x, int y)
515 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
516 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
519 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
520 int *width, int *height, boolean is_dest)
522 int clip_x, clip_y, clip_width, clip_height;
524 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
526 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
527 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
528 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
529 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
535 clip_width = bitmap->width;
536 clip_height = bitmap->height;
539 /* skip if rectangle completely outside bitmap */
541 if (*x + *width <= clip_x ||
542 *y + *height <= clip_y ||
543 *x >= clip_x + clip_width ||
544 *y >= clip_y + clip_height)
547 /* clip if rectangle overlaps bitmap */
551 *width -= clip_x - *x;
554 else if (*x + *width > clip_x + clip_width)
556 *width = clip_x + clip_width - *x;
561 *height -= clip_y - *y;
564 else if (*y + *height > clip_y + clip_height)
566 *height = clip_y + clip_height - *y;
572 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
573 int src_x, int src_y, int width, int height,
574 int dst_x, int dst_y)
576 int dst_x_unclipped = dst_x;
577 int dst_y_unclipped = dst_y;
579 if (src_bitmap == NULL || dst_bitmap == NULL)
582 if (DrawingDeactivated(dst_x, dst_y, width, height))
585 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
586 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
589 /* source x/y might need adjustment if destination x/y was clipped top/left */
590 src_x += dst_x - dst_x_unclipped;
591 src_y += dst_y - dst_y_unclipped;
593 #if defined(TARGET_SDL2)
594 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
595 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
596 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
597 but is already fixed in SVN and should therefore finally be fixed with
598 the next official SDL release, which is probably version 1.2.14.) */
599 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
601 if (src_bitmap == dst_bitmap)
603 /* needed when blitting directly to same bitmap -- should not be needed with
604 recent SDL libraries, but apparently does not work in 1.2.11 directly */
606 static Bitmap *tmp_bitmap = NULL;
607 static int tmp_bitmap_xsize = 0;
608 static int tmp_bitmap_ysize = 0;
610 /* start with largest static bitmaps for initial bitmap size ... */
611 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
613 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
614 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
617 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
618 if (src_bitmap->width > tmp_bitmap_xsize ||
619 src_bitmap->height > tmp_bitmap_ysize)
621 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
622 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
624 FreeBitmap(tmp_bitmap);
629 if (tmp_bitmap == NULL)
630 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
633 sysCopyArea(src_bitmap, tmp_bitmap,
634 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
635 sysCopyArea(tmp_bitmap, dst_bitmap,
636 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
642 sysCopyArea(src_bitmap, dst_bitmap,
643 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
646 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
647 int src_x, int src_y, int src_width, int src_height,
648 int dst_x, int dst_y, int dst_width, int dst_height)
650 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
651 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
652 int dst_xsize = dst_width;
653 int dst_ysize = dst_height;
654 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
655 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
658 for (y = 0; y < src_ysteps; y++)
660 for (x = 0; x < src_xsteps; x++)
662 int draw_x = dst_x + x * src_xsize;
663 int draw_y = dst_y + y * src_ysize;
664 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
665 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
667 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
673 void FadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
674 int fade_mode, int fade_delay, int post_delay,
675 void (*draw_border_function)(void))
677 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
678 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
681 SDLFadeRectangle(bitmap_cross, x, y, width, height,
682 fade_mode, fade_delay, post_delay, draw_border_function);
685 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
688 if (DrawingDeactivated(x, y, width, height))
691 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
694 sysFillRectangle(bitmap, x, y, width, height, color);
697 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
699 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
702 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
703 int width, int height)
705 if (DrawingOnBackground(x, y))
706 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
708 ClearRectangle(bitmap, x, y, width, height);
711 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
712 int src_x, int src_y, int width, int height,
713 int dst_x, int dst_y)
715 if (DrawingDeactivated(dst_x, dst_y, width, height))
718 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
719 dst_x, dst_y, BLIT_MASKED);
722 void BlitBitmapOnBackground(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 (DrawingOnBackground(dst_x, dst_y))
728 /* draw background */
729 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
732 /* draw foreground */
733 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
737 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
741 void BlitTexture(Bitmap *bitmap,
742 int src_x, int src_y, int width, int height,
743 int dst_x, int dst_y)
748 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
752 void BlitTextureMasked(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 BlitToScreen(Bitmap *bitmap,
764 int src_x, int src_y, int width, int height,
765 int dst_x, int dst_y)
770 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
771 BlitBitmap(bitmap, gfx.final_screen_bitmap, src_x, src_y,
772 width, height, dst_x, dst_y);
774 BlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y);
777 void BlitToScreenMasked(Bitmap *bitmap,
778 int src_x, int src_y, int width, int height,
779 int dst_x, int dst_y)
784 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
785 BlitBitmapMasked(bitmap, gfx.final_screen_bitmap, src_x, src_y,
786 width, height, dst_x, dst_y);
788 BlitTextureMasked(bitmap, src_x, src_y, width, height, dst_x, dst_y);
791 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
794 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
797 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
800 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
803 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
804 int to_x, int to_y, Pixel pixel, int line_width)
808 for (x = 0; x < line_width; x++)
810 for (y = 0; y < line_width; y++)
812 int dx = x - line_width / 2;
813 int dy = y - line_width / 2;
815 if ((x == 0 && y == 0) ||
816 (x == 0 && y == line_width - 1) ||
817 (x == line_width - 1 && y == 0) ||
818 (x == line_width - 1 && y == line_width - 1))
822 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
827 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
832 for (i = 0; i < num_points - 1; i++)
833 DrawLine(bitmap, points[i].x, points[i].y,
834 points[i + 1].x, points[i + 1].y, pixel, line_width);
837 SDLDrawLines(bitmap->surface, points, num_points, pixel);
841 Pixel GetPixel(Bitmap *bitmap, int x, int y)
843 if (x < 0 || x >= bitmap->width ||
844 y < 0 || y >= bitmap->height)
847 return SDLGetPixel(bitmap, x, y);
850 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
851 unsigned int color_g, unsigned int color_b)
853 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
856 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
858 unsigned int color_r = (color >> 16) & 0xff;
859 unsigned int color_g = (color >> 8) & 0xff;
860 unsigned int color_b = (color >> 0) & 0xff;
862 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
865 void KeyboardAutoRepeatOn(void)
867 #if defined(TARGET_SDL2)
868 keyrepeat_status = TRUE;
870 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
871 SDL_DEFAULT_REPEAT_INTERVAL / 2);
872 SDL_EnableUNICODE(1);
876 void KeyboardAutoRepeatOff(void)
878 #if defined(TARGET_SDL2)
879 keyrepeat_status = FALSE;
881 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
882 SDL_EnableUNICODE(0);
886 boolean SetVideoMode(boolean fullscreen)
888 return SDLSetVideoMode(fullscreen);
891 void SetVideoFrameDelay(unsigned int frame_delay_value)
893 video.frame_delay_value = frame_delay_value;
896 unsigned int GetVideoFrameDelay()
898 return video.frame_delay_value;
901 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
903 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
904 (!fullscreen && video.fullscreen_enabled))
905 fullscreen = SetVideoMode(fullscreen);
910 Bitmap *LoadImage(char *filename)
914 new_bitmap = SDLLoadImage(filename);
917 new_bitmap->source_filename = getStringCopy(filename);
922 Bitmap *LoadCustomImage(char *basename)
924 char *filename = getCustomImageFilename(basename);
927 if (filename == NULL)
928 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
930 if ((new_bitmap = LoadImage(filename)) == NULL)
931 Error(ERR_EXIT, "LoadImage('%s') failed: %s", basename, GetError());
936 void ReloadCustomImage(Bitmap *bitmap, char *basename)
938 char *filename = getCustomImageFilename(basename);
941 if (filename == NULL) /* (should never happen) */
943 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
947 if (strEqual(filename, bitmap->source_filename))
949 /* The old and new image are the same (have the same filename and path).
950 This usually means that this image does not exist in this graphic set
951 and a fallback to the existing image is done. */
956 if ((new_bitmap = LoadImage(filename)) == NULL)
958 Error(ERR_WARN, "LoadImage('%s') failed: %s", basename, GetError());
962 if (bitmap->width != new_bitmap->width ||
963 bitmap->height != new_bitmap->height)
965 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
967 FreeBitmap(new_bitmap);
971 TransferBitmapPointers(new_bitmap, bitmap);
975 Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
977 Bitmap *dst_bitmap = SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
982 static void SetMaskedBitmapSurface(Bitmap *bitmap)
987 SDL_Surface *surface = bitmap->surface;
989 if (bitmap->surface_masked)
990 SDL_FreeSurface(bitmap->surface_masked);
992 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
993 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
995 if ((bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
996 Error(ERR_EXIT, "SDL_DisplayFormat() failed");
998 SDL_SetColorKey(surface, UNSET_TRANSPARENT_PIXEL, 0);
1001 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
1003 if (bitmaps[IMG_BITMAP_CUSTOM])
1005 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
1007 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
1010 if (gfx.game_tile_size == gfx.standard_tile_size)
1012 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1017 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
1018 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
1019 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
1021 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
1023 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
1024 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
1026 SetMaskedBitmapSurface(bitmap_new);
1029 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1030 int tile_size, boolean create_small_bitmaps)
1032 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1033 Bitmap *tmp_bitmap_final = NULL;
1034 Bitmap *tmp_bitmap_0 = NULL;
1035 Bitmap *tmp_bitmap_1 = NULL;
1036 Bitmap *tmp_bitmap_2 = NULL;
1037 Bitmap *tmp_bitmap_4 = NULL;
1038 Bitmap *tmp_bitmap_8 = NULL;
1039 Bitmap *tmp_bitmap_16 = NULL;
1040 Bitmap *tmp_bitmap_32 = NULL;
1041 int width_final, height_final;
1042 int width_0, height_0;
1043 int width_1, height_1;
1044 int width_2, height_2;
1045 int width_4, height_4;
1046 int width_8, height_8;
1047 int width_16, height_16;
1048 int width_32, height_32;
1049 int old_width, old_height;
1052 print_timestamp_init("CreateScaledBitmaps");
1054 old_width = old_bitmap->width;
1055 old_height = old_bitmap->height;
1057 /* calculate new image dimensions for final image size */
1058 width_final = old_width * zoom_factor;
1059 height_final = old_height * zoom_factor;
1061 /* get image with final size (this might require scaling up) */
1062 /* ("final" size may result in non-standard tile size image) */
1063 if (zoom_factor != 1)
1064 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1066 tmp_bitmap_final = old_bitmap;
1068 UPDATE_BUSY_STATE();
1070 width_0 = width_1 = width_final;
1071 height_0 = height_1 = height_final;
1073 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1075 if (create_small_bitmaps)
1077 /* check if we have a non-gameplay tile size image */
1078 if (tile_size != gfx.game_tile_size)
1080 /* get image with gameplay tile size */
1081 width_0 = width_final * gfx.game_tile_size / tile_size;
1082 height_0 = height_final * gfx.game_tile_size / tile_size;
1084 if (width_0 == old_width)
1085 tmp_bitmap_0 = old_bitmap;
1086 else if (width_0 == width_final)
1087 tmp_bitmap_0 = tmp_bitmap_final;
1089 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1091 UPDATE_BUSY_STATE();
1094 /* check if we have a non-standard tile size image */
1095 if (tile_size != gfx.standard_tile_size)
1097 /* get image with standard tile size */
1098 width_1 = width_final * gfx.standard_tile_size / tile_size;
1099 height_1 = height_final * gfx.standard_tile_size / tile_size;
1101 if (width_1 == old_width)
1102 tmp_bitmap_1 = old_bitmap;
1103 else if (width_1 == width_final)
1104 tmp_bitmap_1 = tmp_bitmap_final;
1105 else if (width_1 == width_0)
1106 tmp_bitmap_1 = tmp_bitmap_0;
1108 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1110 UPDATE_BUSY_STATE();
1113 /* calculate new image dimensions for small images */
1114 width_2 = width_1 / 2;
1115 height_2 = height_1 / 2;
1116 width_4 = width_1 / 4;
1117 height_4 = height_1 / 4;
1118 width_8 = width_1 / 8;
1119 height_8 = height_1 / 8;
1120 width_16 = width_1 / 16;
1121 height_16 = height_1 / 16;
1122 width_32 = width_1 / 32;
1123 height_32 = height_1 / 32;
1125 /* get image with 1/2 of normal size (for use in the level editor) */
1126 if (width_2 == old_width)
1127 tmp_bitmap_2 = old_bitmap;
1129 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1131 UPDATE_BUSY_STATE();
1133 /* get image with 1/4 of normal size (for use in the level editor) */
1134 if (width_4 == old_width)
1135 tmp_bitmap_4 = old_bitmap;
1137 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1139 UPDATE_BUSY_STATE();
1141 /* get image with 1/8 of normal size (for use on the preview screen) */
1142 if (width_8 == old_width)
1143 tmp_bitmap_8 = old_bitmap;
1145 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1147 UPDATE_BUSY_STATE();
1149 /* get image with 1/16 of normal size (for use on the preview screen) */
1150 if (width_16 == old_width)
1151 tmp_bitmap_16 = old_bitmap;
1153 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1155 UPDATE_BUSY_STATE();
1157 /* get image with 1/32 of normal size (for use on the preview screen) */
1158 if (width_32 == old_width)
1159 tmp_bitmap_32 = old_bitmap;
1161 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1163 UPDATE_BUSY_STATE();
1165 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1166 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1167 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1168 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1169 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1170 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1172 if (width_0 != width_1)
1173 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1175 if (bitmaps[IMG_BITMAP_CUSTOM])
1176 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1178 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1180 boolean free_old_bitmap = TRUE;
1182 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1183 if (bitmaps[i] == old_bitmap)
1184 free_old_bitmap = FALSE;
1186 if (free_old_bitmap)
1187 FreeBitmap(old_bitmap);
1191 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1194 // create corresponding bitmaps for masked blitting
1195 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1196 if (bitmaps[i] != NULL &&
1197 bitmaps[i] != old_bitmap)
1198 SetMaskedBitmapSurface(bitmaps[i]);
1200 UPDATE_BUSY_STATE();
1202 print_timestamp_done("CreateScaledBitmaps");
1205 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1208 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1211 void CreateBitmapTextures(Bitmap **bitmaps)
1213 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1216 void FreeBitmapTextures(Bitmap **bitmaps)
1218 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1221 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1223 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1227 /* ------------------------------------------------------------------------- */
1228 /* mouse pointer functions */
1229 /* ------------------------------------------------------------------------- */
1231 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1233 /* XPM image definitions */
1234 static const char *cursor_image_none[] =
1236 /* width height num_colors chars_per_pixel */
1266 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1267 static const char *cursor_image_dot[] =
1269 /* width height num_colors chars_per_pixel */
1298 static const char **cursor_image_playfield = cursor_image_dot;
1300 /* some people complained about a "white dot" on the screen and thought it
1301 was a graphical error... OK, let's just remove the whole pointer :-) */
1302 static const char **cursor_image_playfield = cursor_image_none;
1305 static const int cursor_bit_order = BIT_ORDER_MSB;
1307 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1309 struct MouseCursorInfo *cursor;
1310 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1311 int header_lines = 4;
1314 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1316 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1319 for (y = 0; y < cursor->width; y++)
1321 for (x = 0; x < cursor->height; x++)
1324 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1329 cursor->data[i] = cursor->mask[i] = 0;
1332 switch (image[header_lines + y][x])
1335 cursor->data[i] |= bit_mask;
1336 cursor->mask[i] |= bit_mask;
1340 cursor->mask[i] |= bit_mask;
1349 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1354 void SetMouseCursor(int mode)
1356 static struct MouseCursorInfo *cursor_none = NULL;
1357 static struct MouseCursorInfo *cursor_playfield = NULL;
1358 struct MouseCursorInfo *cursor_new;
1360 if (cursor_none == NULL)
1361 cursor_none = get_cursor_from_image(cursor_image_none);
1363 if (cursor_playfield == NULL)
1364 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1366 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1367 mode == CURSOR_NONE ? cursor_none :
1368 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1370 SDLSetMouseCursor(cursor_new);
1372 gfx.cursor_mode = mode;
1376 /* ========================================================================= */
1377 /* audio functions */
1378 /* ========================================================================= */
1380 void OpenAudio(void)
1382 /* always start with reliable default values */
1383 audio.sound_available = FALSE;
1384 audio.music_available = FALSE;
1385 audio.loops_available = FALSE;
1387 audio.sound_enabled = FALSE;
1388 audio.sound_deactivated = FALSE;
1390 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1391 audio.mixer_pid = 0;
1392 audio.device_name = NULL;
1393 audio.device_fd = -1;
1395 audio.num_channels = 0;
1396 audio.music_channel = 0;
1397 audio.first_sound_channel = 0;
1402 void CloseAudio(void)
1406 audio.sound_enabled = FALSE;
1409 void SetAudioMode(boolean enabled)
1411 if (!audio.sound_available)
1414 audio.sound_enabled = enabled;
1418 /* ========================================================================= */
1419 /* event functions */
1420 /* ========================================================================= */
1422 void InitEventFilter(EventFilter filter_function)
1424 /* set event filter to filter out certain events */
1425 #if defined(TARGET_SDL2)
1426 SDL_SetEventFilter(filter_function, NULL);
1428 SDL_SetEventFilter(filter_function);
1432 boolean PendingEvent(void)
1434 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1437 void NextEvent(Event *event)
1439 SDLNextEvent(event);
1442 void PeekEvent(Event *event)
1444 #if defined(TARGET_SDL2)
1445 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1447 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1451 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1453 #if defined(TARGET_SDL2)
1454 /* key up/down events in SDL2 do not return text characters anymore */
1455 return event->keysym.sym;
1458 #if ENABLE_UNUSED_CODE
1459 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1460 (int)event->keysym.unicode,
1461 (int)event->keysym.sym,
1462 (int)SDL_GetModState());
1465 if (with_modifiers &&
1466 event->keysym.unicode > 0x0000 &&
1467 event->keysym.unicode < 0x2000)
1468 return event->keysym.unicode;
1470 return event->keysym.sym;
1475 KeyMod HandleKeyModState(Key key, int key_status)
1477 static KeyMod current_modifiers = KMOD_None;
1479 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1481 KeyMod new_modifier = KMOD_None;
1486 new_modifier = KMOD_Shift_L;
1489 new_modifier = KMOD_Shift_R;
1491 case KSYM_Control_L:
1492 new_modifier = KMOD_Control_L;
1494 case KSYM_Control_R:
1495 new_modifier = KMOD_Control_R;
1498 new_modifier = KMOD_Meta_L;
1501 new_modifier = KMOD_Meta_R;
1504 new_modifier = KMOD_Alt_L;
1507 new_modifier = KMOD_Alt_R;
1513 if (key_status == KEY_PRESSED)
1514 current_modifiers |= new_modifier;
1516 current_modifiers &= ~new_modifier;
1519 return current_modifiers;
1522 KeyMod GetKeyModState()
1524 return (KeyMod)SDL_GetModState();
1527 KeyMod GetKeyModStateFromEvents()
1529 /* always use key modifier state as tracked from key events (this is needed
1530 if the modifier key event was injected into the event queue, but the key
1531 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1532 query the keys as held pressed on the keyboard) -- this case is currently
1533 only used to filter out clipboard insert events from "True X-Mouse" tool */
1535 return HandleKeyModState(KSYM_UNDEFINED, 0);
1538 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1540 if (event->type != EVENT_CLIENTMESSAGE)
1543 return TRUE; /* the only possible message here is SDL_QUIT */
1547 /* ========================================================================= */
1548 /* joystick functions */
1549 /* ========================================================================= */
1551 void InitJoysticks()
1555 #if defined(NO_JOYSTICK)
1556 return; /* joysticks generally deactivated by compile-time directive */
1559 /* always start with reliable default values */
1560 joystick.status = JOYSTICK_NOT_AVAILABLE;
1561 for (i = 0; i < MAX_PLAYERS; i++)
1562 joystick.fd[i] = -1; /* joystick device closed */
1567 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1569 return SDLReadJoystick(nr, x, y, b1, b2);