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 OverlayInfo overlay;
37 struct ArtworkInfo artwork;
38 struct JoystickInfo joystick;
39 struct SetupInfo setup;
41 LevelDirTree *leveldir_first_all = NULL;
42 LevelDirTree *leveldir_first = NULL;
43 LevelDirTree *leveldir_current = NULL;
46 struct LevelStats level_stats[MAX_LEVELS];
48 DrawWindow *window = NULL;
49 DrawBuffer *backbuffer = NULL;
50 DrawBuffer *drawto = NULL;
52 int button_status = MB_NOT_PRESSED;
53 boolean motion_status = FALSE;
54 int wheel_steps = DEFAULT_WHEEL_STEPS;
55 #if defined(TARGET_SDL2)
56 boolean keyrepeat_status = TRUE;
59 int redraw_mask = REDRAW_NONE;
64 /* ========================================================================= */
65 /* init/close functions */
66 /* ========================================================================= */
68 void InitProgramInfo(char *argv0, char *config_filename, char *userdata_subdir,
69 char *program_title, char *icon_title,
70 char *icon_filename, char *cookie_prefix,
71 char *program_version_string, int program_version)
73 program.command_basepath = getBasePath(argv0);
74 program.command_basename = getBaseName(argv0);
76 program.config_filename = config_filename;
78 program.userdata_subdir = userdata_subdir;
79 program.userdata_path = getUserGameDataDir();
81 program.program_title = program_title;
82 program.window_title = "(undefined)";
83 program.icon_title = icon_title;
85 program.icon_filename = icon_filename;
87 program.cookie_prefix = cookie_prefix;
89 program.version_major = VERSION_MAJOR(program_version);
90 program.version_minor = VERSION_MINOR(program_version);
91 program.version_patch = VERSION_PATCH(program_version);
92 program.version_build = VERSION_BUILD(program_version);
93 program.version_ident = program_version;
95 program.version_string = program_version_string;
97 program.log_filename[LOG_OUT_ID] = getLogFilename(LOG_OUT_BASENAME);
98 program.log_filename[LOG_ERR_ID] = getLogFilename(LOG_ERR_BASENAME);
99 program.log_file[LOG_OUT_ID] = program.log_file_default[LOG_OUT_ID] = stdout;
100 program.log_file[LOG_ERR_ID] = program.log_file_default[LOG_ERR_ID] = stderr;
102 program.headless = FALSE;
105 void InitScoresInfo()
107 char *global_scores_dir = getPath2(getCommonDataDir(), SCORES_DIRECTORY);
109 program.global_scores = directoryExists(global_scores_dir);
110 program.many_scores_per_name = !program.global_scores;
114 if (program.global_scores)
116 Error(ERR_DEBUG, "Using global, multi-user scores directory '%s'.",
118 Error(ERR_DEBUG, "Remove to enable single-user scores directory.");
119 Error(ERR_DEBUG, "(This enables multipe score entries per user.)");
123 Error(ERR_DEBUG, "Using private, single-user scores directory.");
127 free(global_scores_dir);
130 void SetWindowTitle()
132 program.window_title = program.window_title_function();
137 void InitWindowTitleFunction(char *(*window_title_function)(void))
139 program.window_title_function = window_title_function;
142 void InitExitMessageFunction(void (*exit_message_function)(char *, va_list))
144 program.exit_message_function = exit_message_function;
147 void InitExitFunction(void (*exit_function)(int))
149 program.exit_function = exit_function;
151 /* set signal handlers to custom exit function */
152 // signal(SIGINT, exit_function);
153 signal(SIGTERM, exit_function);
155 /* set exit function to automatically cleanup SDL stuff after exit() */
159 void InitPlatformDependentStuff(void)
161 // this is initialized in GetOptions(), but may already be used before
162 options.verbose = TRUE;
166 #if defined(TARGET_SDL2)
167 int sdl_init_flags = SDL_INIT_EVENTS | SDL_INIT_NOPARACHUTE;
169 int sdl_init_flags = SDL_INIT_EVENTTHREAD | SDL_INIT_NOPARACHUTE;
172 if (SDL_Init(sdl_init_flags) < 0)
173 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
178 void ClosePlatformDependentStuff(void)
183 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
184 int real_sx, int real_sy,
185 int full_sxsize, int full_sysize,
186 Bitmap *field_save_buffer)
192 gfx.real_sx = real_sx;
193 gfx.real_sy = real_sy;
194 gfx.full_sxsize = full_sxsize;
195 gfx.full_sysize = full_sysize;
197 gfx.field_save_buffer = field_save_buffer;
199 SetDrawDeactivationMask(REDRAW_NONE); /* do not deactivate drawing */
200 SetDrawBackgroundMask(REDRAW_NONE); /* deactivate masked drawing */
203 void InitGfxTileSizeInfo(int game_tile_size, int standard_tile_size)
205 gfx.game_tile_size = game_tile_size;
206 gfx.standard_tile_size = standard_tile_size;
209 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
217 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
225 void InitGfxDoor3Info(int ex, int ey, int exsize, int eysize)
233 void InitGfxWindowInfo(int win_xsize, int win_ysize)
235 if (win_xsize != gfx.win_xsize || win_ysize != gfx.win_ysize)
237 ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize);
239 #if defined(TARGET_SDL2)
240 ReCreateBitmap(&gfx.final_screen_bitmap, win_xsize, win_ysize);
243 ReCreateBitmap(&gfx.fade_bitmap_backup, win_xsize, win_ysize);
244 ReCreateBitmap(&gfx.fade_bitmap_source, win_xsize, win_ysize);
245 ReCreateBitmap(&gfx.fade_bitmap_target, win_xsize, win_ysize);
246 ReCreateBitmap(&gfx.fade_bitmap_black, win_xsize, win_ysize);
248 ClearRectangle(gfx.fade_bitmap_black, 0, 0, win_xsize, win_ysize);
251 gfx.win_xsize = win_xsize;
252 gfx.win_ysize = win_ysize;
254 gfx.background_bitmap_mask = REDRAW_NONE;
257 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
259 /* currently only used by MSDOS code to alloc VRAM buffer, if available */
260 /* 2009-03-24: also (temporarily?) used for overlapping blit workaround */
261 gfx.scrollbuffer_width = scrollbuffer_width;
262 gfx.scrollbuffer_height = scrollbuffer_height;
265 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
267 gfx.clipping_enabled = enabled;
270 gfx.clip_width = width;
271 gfx.clip_height = height;
274 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
276 gfx.draw_busy_anim_function = draw_busy_anim_function;
279 void InitGfxDrawGlobalAnimFunction(void (*draw_global_anim_function)(int, int))
281 gfx.draw_global_anim_function = draw_global_anim_function;
284 void InitGfxDrawGlobalBorderFunction(void (*draw_global_border_function)(int))
286 gfx.draw_global_border_function = draw_global_border_function;
289 void InitGfxCustomArtworkInfo()
291 gfx.override_level_graphics = FALSE;
292 gfx.override_level_sounds = FALSE;
293 gfx.override_level_music = FALSE;
295 gfx.draw_init_text = TRUE;
298 void InitGfxOtherSettings()
300 gfx.cursor_mode = CURSOR_DEFAULT;
303 void InitOverlayInfo()
305 overlay.enabled = FALSE;
306 overlay.active = FALSE;
308 #if defined(PLATFORM_ANDROID)
309 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
310 overlay.enabled = TRUE;
314 void SetOverlayEnabled(boolean enabled)
316 overlay.enabled = enabled;
319 void SetOverlayActive(boolean active)
321 overlay.active = active;
324 boolean GetOverlayActive()
326 return overlay.active;
329 void SetDrawDeactivationMask(int draw_deactivation_mask)
331 gfx.draw_deactivation_mask = draw_deactivation_mask;
334 int GetDrawDeactivationMask()
336 return gfx.draw_deactivation_mask;
339 void SetDrawBackgroundMask(int draw_background_mask)
341 gfx.draw_background_mask = draw_background_mask;
344 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
346 if (background_bitmap_tile != NULL)
347 gfx.background_bitmap_mask |= mask;
349 gfx.background_bitmap_mask &= ~mask;
351 if (background_bitmap_tile == NULL) /* empty background requested */
354 if (mask == REDRAW_ALL)
355 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
356 0, 0, video.width, video.height);
357 else if (mask == REDRAW_FIELD)
358 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
359 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
360 else if (mask == REDRAW_DOOR_1)
361 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
362 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
365 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
367 /* remove every mask before setting mask for window */
368 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
369 SetBackgroundBitmap(NULL, 0xffff); /* !!! FIX THIS !!! */
370 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
373 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
375 /* remove window area mask before setting mask for main area */
376 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
377 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
378 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
381 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
383 /* remove window area mask before setting mask for door area */
384 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
385 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
386 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
390 /* ========================================================================= */
391 /* video functions */
392 /* ========================================================================= */
394 inline static int GetRealDepth(int depth)
396 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
399 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
400 int width, int height, Pixel color)
402 SDLFillRectangle(bitmap, x, y, width, height, color);
404 if (bitmap == backbuffer)
405 SetRedrawMaskFromArea(x, y, width, height);
408 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
409 int src_x, int src_y, int width, int height,
410 int dst_x, int dst_y, int mask_mode)
412 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
413 dst_x, dst_y, mask_mode);
415 if (dst_bitmap == backbuffer)
416 SetRedrawMaskFromArea(dst_x, dst_y, width, height);
419 void LimitScreenUpdates(boolean enable)
421 SDLLimitScreenUpdates(enable);
424 void InitVideoDisplay(void)
426 if (program.headless)
429 SDLInitVideoDisplay();
430 #if defined(TARGET_SDL2)
435 void CloseVideoDisplay(void)
437 KeyboardAutoRepeatOn();
439 SDL_QuitSubSystem(SDL_INIT_VIDEO);
442 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
445 video.height = height;
446 video.depth = GetRealDepth(depth);
448 video.screen_width = width;
449 video.screen_height = height;
450 video.screen_xoffset = 0;
451 video.screen_yoffset = 0;
453 video.fullscreen_available = FULLSCREEN_STATUS;
454 video.fullscreen_enabled = FALSE;
456 video.window_scaling_available = WINDOW_SCALING_STATUS;
458 video.frame_delay = 0;
459 video.frame_delay_value = GAME_FRAME_DELAY;
461 video.shifted_up = FALSE;
462 video.shifted_up_pos = 0;
463 video.shifted_up_pos_last = 0;
464 video.shifted_up_delay = 0;
465 video.shifted_up_delay_value = ONE_SECOND_DELAY / 4;
467 SDLInitVideoBuffer(fullscreen);
469 video.initialized = TRUE;
474 inline static void FreeBitmapPointers(Bitmap *bitmap)
479 SDLFreeBitmapPointers(bitmap);
481 checked_free(bitmap->source_filename);
482 bitmap->source_filename = NULL;
485 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
488 if (src_bitmap == NULL || dst_bitmap == NULL)
491 FreeBitmapPointers(dst_bitmap);
493 *dst_bitmap = *src_bitmap;
496 void FreeBitmap(Bitmap *bitmap)
501 FreeBitmapPointers(bitmap);
506 Bitmap *CreateBitmapStruct(void)
508 return checked_calloc(sizeof(Bitmap));
511 Bitmap *CreateBitmap(int width, int height, int depth)
513 Bitmap *new_bitmap = CreateBitmapStruct();
514 int real_width = MAX(1, width); /* prevent zero bitmap width */
515 int real_height = MAX(1, height); /* prevent zero bitmap height */
516 int real_depth = GetRealDepth(depth);
518 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
520 new_bitmap->width = real_width;
521 new_bitmap->height = real_height;
526 void ReCreateBitmap(Bitmap **bitmap, int width, int height)
530 /* if new bitmap size fits into old one, no need to re-create it */
531 if (width <= (*bitmap)->width &&
532 height <= (*bitmap)->height)
535 /* else adjust size so that old and new bitmap size fit into it */
536 width = MAX(width, (*bitmap)->width);
537 height = MAX(height, (*bitmap)->height);
540 Bitmap *new_bitmap = CreateBitmap(width, height, DEFAULT_DEPTH);
544 *bitmap = new_bitmap;
548 TransferBitmapPointers(new_bitmap, *bitmap);
553 void CloseWindow(DrawWindow *window)
557 void SetRedrawMaskFromArea(int x, int y, int width, int height)
561 int x2 = x + width - 1;
562 int y2 = y + height - 1;
564 if (width == 0 || height == 0)
567 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
568 redraw_mask |= REDRAW_FIELD;
569 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
570 redraw_mask |= REDRAW_DOOR_1;
571 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
572 redraw_mask |= REDRAW_DOOR_2;
573 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
574 redraw_mask |= REDRAW_DOOR_3;
576 redraw_mask = REDRAW_ALL;
579 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
582 if (draw_mask == REDRAW_NONE)
585 if (draw_mask & REDRAW_ALL)
588 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
591 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
594 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
597 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
603 boolean DrawingDeactivated(int x, int y, int width, int height)
605 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
608 boolean DrawingOnBackground(int x, int y)
610 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
611 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
614 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
615 int *width, int *height, boolean is_dest)
617 int clip_x, clip_y, clip_width, clip_height;
619 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
621 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
622 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
623 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
624 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
630 clip_width = bitmap->width;
631 clip_height = bitmap->height;
634 /* skip if rectangle completely outside bitmap */
636 if (*x + *width <= clip_x ||
637 *y + *height <= clip_y ||
638 *x >= clip_x + clip_width ||
639 *y >= clip_y + clip_height)
642 /* clip if rectangle overlaps bitmap */
646 *width -= clip_x - *x;
649 else if (*x + *width > clip_x + clip_width)
651 *width = clip_x + clip_width - *x;
656 *height -= clip_y - *y;
659 else if (*y + *height > clip_y + clip_height)
661 *height = clip_y + clip_height - *y;
667 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
668 int src_x, int src_y, int width, int height,
669 int dst_x, int dst_y)
671 int dst_x_unclipped = dst_x;
672 int dst_y_unclipped = dst_y;
674 if (program.headless)
677 if (src_bitmap == NULL || dst_bitmap == NULL)
680 if (DrawingDeactivated(dst_x, dst_y, width, height))
683 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
684 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
687 /* source x/y might need adjustment if destination x/y was clipped top/left */
688 src_x += dst_x - dst_x_unclipped;
689 src_y += dst_y - dst_y_unclipped;
691 #if defined(TARGET_SDL2)
692 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
693 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
694 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
695 but is already fixed in SVN and should therefore finally be fixed with
696 the next official SDL release, which is probably version 1.2.14.) */
697 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
699 if (src_bitmap == dst_bitmap)
701 /* needed when blitting directly to same bitmap -- should not be needed with
702 recent SDL libraries, but apparently does not work in 1.2.11 directly */
704 static Bitmap *tmp_bitmap = NULL;
705 static int tmp_bitmap_xsize = 0;
706 static int tmp_bitmap_ysize = 0;
708 /* start with largest static bitmaps for initial bitmap size ... */
709 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
711 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
712 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
715 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
716 if (src_bitmap->width > tmp_bitmap_xsize ||
717 src_bitmap->height > tmp_bitmap_ysize)
719 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
720 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
722 FreeBitmap(tmp_bitmap);
727 if (tmp_bitmap == NULL)
728 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
731 sysCopyArea(src_bitmap, tmp_bitmap,
732 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
733 sysCopyArea(tmp_bitmap, dst_bitmap,
734 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
740 sysCopyArea(src_bitmap, dst_bitmap,
741 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
744 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
745 int src_x, int src_y, int src_width, int src_height,
746 int dst_x, int dst_y, int dst_width, int dst_height)
748 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
749 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
750 int dst_xsize = dst_width;
751 int dst_ysize = dst_height;
752 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
753 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
756 for (y = 0; y < src_ysteps; y++)
758 for (x = 0; x < src_xsteps; x++)
760 int draw_x = dst_x + x * src_xsize;
761 int draw_y = dst_y + y * src_ysize;
762 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
763 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
765 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
771 void FadeRectangle(int x, int y, int width, int height,
772 int fade_mode, int fade_delay, int post_delay,
773 void (*draw_border_function)(void))
775 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
776 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
779 SDLFadeRectangle(x, y, width, height,
780 fade_mode, fade_delay, post_delay, draw_border_function);
783 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
786 if (DrawingDeactivated(x, y, width, height))
789 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
792 sysFillRectangle(bitmap, x, y, width, height, color);
795 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
797 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
800 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
801 int width, int height)
803 if (DrawingOnBackground(x, y))
804 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
806 ClearRectangle(bitmap, x, y, width, height);
809 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
810 int src_x, int src_y, int width, int height,
811 int dst_x, int dst_y)
813 if (DrawingDeactivated(dst_x, dst_y, width, height))
816 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
817 dst_x, dst_y, BLIT_MASKED);
820 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
821 int src_x, int src_y, int width, int height,
822 int dst_x, int dst_y)
824 if (DrawingOnBackground(dst_x, dst_y))
826 /* draw background */
827 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
830 /* draw foreground */
831 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
835 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
839 void BlitTexture(Bitmap *bitmap,
840 int src_x, int src_y, int width, int height,
841 int dst_x, int dst_y)
846 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
850 void BlitTextureMasked(Bitmap *bitmap,
851 int src_x, int src_y, int width, int height,
852 int dst_x, int dst_y)
857 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
861 void BlitToScreen(Bitmap *bitmap,
862 int src_x, int src_y, int width, int height,
863 int dst_x, int dst_y)
868 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
869 BlitBitmap(bitmap, gfx.final_screen_bitmap, src_x, src_y,
870 width, height, dst_x, dst_y);
872 BlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y);
875 void BlitToScreenMasked(Bitmap *bitmap,
876 int src_x, int src_y, int width, int height,
877 int dst_x, int dst_y)
882 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
883 BlitBitmapMasked(bitmap, gfx.final_screen_bitmap, src_x, src_y,
884 width, height, dst_x, dst_y);
886 BlitTextureMasked(bitmap, src_x, src_y, width, height, dst_x, dst_y);
889 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
892 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
895 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
898 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
901 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
902 int to_x, int to_y, Pixel pixel, int line_width)
906 for (x = 0; x < line_width; x++)
908 for (y = 0; y < line_width; y++)
910 int dx = x - line_width / 2;
911 int dy = y - line_width / 2;
913 if ((x == 0 && y == 0) ||
914 (x == 0 && y == line_width - 1) ||
915 (x == line_width - 1 && y == 0) ||
916 (x == line_width - 1 && y == line_width - 1))
920 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
925 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
930 for (i = 0; i < num_points - 1; i++)
931 DrawLine(bitmap, points[i].x, points[i].y,
932 points[i + 1].x, points[i + 1].y, pixel, line_width);
935 SDLDrawLines(bitmap->surface, points, num_points, pixel);
939 Pixel GetPixel(Bitmap *bitmap, int x, int y)
941 if (x < 0 || x >= bitmap->width ||
942 y < 0 || y >= bitmap->height)
945 return SDLGetPixel(bitmap, x, y);
948 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
949 unsigned int color_g, unsigned int color_b)
951 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
954 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
956 unsigned int color_r = (color >> 16) & 0xff;
957 unsigned int color_g = (color >> 8) & 0xff;
958 unsigned int color_b = (color >> 0) & 0xff;
960 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
963 void KeyboardAutoRepeatOn(void)
965 #if defined(TARGET_SDL2)
966 keyrepeat_status = TRUE;
968 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
969 SDL_DEFAULT_REPEAT_INTERVAL / 2);
970 SDL_EnableUNICODE(1);
974 void KeyboardAutoRepeatOff(void)
976 #if defined(TARGET_SDL2)
977 keyrepeat_status = FALSE;
979 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
980 SDL_EnableUNICODE(0);
984 boolean SetVideoMode(boolean fullscreen)
986 return SDLSetVideoMode(fullscreen);
989 void SetVideoFrameDelay(unsigned int frame_delay_value)
991 video.frame_delay_value = frame_delay_value;
994 unsigned int GetVideoFrameDelay()
996 return video.frame_delay_value;
999 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
1001 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
1002 (!fullscreen && video.fullscreen_enabled))
1003 fullscreen = SetVideoMode(fullscreen);
1008 Bitmap *LoadImage(char *filename)
1012 new_bitmap = SDLLoadImage(filename);
1015 new_bitmap->source_filename = getStringCopy(filename);
1020 Bitmap *LoadCustomImage(char *basename)
1022 char *filename = getCustomImageFilename(basename);
1025 if (filename == NULL)
1026 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
1028 if ((new_bitmap = LoadImage(filename)) == NULL)
1029 Error(ERR_EXIT, "LoadImage('%s') failed: %s", basename, GetError());
1034 void ReloadCustomImage(Bitmap *bitmap, char *basename)
1036 char *filename = getCustomImageFilename(basename);
1039 if (filename == NULL) /* (should never happen) */
1041 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
1045 if (strEqual(filename, bitmap->source_filename))
1047 /* The old and new image are the same (have the same filename and path).
1048 This usually means that this image does not exist in this graphic set
1049 and a fallback to the existing image is done. */
1054 if ((new_bitmap = LoadImage(filename)) == NULL)
1056 Error(ERR_WARN, "LoadImage('%s') failed: %s", basename, GetError());
1060 if (bitmap->width != new_bitmap->width ||
1061 bitmap->height != new_bitmap->height)
1063 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
1065 FreeBitmap(new_bitmap);
1069 TransferBitmapPointers(new_bitmap, bitmap);
1073 static Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
1075 return SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
1078 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
1080 if (bitmaps[IMG_BITMAP_CUSTOM])
1082 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
1084 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
1087 if (gfx.game_tile_size == gfx.standard_tile_size)
1089 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1094 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
1095 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
1096 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
1098 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
1100 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
1101 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
1104 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1105 int tile_size, boolean create_small_bitmaps)
1107 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1108 Bitmap *tmp_bitmap_final = NULL;
1109 Bitmap *tmp_bitmap_0 = NULL;
1110 Bitmap *tmp_bitmap_1 = NULL;
1111 Bitmap *tmp_bitmap_2 = NULL;
1112 Bitmap *tmp_bitmap_4 = NULL;
1113 Bitmap *tmp_bitmap_8 = NULL;
1114 Bitmap *tmp_bitmap_16 = NULL;
1115 Bitmap *tmp_bitmap_32 = NULL;
1116 int width_final, height_final;
1117 int width_0, height_0;
1118 int width_1, height_1;
1119 int width_2, height_2;
1120 int width_4, height_4;
1121 int width_8, height_8;
1122 int width_16, height_16;
1123 int width_32, height_32;
1124 int old_width, old_height;
1127 print_timestamp_init("CreateScaledBitmaps");
1129 old_width = old_bitmap->width;
1130 old_height = old_bitmap->height;
1132 /* calculate new image dimensions for final image size */
1133 width_final = old_width * zoom_factor;
1134 height_final = old_height * zoom_factor;
1136 /* get image with final size (this might require scaling up) */
1137 /* ("final" size may result in non-standard tile size image) */
1138 if (zoom_factor != 1)
1139 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1141 tmp_bitmap_final = old_bitmap;
1143 UPDATE_BUSY_STATE();
1145 width_0 = width_1 = width_final;
1146 height_0 = height_1 = height_final;
1148 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1150 if (create_small_bitmaps)
1152 /* check if we have a non-gameplay tile size image */
1153 if (tile_size != gfx.game_tile_size)
1155 /* get image with gameplay tile size */
1156 width_0 = width_final * gfx.game_tile_size / tile_size;
1157 height_0 = height_final * gfx.game_tile_size / tile_size;
1159 if (width_0 == old_width)
1160 tmp_bitmap_0 = old_bitmap;
1161 else if (width_0 == width_final)
1162 tmp_bitmap_0 = tmp_bitmap_final;
1164 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1166 UPDATE_BUSY_STATE();
1169 /* check if we have a non-standard tile size image */
1170 if (tile_size != gfx.standard_tile_size)
1172 /* get image with standard tile size */
1173 width_1 = width_final * gfx.standard_tile_size / tile_size;
1174 height_1 = height_final * gfx.standard_tile_size / tile_size;
1176 if (width_1 == old_width)
1177 tmp_bitmap_1 = old_bitmap;
1178 else if (width_1 == width_final)
1179 tmp_bitmap_1 = tmp_bitmap_final;
1180 else if (width_1 == width_0)
1181 tmp_bitmap_1 = tmp_bitmap_0;
1183 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1185 UPDATE_BUSY_STATE();
1188 /* calculate new image dimensions for small images */
1189 width_2 = width_1 / 2;
1190 height_2 = height_1 / 2;
1191 width_4 = width_1 / 4;
1192 height_4 = height_1 / 4;
1193 width_8 = width_1 / 8;
1194 height_8 = height_1 / 8;
1195 width_16 = width_1 / 16;
1196 height_16 = height_1 / 16;
1197 width_32 = width_1 / 32;
1198 height_32 = height_1 / 32;
1200 /* get image with 1/2 of normal size (for use in the level editor) */
1201 if (width_2 == old_width)
1202 tmp_bitmap_2 = old_bitmap;
1204 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1206 UPDATE_BUSY_STATE();
1208 /* get image with 1/4 of normal size (for use in the level editor) */
1209 if (width_4 == old_width)
1210 tmp_bitmap_4 = old_bitmap;
1212 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1214 UPDATE_BUSY_STATE();
1216 /* get image with 1/8 of normal size (for use on the preview screen) */
1217 if (width_8 == old_width)
1218 tmp_bitmap_8 = old_bitmap;
1220 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1222 UPDATE_BUSY_STATE();
1224 /* get image with 1/16 of normal size (for use on the preview screen) */
1225 if (width_16 == old_width)
1226 tmp_bitmap_16 = old_bitmap;
1228 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1230 UPDATE_BUSY_STATE();
1232 /* get image with 1/32 of normal size (for use on the preview screen) */
1233 if (width_32 == old_width)
1234 tmp_bitmap_32 = old_bitmap;
1236 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1238 UPDATE_BUSY_STATE();
1240 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1241 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1242 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1243 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1244 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1245 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1247 if (width_0 != width_1)
1248 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1250 if (bitmaps[IMG_BITMAP_CUSTOM])
1251 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1253 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1255 boolean free_old_bitmap = TRUE;
1257 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1258 if (bitmaps[i] == old_bitmap)
1259 free_old_bitmap = FALSE;
1261 if (free_old_bitmap)
1262 FreeBitmap(old_bitmap);
1266 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1269 UPDATE_BUSY_STATE();
1271 print_timestamp_done("CreateScaledBitmaps");
1274 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1277 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1280 void CreateBitmapTextures(Bitmap **bitmaps)
1282 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1285 void FreeBitmapTextures(Bitmap **bitmaps)
1287 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1290 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1292 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1296 /* ------------------------------------------------------------------------- */
1297 /* mouse pointer functions */
1298 /* ------------------------------------------------------------------------- */
1300 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1302 /* XPM image definitions */
1303 static const char *cursor_image_none[] =
1305 /* width height num_colors chars_per_pixel */
1335 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1336 static const char *cursor_image_dot[] =
1338 /* width height num_colors chars_per_pixel */
1367 static const char **cursor_image_playfield = cursor_image_dot;
1369 /* some people complained about a "white dot" on the screen and thought it
1370 was a graphical error... OK, let's just remove the whole pointer :-) */
1371 static const char **cursor_image_playfield = cursor_image_none;
1374 static const int cursor_bit_order = BIT_ORDER_MSB;
1376 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1378 struct MouseCursorInfo *cursor;
1379 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1380 int header_lines = 4;
1383 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1385 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1388 for (y = 0; y < cursor->width; y++)
1390 for (x = 0; x < cursor->height; x++)
1393 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1398 cursor->data[i] = cursor->mask[i] = 0;
1401 switch (image[header_lines + y][x])
1404 cursor->data[i] |= bit_mask;
1405 cursor->mask[i] |= bit_mask;
1409 cursor->mask[i] |= bit_mask;
1418 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1423 void SetMouseCursor(int mode)
1425 static struct MouseCursorInfo *cursor_none = NULL;
1426 static struct MouseCursorInfo *cursor_playfield = NULL;
1427 struct MouseCursorInfo *cursor_new;
1429 if (cursor_none == NULL)
1430 cursor_none = get_cursor_from_image(cursor_image_none);
1432 if (cursor_playfield == NULL)
1433 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1435 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1436 mode == CURSOR_NONE ? cursor_none :
1437 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1439 SDLSetMouseCursor(cursor_new);
1441 gfx.cursor_mode = mode;
1445 /* ========================================================================= */
1446 /* audio functions */
1447 /* ========================================================================= */
1449 void OpenAudio(void)
1451 /* always start with reliable default values */
1452 audio.sound_available = FALSE;
1453 audio.music_available = FALSE;
1454 audio.loops_available = FALSE;
1456 audio.sound_enabled = FALSE;
1457 audio.sound_deactivated = FALSE;
1459 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1460 audio.mixer_pid = 0;
1461 audio.device_name = NULL;
1462 audio.device_fd = -1;
1464 audio.num_channels = 0;
1465 audio.music_channel = 0;
1466 audio.first_sound_channel = 0;
1471 void CloseAudio(void)
1475 audio.sound_enabled = FALSE;
1478 void SetAudioMode(boolean enabled)
1480 if (!audio.sound_available)
1483 audio.sound_enabled = enabled;
1487 /* ========================================================================= */
1488 /* event functions */
1489 /* ========================================================================= */
1491 boolean PendingEvent(void)
1493 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1496 void NextEvent(Event *event)
1498 SDLNextEvent(event);
1501 void PeekEvent(Event *event)
1503 #if defined(TARGET_SDL2)
1504 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1506 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1510 void CheckQuitEvent(void)
1512 if (SDL_QuitRequested())
1513 program.exit_function(0);
1516 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1518 #if defined(TARGET_SDL2)
1519 /* key up/down events in SDL2 do not return text characters anymore */
1520 return event->keysym.sym;
1523 #if ENABLE_UNUSED_CODE
1524 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1525 (int)event->keysym.unicode,
1526 (int)event->keysym.sym,
1527 (int)SDL_GetModState());
1530 if (with_modifiers &&
1531 event->keysym.unicode > 0x0000 &&
1532 event->keysym.unicode < 0x2000)
1533 return event->keysym.unicode;
1535 return event->keysym.sym;
1540 KeyMod HandleKeyModState(Key key, int key_status)
1542 static KeyMod current_modifiers = KMOD_None;
1544 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1546 KeyMod new_modifier = KMOD_None;
1551 new_modifier = KMOD_Shift_L;
1554 new_modifier = KMOD_Shift_R;
1556 case KSYM_Control_L:
1557 new_modifier = KMOD_Control_L;
1559 case KSYM_Control_R:
1560 new_modifier = KMOD_Control_R;
1563 new_modifier = KMOD_Meta_L;
1566 new_modifier = KMOD_Meta_R;
1569 new_modifier = KMOD_Alt_L;
1572 new_modifier = KMOD_Alt_R;
1578 if (key_status == KEY_PRESSED)
1579 current_modifiers |= new_modifier;
1581 current_modifiers &= ~new_modifier;
1584 return current_modifiers;
1587 KeyMod GetKeyModState()
1589 return (KeyMod)SDL_GetModState();
1592 KeyMod GetKeyModStateFromEvents()
1594 /* always use key modifier state as tracked from key events (this is needed
1595 if the modifier key event was injected into the event queue, but the key
1596 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1597 query the keys as held pressed on the keyboard) -- this case is currently
1598 only used to filter out clipboard insert events from "True X-Mouse" tool */
1600 return HandleKeyModState(KSYM_UNDEFINED, 0);
1603 void StartTextInput(int x, int y, int width, int height)
1605 #if defined(TARGET_SDL2)
1606 #if defined(HAS_SCREEN_KEYBOARD)
1607 SDL_StartTextInput();
1609 if (y + height > SCREEN_KEYBOARD_POS(video.height))
1611 video.shifted_up_pos = y + height - SCREEN_KEYBOARD_POS(video.height);
1612 video.shifted_up_delay = SDL_GetTicks();
1613 video.shifted_up = TRUE;
1619 void StopTextInput()
1621 #if defined(TARGET_SDL2)
1622 #if defined(HAS_SCREEN_KEYBOARD)
1623 SDL_StopTextInput();
1625 if (video.shifted_up)
1627 video.shifted_up_pos = 0;
1628 video.shifted_up_delay = SDL_GetTicks();
1629 video.shifted_up = FALSE;
1635 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1637 if (event->type != EVENT_CLIENTMESSAGE)
1640 return TRUE; /* the only possible message here is SDL_QUIT */
1644 /* ========================================================================= */
1645 /* joystick functions */
1646 /* ========================================================================= */
1648 void InitJoysticks()
1652 #if defined(NO_JOYSTICK)
1653 return; /* joysticks generally deactivated by compile-time directive */
1656 /* always start with reliable default values */
1657 joystick.status = JOYSTICK_NOT_AVAILABLE;
1658 for (i = 0; i < MAX_PLAYERS; i++)
1659 joystick.nr[i] = -1; /* no joystick configured */
1664 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1666 return SDLReadJoystick(nr, x, y, b1, b2);
1669 boolean CheckJoystickOpened(int nr)
1671 return SDLCheckJoystickOpened(nr);
1674 void ClearJoystickState()
1676 SDLClearJoystickState();