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_backup, win_xsize, win_ysize, DEFAULT_DEPTH);
216 ReCreateBitmap(&gfx.fade_bitmap_source, win_xsize, win_ysize, DEFAULT_DEPTH);
217 ReCreateBitmap(&gfx.fade_bitmap_target, win_xsize, win_ysize, DEFAULT_DEPTH);
218 ReCreateBitmap(&gfx.fade_bitmap_black, win_xsize, win_ysize, DEFAULT_DEPTH);
220 ClearRectangle(gfx.fade_bitmap_black, 0, 0, win_xsize, win_ysize);
223 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
225 /* currently only used by MSDOS code to alloc VRAM buffer, if available */
226 /* 2009-03-24: also (temporarily?) used for overlapping blit workaround */
227 gfx.scrollbuffer_width = scrollbuffer_width;
228 gfx.scrollbuffer_height = scrollbuffer_height;
231 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
233 gfx.clipping_enabled = enabled;
236 gfx.clip_width = width;
237 gfx.clip_height = height;
240 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
242 gfx.draw_busy_anim_function = draw_busy_anim_function;
245 void InitGfxDrawGlobalAnimFunction(void (*draw_global_anim_function)(int, int))
247 gfx.draw_global_anim_function = draw_global_anim_function;
250 void InitGfxDrawGlobalBorderFunction(void (*draw_global_border_function)(int))
252 gfx.draw_global_border_function = draw_global_border_function;
255 void InitGfxCustomArtworkInfo()
257 gfx.override_level_graphics = FALSE;
258 gfx.override_level_sounds = FALSE;
259 gfx.override_level_music = FALSE;
261 gfx.draw_init_text = TRUE;
264 void InitGfxOtherSettings()
266 gfx.cursor_mode = CURSOR_DEFAULT;
269 void SetDrawDeactivationMask(int draw_deactivation_mask)
271 gfx.draw_deactivation_mask = draw_deactivation_mask;
274 void SetDrawBackgroundMask(int draw_background_mask)
276 gfx.draw_background_mask = draw_background_mask;
279 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
281 if (background_bitmap_tile != NULL)
282 gfx.background_bitmap_mask |= mask;
284 gfx.background_bitmap_mask &= ~mask;
286 if (background_bitmap_tile == NULL) /* empty background requested */
289 if (mask == REDRAW_ALL)
290 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
291 0, 0, video.width, video.height);
292 else if (mask == REDRAW_FIELD)
293 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
294 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
295 else if (mask == REDRAW_DOOR_1)
296 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
297 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
300 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
302 /* remove every mask before setting mask for window */
303 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
304 SetBackgroundBitmap(NULL, 0xffff); /* !!! FIX THIS !!! */
305 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
308 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
310 /* remove window area mask before setting mask for main area */
311 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
312 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
313 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
316 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
318 /* remove window area mask before setting mask for door area */
319 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
320 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
321 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
325 /* ========================================================================= */
326 /* video functions */
327 /* ========================================================================= */
329 inline static int GetRealDepth(int depth)
331 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
334 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
335 int width, int height, Pixel color)
337 SDLFillRectangle(bitmap, x, y, width, height, color);
339 if (bitmap == backbuffer)
340 SetRedrawMaskFromArea(x, y, width, height);
343 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
344 int src_x, int src_y, int width, int height,
345 int dst_x, int dst_y, int mask_mode)
347 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
348 dst_x, dst_y, mask_mode);
350 if (dst_bitmap == backbuffer)
351 SetRedrawMaskFromArea(dst_x, dst_y, width, height);
354 void LimitScreenUpdates(boolean enable)
356 SDLLimitScreenUpdates(enable);
359 void InitVideoDisplay(void)
361 SDLInitVideoDisplay();
364 void CloseVideoDisplay(void)
366 KeyboardAutoRepeatOn();
368 SDL_QuitSubSystem(SDL_INIT_VIDEO);
371 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
374 video.height = height;
375 video.depth = GetRealDepth(depth);
377 video.fullscreen_available = FULLSCREEN_STATUS;
378 video.fullscreen_enabled = FALSE;
380 video.window_scaling_available = WINDOW_SCALING_STATUS;
382 video.frame_delay = 0;
383 video.frame_delay_value = GAME_FRAME_DELAY;
385 SDLInitVideoBuffer(fullscreen);
387 video.initialized = TRUE;
392 inline static void FreeBitmapPointers(Bitmap *bitmap)
397 SDLFreeBitmapPointers(bitmap);
399 checked_free(bitmap->source_filename);
400 bitmap->source_filename = NULL;
403 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
406 if (src_bitmap == NULL || dst_bitmap == NULL)
409 FreeBitmapPointers(dst_bitmap);
411 *dst_bitmap = *src_bitmap;
414 void FreeBitmap(Bitmap *bitmap)
419 FreeBitmapPointers(bitmap);
424 Bitmap *CreateBitmapStruct(void)
426 return checked_calloc(sizeof(Bitmap));
429 Bitmap *CreateBitmap(int width, int height, int depth)
431 Bitmap *new_bitmap = CreateBitmapStruct();
432 int real_width = MAX(1, width); /* prevent zero bitmap width */
433 int real_height = MAX(1, height); /* prevent zero bitmap height */
434 int real_depth = GetRealDepth(depth);
436 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
438 new_bitmap->width = real_width;
439 new_bitmap->height = real_height;
444 void ReCreateBitmap(Bitmap **bitmap, int width, int height, int depth)
446 Bitmap *new_bitmap = CreateBitmap(width, height, depth);
450 *bitmap = new_bitmap;
454 TransferBitmapPointers(new_bitmap, *bitmap);
459 void CloseWindow(DrawWindow *window)
463 void SetRedrawMaskFromArea(int x, int y, int width, int height)
467 int x2 = x + width - 1;
468 int y2 = y + height - 1;
470 if (width == 0 || height == 0)
473 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
474 redraw_mask |= REDRAW_FIELD;
475 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
476 redraw_mask |= REDRAW_DOOR_1;
477 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
478 redraw_mask |= REDRAW_DOOR_2;
479 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
480 redraw_mask |= REDRAW_DOOR_3;
482 redraw_mask = REDRAW_ALL;
485 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
488 if (draw_mask == REDRAW_NONE)
491 if (draw_mask & REDRAW_ALL)
494 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
497 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
500 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
503 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
509 boolean DrawingDeactivated(int x, int y, int width, int height)
511 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
514 boolean DrawingOnBackground(int x, int y)
516 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
517 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
520 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
521 int *width, int *height, boolean is_dest)
523 int clip_x, clip_y, clip_width, clip_height;
525 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
527 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
528 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
529 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
530 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
536 clip_width = bitmap->width;
537 clip_height = bitmap->height;
540 /* skip if rectangle completely outside bitmap */
542 if (*x + *width <= clip_x ||
543 *y + *height <= clip_y ||
544 *x >= clip_x + clip_width ||
545 *y >= clip_y + clip_height)
548 /* clip if rectangle overlaps bitmap */
552 *width -= clip_x - *x;
555 else if (*x + *width > clip_x + clip_width)
557 *width = clip_x + clip_width - *x;
562 *height -= clip_y - *y;
565 else if (*y + *height > clip_y + clip_height)
567 *height = clip_y + clip_height - *y;
573 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
574 int src_x, int src_y, int width, int height,
575 int dst_x, int dst_y)
577 int dst_x_unclipped = dst_x;
578 int dst_y_unclipped = dst_y;
580 if (src_bitmap == NULL || dst_bitmap == NULL)
583 if (DrawingDeactivated(dst_x, dst_y, width, height))
586 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
587 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
590 /* source x/y might need adjustment if destination x/y was clipped top/left */
591 src_x += dst_x - dst_x_unclipped;
592 src_y += dst_y - dst_y_unclipped;
594 #if defined(TARGET_SDL2)
595 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
596 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
597 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
598 but is already fixed in SVN and should therefore finally be fixed with
599 the next official SDL release, which is probably version 1.2.14.) */
600 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
602 if (src_bitmap == dst_bitmap)
604 /* needed when blitting directly to same bitmap -- should not be needed with
605 recent SDL libraries, but apparently does not work in 1.2.11 directly */
607 static Bitmap *tmp_bitmap = NULL;
608 static int tmp_bitmap_xsize = 0;
609 static int tmp_bitmap_ysize = 0;
611 /* start with largest static bitmaps for initial bitmap size ... */
612 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
614 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
615 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
618 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
619 if (src_bitmap->width > tmp_bitmap_xsize ||
620 src_bitmap->height > tmp_bitmap_ysize)
622 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
623 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
625 FreeBitmap(tmp_bitmap);
630 if (tmp_bitmap == NULL)
631 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
634 sysCopyArea(src_bitmap, tmp_bitmap,
635 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
636 sysCopyArea(tmp_bitmap, dst_bitmap,
637 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
643 sysCopyArea(src_bitmap, dst_bitmap,
644 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
647 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
648 int src_x, int src_y, int src_width, int src_height,
649 int dst_x, int dst_y, int dst_width, int dst_height)
651 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
652 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
653 int dst_xsize = dst_width;
654 int dst_ysize = dst_height;
655 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
656 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
659 for (y = 0; y < src_ysteps; y++)
661 for (x = 0; x < src_xsteps; x++)
663 int draw_x = dst_x + x * src_xsize;
664 int draw_y = dst_y + y * src_ysize;
665 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
666 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
668 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
674 void FadeRectangle(int x, int y, int width, int height,
675 int fade_mode, int fade_delay, int post_delay,
676 void (*draw_border_function)(void))
678 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
679 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
682 SDLFadeRectangle(x, y, width, height,
683 fade_mode, fade_delay, post_delay, draw_border_function);
686 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
689 if (DrawingDeactivated(x, y, width, height))
692 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
695 sysFillRectangle(bitmap, x, y, width, height, color);
698 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
700 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
703 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
704 int width, int height)
706 if (DrawingOnBackground(x, y))
707 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
709 ClearRectangle(bitmap, x, y, width, height);
712 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
713 int src_x, int src_y, int width, int height,
714 int dst_x, int dst_y)
716 if (DrawingDeactivated(dst_x, dst_y, width, height))
719 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
720 dst_x, dst_y, BLIT_MASKED);
723 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
724 int src_x, int src_y, int width, int height,
725 int dst_x, int dst_y)
727 if (DrawingOnBackground(dst_x, dst_y))
729 /* draw background */
730 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
733 /* draw foreground */
734 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
738 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
742 void BlitTexture(Bitmap *bitmap,
743 int src_x, int src_y, int width, int height,
744 int dst_x, int dst_y)
749 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
753 void BlitTextureMasked(Bitmap *bitmap,
754 int src_x, int src_y, int width, int height,
755 int dst_x, int dst_y)
760 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
764 void BlitToScreen(Bitmap *bitmap,
765 int src_x, int src_y, int width, int height,
766 int dst_x, int dst_y)
771 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
772 BlitBitmap(bitmap, gfx.final_screen_bitmap, src_x, src_y,
773 width, height, dst_x, dst_y);
775 BlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y);
778 void BlitToScreenMasked(Bitmap *bitmap,
779 int src_x, int src_y, int width, int height,
780 int dst_x, int dst_y)
785 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
786 BlitBitmapMasked(bitmap, gfx.final_screen_bitmap, src_x, src_y,
787 width, height, dst_x, dst_y);
789 BlitTextureMasked(bitmap, src_x, src_y, width, height, dst_x, dst_y);
792 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
795 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
798 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
801 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
804 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
805 int to_x, int to_y, Pixel pixel, int line_width)
809 for (x = 0; x < line_width; x++)
811 for (y = 0; y < line_width; y++)
813 int dx = x - line_width / 2;
814 int dy = y - line_width / 2;
816 if ((x == 0 && y == 0) ||
817 (x == 0 && y == line_width - 1) ||
818 (x == line_width - 1 && y == 0) ||
819 (x == line_width - 1 && y == line_width - 1))
823 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
828 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
833 for (i = 0; i < num_points - 1; i++)
834 DrawLine(bitmap, points[i].x, points[i].y,
835 points[i + 1].x, points[i + 1].y, pixel, line_width);
838 SDLDrawLines(bitmap->surface, points, num_points, pixel);
842 Pixel GetPixel(Bitmap *bitmap, int x, int y)
844 if (x < 0 || x >= bitmap->width ||
845 y < 0 || y >= bitmap->height)
848 return SDLGetPixel(bitmap, x, y);
851 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
852 unsigned int color_g, unsigned int color_b)
854 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
857 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
859 unsigned int color_r = (color >> 16) & 0xff;
860 unsigned int color_g = (color >> 8) & 0xff;
861 unsigned int color_b = (color >> 0) & 0xff;
863 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
866 void KeyboardAutoRepeatOn(void)
868 #if defined(TARGET_SDL2)
869 keyrepeat_status = TRUE;
871 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
872 SDL_DEFAULT_REPEAT_INTERVAL / 2);
873 SDL_EnableUNICODE(1);
877 void KeyboardAutoRepeatOff(void)
879 #if defined(TARGET_SDL2)
880 keyrepeat_status = FALSE;
882 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
883 SDL_EnableUNICODE(0);
887 boolean SetVideoMode(boolean fullscreen)
889 return SDLSetVideoMode(fullscreen);
892 void SetVideoFrameDelay(unsigned int frame_delay_value)
894 video.frame_delay_value = frame_delay_value;
897 unsigned int GetVideoFrameDelay()
899 return video.frame_delay_value;
902 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
904 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
905 (!fullscreen && video.fullscreen_enabled))
906 fullscreen = SetVideoMode(fullscreen);
911 Bitmap *LoadImage(char *filename)
915 new_bitmap = SDLLoadImage(filename);
918 new_bitmap->source_filename = getStringCopy(filename);
923 Bitmap *LoadCustomImage(char *basename)
925 char *filename = getCustomImageFilename(basename);
928 if (filename == NULL)
929 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
931 if ((new_bitmap = LoadImage(filename)) == NULL)
932 Error(ERR_EXIT, "LoadImage('%s') failed: %s", basename, GetError());
937 void ReloadCustomImage(Bitmap *bitmap, char *basename)
939 char *filename = getCustomImageFilename(basename);
942 if (filename == NULL) /* (should never happen) */
944 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
948 if (strEqual(filename, bitmap->source_filename))
950 /* The old and new image are the same (have the same filename and path).
951 This usually means that this image does not exist in this graphic set
952 and a fallback to the existing image is done. */
957 if ((new_bitmap = LoadImage(filename)) == NULL)
959 Error(ERR_WARN, "LoadImage('%s') failed: %s", basename, GetError());
963 if (bitmap->width != new_bitmap->width ||
964 bitmap->height != new_bitmap->height)
966 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
968 FreeBitmap(new_bitmap);
972 TransferBitmapPointers(new_bitmap, bitmap);
976 Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
978 Bitmap *dst_bitmap = SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
983 static void SetMaskedBitmapSurface(Bitmap *bitmap)
988 SDL_Surface *surface = bitmap->surface;
990 if (bitmap->surface_masked)
991 SDL_FreeSurface(bitmap->surface_masked);
993 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
994 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
996 if ((bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
997 Error(ERR_EXIT, "SDL_DisplayFormat() failed");
999 SDL_SetColorKey(surface, UNSET_TRANSPARENT_PIXEL, 0);
1002 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
1004 if (bitmaps[IMG_BITMAP_CUSTOM])
1006 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
1008 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
1011 if (gfx.game_tile_size == gfx.standard_tile_size)
1013 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1018 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
1019 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
1020 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
1022 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
1024 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
1025 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
1027 SetMaskedBitmapSurface(bitmap_new);
1030 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1031 int tile_size, boolean create_small_bitmaps)
1033 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1034 Bitmap *tmp_bitmap_final = NULL;
1035 Bitmap *tmp_bitmap_0 = NULL;
1036 Bitmap *tmp_bitmap_1 = NULL;
1037 Bitmap *tmp_bitmap_2 = NULL;
1038 Bitmap *tmp_bitmap_4 = NULL;
1039 Bitmap *tmp_bitmap_8 = NULL;
1040 Bitmap *tmp_bitmap_16 = NULL;
1041 Bitmap *tmp_bitmap_32 = NULL;
1042 int width_final, height_final;
1043 int width_0, height_0;
1044 int width_1, height_1;
1045 int width_2, height_2;
1046 int width_4, height_4;
1047 int width_8, height_8;
1048 int width_16, height_16;
1049 int width_32, height_32;
1050 int old_width, old_height;
1053 print_timestamp_init("CreateScaledBitmaps");
1055 old_width = old_bitmap->width;
1056 old_height = old_bitmap->height;
1058 /* calculate new image dimensions for final image size */
1059 width_final = old_width * zoom_factor;
1060 height_final = old_height * zoom_factor;
1062 /* get image with final size (this might require scaling up) */
1063 /* ("final" size may result in non-standard tile size image) */
1064 if (zoom_factor != 1)
1065 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1067 tmp_bitmap_final = old_bitmap;
1069 UPDATE_BUSY_STATE();
1071 width_0 = width_1 = width_final;
1072 height_0 = height_1 = height_final;
1074 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1076 if (create_small_bitmaps)
1078 /* check if we have a non-gameplay tile size image */
1079 if (tile_size != gfx.game_tile_size)
1081 /* get image with gameplay tile size */
1082 width_0 = width_final * gfx.game_tile_size / tile_size;
1083 height_0 = height_final * gfx.game_tile_size / tile_size;
1085 if (width_0 == old_width)
1086 tmp_bitmap_0 = old_bitmap;
1087 else if (width_0 == width_final)
1088 tmp_bitmap_0 = tmp_bitmap_final;
1090 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1092 UPDATE_BUSY_STATE();
1095 /* check if we have a non-standard tile size image */
1096 if (tile_size != gfx.standard_tile_size)
1098 /* get image with standard tile size */
1099 width_1 = width_final * gfx.standard_tile_size / tile_size;
1100 height_1 = height_final * gfx.standard_tile_size / tile_size;
1102 if (width_1 == old_width)
1103 tmp_bitmap_1 = old_bitmap;
1104 else if (width_1 == width_final)
1105 tmp_bitmap_1 = tmp_bitmap_final;
1106 else if (width_1 == width_0)
1107 tmp_bitmap_1 = tmp_bitmap_0;
1109 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1111 UPDATE_BUSY_STATE();
1114 /* calculate new image dimensions for small images */
1115 width_2 = width_1 / 2;
1116 height_2 = height_1 / 2;
1117 width_4 = width_1 / 4;
1118 height_4 = height_1 / 4;
1119 width_8 = width_1 / 8;
1120 height_8 = height_1 / 8;
1121 width_16 = width_1 / 16;
1122 height_16 = height_1 / 16;
1123 width_32 = width_1 / 32;
1124 height_32 = height_1 / 32;
1126 /* get image with 1/2 of normal size (for use in the level editor) */
1127 if (width_2 == old_width)
1128 tmp_bitmap_2 = old_bitmap;
1130 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1132 UPDATE_BUSY_STATE();
1134 /* get image with 1/4 of normal size (for use in the level editor) */
1135 if (width_4 == old_width)
1136 tmp_bitmap_4 = old_bitmap;
1138 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1140 UPDATE_BUSY_STATE();
1142 /* get image with 1/8 of normal size (for use on the preview screen) */
1143 if (width_8 == old_width)
1144 tmp_bitmap_8 = old_bitmap;
1146 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1148 UPDATE_BUSY_STATE();
1150 /* get image with 1/16 of normal size (for use on the preview screen) */
1151 if (width_16 == old_width)
1152 tmp_bitmap_16 = old_bitmap;
1154 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1156 UPDATE_BUSY_STATE();
1158 /* get image with 1/32 of normal size (for use on the preview screen) */
1159 if (width_32 == old_width)
1160 tmp_bitmap_32 = old_bitmap;
1162 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1164 UPDATE_BUSY_STATE();
1166 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1167 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1168 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1169 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1170 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1171 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1173 if (width_0 != width_1)
1174 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1176 if (bitmaps[IMG_BITMAP_CUSTOM])
1177 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1179 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1181 boolean free_old_bitmap = TRUE;
1183 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1184 if (bitmaps[i] == old_bitmap)
1185 free_old_bitmap = FALSE;
1187 if (free_old_bitmap)
1188 FreeBitmap(old_bitmap);
1192 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1195 // create corresponding bitmaps for masked blitting
1196 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1197 if (bitmaps[i] != NULL &&
1198 bitmaps[i] != old_bitmap)
1199 SetMaskedBitmapSurface(bitmaps[i]);
1201 UPDATE_BUSY_STATE();
1203 print_timestamp_done("CreateScaledBitmaps");
1206 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1209 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1212 void CreateBitmapTextures(Bitmap **bitmaps)
1214 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1217 void FreeBitmapTextures(Bitmap **bitmaps)
1219 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1222 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1224 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1228 /* ------------------------------------------------------------------------- */
1229 /* mouse pointer functions */
1230 /* ------------------------------------------------------------------------- */
1232 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1234 /* XPM image definitions */
1235 static const char *cursor_image_none[] =
1237 /* width height num_colors chars_per_pixel */
1267 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1268 static const char *cursor_image_dot[] =
1270 /* width height num_colors chars_per_pixel */
1299 static const char **cursor_image_playfield = cursor_image_dot;
1301 /* some people complained about a "white dot" on the screen and thought it
1302 was a graphical error... OK, let's just remove the whole pointer :-) */
1303 static const char **cursor_image_playfield = cursor_image_none;
1306 static const int cursor_bit_order = BIT_ORDER_MSB;
1308 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1310 struct MouseCursorInfo *cursor;
1311 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1312 int header_lines = 4;
1315 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1317 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1320 for (y = 0; y < cursor->width; y++)
1322 for (x = 0; x < cursor->height; x++)
1325 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1330 cursor->data[i] = cursor->mask[i] = 0;
1333 switch (image[header_lines + y][x])
1336 cursor->data[i] |= bit_mask;
1337 cursor->mask[i] |= bit_mask;
1341 cursor->mask[i] |= bit_mask;
1350 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1355 void SetMouseCursor(int mode)
1357 static struct MouseCursorInfo *cursor_none = NULL;
1358 static struct MouseCursorInfo *cursor_playfield = NULL;
1359 struct MouseCursorInfo *cursor_new;
1361 if (cursor_none == NULL)
1362 cursor_none = get_cursor_from_image(cursor_image_none);
1364 if (cursor_playfield == NULL)
1365 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1367 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1368 mode == CURSOR_NONE ? cursor_none :
1369 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1371 SDLSetMouseCursor(cursor_new);
1373 gfx.cursor_mode = mode;
1377 /* ========================================================================= */
1378 /* audio functions */
1379 /* ========================================================================= */
1381 void OpenAudio(void)
1383 /* always start with reliable default values */
1384 audio.sound_available = FALSE;
1385 audio.music_available = FALSE;
1386 audio.loops_available = FALSE;
1388 audio.sound_enabled = FALSE;
1389 audio.sound_deactivated = FALSE;
1391 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1392 audio.mixer_pid = 0;
1393 audio.device_name = NULL;
1394 audio.device_fd = -1;
1396 audio.num_channels = 0;
1397 audio.music_channel = 0;
1398 audio.first_sound_channel = 0;
1403 void CloseAudio(void)
1407 audio.sound_enabled = FALSE;
1410 void SetAudioMode(boolean enabled)
1412 if (!audio.sound_available)
1415 audio.sound_enabled = enabled;
1419 /* ========================================================================= */
1420 /* event functions */
1421 /* ========================================================================= */
1423 void InitEventFilter(EventFilter filter_function)
1425 /* set event filter to filter out certain events */
1426 #if defined(TARGET_SDL2)
1427 SDL_SetEventFilter(filter_function, NULL);
1429 SDL_SetEventFilter(filter_function);
1433 boolean PendingEvent(void)
1435 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1438 void NextEvent(Event *event)
1440 SDLNextEvent(event);
1443 void PeekEvent(Event *event)
1445 #if defined(TARGET_SDL2)
1446 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1448 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1452 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1454 #if defined(TARGET_SDL2)
1455 /* key up/down events in SDL2 do not return text characters anymore */
1456 return event->keysym.sym;
1459 #if ENABLE_UNUSED_CODE
1460 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1461 (int)event->keysym.unicode,
1462 (int)event->keysym.sym,
1463 (int)SDL_GetModState());
1466 if (with_modifiers &&
1467 event->keysym.unicode > 0x0000 &&
1468 event->keysym.unicode < 0x2000)
1469 return event->keysym.unicode;
1471 return event->keysym.sym;
1476 KeyMod HandleKeyModState(Key key, int key_status)
1478 static KeyMod current_modifiers = KMOD_None;
1480 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1482 KeyMod new_modifier = KMOD_None;
1487 new_modifier = KMOD_Shift_L;
1490 new_modifier = KMOD_Shift_R;
1492 case KSYM_Control_L:
1493 new_modifier = KMOD_Control_L;
1495 case KSYM_Control_R:
1496 new_modifier = KMOD_Control_R;
1499 new_modifier = KMOD_Meta_L;
1502 new_modifier = KMOD_Meta_R;
1505 new_modifier = KMOD_Alt_L;
1508 new_modifier = KMOD_Alt_R;
1514 if (key_status == KEY_PRESSED)
1515 current_modifiers |= new_modifier;
1517 current_modifiers &= ~new_modifier;
1520 return current_modifiers;
1523 KeyMod GetKeyModState()
1525 return (KeyMod)SDL_GetModState();
1528 KeyMod GetKeyModStateFromEvents()
1530 /* always use key modifier state as tracked from key events (this is needed
1531 if the modifier key event was injected into the event queue, but the key
1532 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1533 query the keys as held pressed on the keyboard) -- this case is currently
1534 only used to filter out clipboard insert events from "True X-Mouse" tool */
1536 return HandleKeyModState(KSYM_UNDEFINED, 0);
1539 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1541 if (event->type != EVENT_CLIENTMESSAGE)
1544 return TRUE; /* the only possible message here is SDL_QUIT */
1548 /* ========================================================================= */
1549 /* joystick functions */
1550 /* ========================================================================= */
1552 void InitJoysticks()
1556 #if defined(NO_JOYSTICK)
1557 return; /* joysticks generally deactivated by compile-time directive */
1560 /* always start with reliable default values */
1561 joystick.status = JOYSTICK_NOT_AVAILABLE;
1562 for (i = 0; i < MAX_PLAYERS; i++)
1563 joystick.fd[i] = -1; /* joystick device closed */
1568 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1570 return SDLReadJoystick(nr, x, y, b1, b2);