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 DrawingDeactivatedField()
605 if (program.headless)
608 if (gfx.draw_deactivation_mask & REDRAW_FIELD)
614 boolean DrawingDeactivated(int x, int y, int width, int height)
616 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
619 boolean DrawingOnBackground(int x, int y)
621 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
622 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
625 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
626 int *width, int *height, boolean is_dest)
628 int clip_x, clip_y, clip_width, clip_height;
630 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
632 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
633 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
634 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
635 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
641 clip_width = bitmap->width;
642 clip_height = bitmap->height;
645 /* skip if rectangle completely outside bitmap */
647 if (*x + *width <= clip_x ||
648 *y + *height <= clip_y ||
649 *x >= clip_x + clip_width ||
650 *y >= clip_y + clip_height)
653 /* clip if rectangle overlaps bitmap */
657 *width -= clip_x - *x;
660 else if (*x + *width > clip_x + clip_width)
662 *width = clip_x + clip_width - *x;
667 *height -= clip_y - *y;
670 else if (*y + *height > clip_y + clip_height)
672 *height = clip_y + clip_height - *y;
678 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
679 int src_x, int src_y, int width, int height,
680 int dst_x, int dst_y)
682 int dst_x_unclipped = dst_x;
683 int dst_y_unclipped = dst_y;
685 if (program.headless)
688 if (src_bitmap == NULL || dst_bitmap == NULL)
691 if (DrawingDeactivated(dst_x, dst_y, width, height))
694 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
695 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
698 /* source x/y might need adjustment if destination x/y was clipped top/left */
699 src_x += dst_x - dst_x_unclipped;
700 src_y += dst_y - dst_y_unclipped;
702 #if defined(TARGET_SDL2)
703 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
704 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
705 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
706 but is already fixed in SVN and should therefore finally be fixed with
707 the next official SDL release, which is probably version 1.2.14.) */
708 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
710 if (src_bitmap == dst_bitmap)
712 /* needed when blitting directly to same bitmap -- should not be needed with
713 recent SDL libraries, but apparently does not work in 1.2.11 directly */
715 static Bitmap *tmp_bitmap = NULL;
716 static int tmp_bitmap_xsize = 0;
717 static int tmp_bitmap_ysize = 0;
719 /* start with largest static bitmaps for initial bitmap size ... */
720 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
722 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
723 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
726 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
727 if (src_bitmap->width > tmp_bitmap_xsize ||
728 src_bitmap->height > tmp_bitmap_ysize)
730 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
731 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
733 FreeBitmap(tmp_bitmap);
738 if (tmp_bitmap == NULL)
739 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
742 sysCopyArea(src_bitmap, tmp_bitmap,
743 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
744 sysCopyArea(tmp_bitmap, dst_bitmap,
745 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
751 sysCopyArea(src_bitmap, dst_bitmap,
752 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
755 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
756 int src_x, int src_y, int src_width, int src_height,
757 int dst_x, int dst_y, int dst_width, int dst_height)
759 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
760 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
761 int dst_xsize = dst_width;
762 int dst_ysize = dst_height;
763 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
764 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
767 for (y = 0; y < src_ysteps; y++)
769 for (x = 0; x < src_xsteps; x++)
771 int draw_x = dst_x + x * src_xsize;
772 int draw_y = dst_y + y * src_ysize;
773 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
774 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
776 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
782 void FadeRectangle(int x, int y, int width, int height,
783 int fade_mode, int fade_delay, int post_delay,
784 void (*draw_border_function)(void))
786 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
787 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
790 SDLFadeRectangle(x, y, width, height,
791 fade_mode, fade_delay, post_delay, draw_border_function);
794 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
797 if (DrawingDeactivated(x, y, width, height))
800 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
803 sysFillRectangle(bitmap, x, y, width, height, color);
806 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
808 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
811 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
812 int width, int height)
814 if (DrawingOnBackground(x, y))
815 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
817 ClearRectangle(bitmap, x, y, width, height);
820 void BlitBitmapMasked(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 (DrawingDeactivated(dst_x, dst_y, width, height))
827 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
828 dst_x, dst_y, BLIT_MASKED);
831 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
832 int src_x, int src_y, int width, int height,
833 int dst_x, int dst_y)
835 if (DrawingOnBackground(dst_x, dst_y))
837 /* draw background */
838 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
841 /* draw foreground */
842 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
846 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
850 void BlitTexture(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 BlitTextureMasked(Bitmap *bitmap,
862 int src_x, int src_y, int width, int height,
863 int dst_x, int dst_y)
868 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
872 void BlitToScreen(Bitmap *bitmap,
873 int src_x, int src_y, int width, int height,
874 int dst_x, int dst_y)
879 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
880 BlitBitmap(bitmap, gfx.final_screen_bitmap, src_x, src_y,
881 width, height, dst_x, dst_y);
883 BlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y);
886 void BlitToScreenMasked(Bitmap *bitmap,
887 int src_x, int src_y, int width, int height,
888 int dst_x, int dst_y)
893 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
894 BlitBitmapMasked(bitmap, gfx.final_screen_bitmap, src_x, src_y,
895 width, height, dst_x, dst_y);
897 BlitTextureMasked(bitmap, src_x, src_y, width, height, dst_x, dst_y);
900 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
903 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
906 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
909 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
912 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
913 int to_x, int to_y, Pixel pixel, int line_width)
917 for (x = 0; x < line_width; x++)
919 for (y = 0; y < line_width; y++)
921 int dx = x - line_width / 2;
922 int dy = y - line_width / 2;
924 if ((x == 0 && y == 0) ||
925 (x == 0 && y == line_width - 1) ||
926 (x == line_width - 1 && y == 0) ||
927 (x == line_width - 1 && y == line_width - 1))
931 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
936 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
941 for (i = 0; i < num_points - 1; i++)
942 DrawLine(bitmap, points[i].x, points[i].y,
943 points[i + 1].x, points[i + 1].y, pixel, line_width);
946 SDLDrawLines(bitmap->surface, points, num_points, pixel);
950 Pixel GetPixel(Bitmap *bitmap, int x, int y)
952 if (x < 0 || x >= bitmap->width ||
953 y < 0 || y >= bitmap->height)
956 return SDLGetPixel(bitmap, x, y);
959 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
960 unsigned int color_g, unsigned int color_b)
962 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
965 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
967 unsigned int color_r = (color >> 16) & 0xff;
968 unsigned int color_g = (color >> 8) & 0xff;
969 unsigned int color_b = (color >> 0) & 0xff;
971 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
974 void KeyboardAutoRepeatOn(void)
976 #if defined(TARGET_SDL2)
977 keyrepeat_status = TRUE;
979 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
980 SDL_DEFAULT_REPEAT_INTERVAL / 2);
981 SDL_EnableUNICODE(1);
985 void KeyboardAutoRepeatOff(void)
987 #if defined(TARGET_SDL2)
988 keyrepeat_status = FALSE;
990 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
991 SDL_EnableUNICODE(0);
995 boolean SetVideoMode(boolean fullscreen)
997 return SDLSetVideoMode(fullscreen);
1000 void SetVideoFrameDelay(unsigned int frame_delay_value)
1002 video.frame_delay_value = frame_delay_value;
1005 unsigned int GetVideoFrameDelay()
1007 return video.frame_delay_value;
1010 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
1012 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
1013 (!fullscreen && video.fullscreen_enabled))
1014 fullscreen = SetVideoMode(fullscreen);
1019 Bitmap *LoadImage(char *filename)
1023 new_bitmap = SDLLoadImage(filename);
1026 new_bitmap->source_filename = getStringCopy(filename);
1031 Bitmap *LoadCustomImage(char *basename)
1033 char *filename = getCustomImageFilename(basename);
1036 if (filename == NULL)
1037 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
1039 if ((new_bitmap = LoadImage(filename)) == NULL)
1040 Error(ERR_EXIT, "LoadImage('%s') failed: %s", basename, GetError());
1045 void ReloadCustomImage(Bitmap *bitmap, char *basename)
1047 char *filename = getCustomImageFilename(basename);
1050 if (filename == NULL) /* (should never happen) */
1052 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
1056 if (strEqual(filename, bitmap->source_filename))
1058 /* The old and new image are the same (have the same filename and path).
1059 This usually means that this image does not exist in this graphic set
1060 and a fallback to the existing image is done. */
1065 if ((new_bitmap = LoadImage(filename)) == NULL)
1067 Error(ERR_WARN, "LoadImage('%s') failed: %s", basename, GetError());
1071 if (bitmap->width != new_bitmap->width ||
1072 bitmap->height != new_bitmap->height)
1074 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
1076 FreeBitmap(new_bitmap);
1080 TransferBitmapPointers(new_bitmap, bitmap);
1084 static Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
1086 return SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
1089 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
1091 if (bitmaps[IMG_BITMAP_CUSTOM])
1093 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
1095 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
1098 if (gfx.game_tile_size == gfx.standard_tile_size)
1100 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1105 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
1106 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
1107 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
1109 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
1111 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
1112 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
1115 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1116 int tile_size, boolean create_small_bitmaps)
1118 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1119 Bitmap *tmp_bitmap_final = NULL;
1120 Bitmap *tmp_bitmap_0 = NULL;
1121 Bitmap *tmp_bitmap_1 = NULL;
1122 Bitmap *tmp_bitmap_2 = NULL;
1123 Bitmap *tmp_bitmap_4 = NULL;
1124 Bitmap *tmp_bitmap_8 = NULL;
1125 Bitmap *tmp_bitmap_16 = NULL;
1126 Bitmap *tmp_bitmap_32 = NULL;
1127 int width_final, height_final;
1128 int width_0, height_0;
1129 int width_1, height_1;
1130 int width_2, height_2;
1131 int width_4, height_4;
1132 int width_8, height_8;
1133 int width_16, height_16;
1134 int width_32, height_32;
1135 int old_width, old_height;
1138 print_timestamp_init("CreateScaledBitmaps");
1140 old_width = old_bitmap->width;
1141 old_height = old_bitmap->height;
1143 /* calculate new image dimensions for final image size */
1144 width_final = old_width * zoom_factor;
1145 height_final = old_height * zoom_factor;
1147 /* get image with final size (this might require scaling up) */
1148 /* ("final" size may result in non-standard tile size image) */
1149 if (zoom_factor != 1)
1150 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1152 tmp_bitmap_final = old_bitmap;
1154 UPDATE_BUSY_STATE();
1156 width_0 = width_1 = width_final;
1157 height_0 = height_1 = height_final;
1159 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1161 if (create_small_bitmaps)
1163 /* check if we have a non-gameplay tile size image */
1164 if (tile_size != gfx.game_tile_size)
1166 /* get image with gameplay tile size */
1167 width_0 = width_final * gfx.game_tile_size / tile_size;
1168 height_0 = height_final * gfx.game_tile_size / tile_size;
1170 if (width_0 == old_width)
1171 tmp_bitmap_0 = old_bitmap;
1172 else if (width_0 == width_final)
1173 tmp_bitmap_0 = tmp_bitmap_final;
1175 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1177 UPDATE_BUSY_STATE();
1180 /* check if we have a non-standard tile size image */
1181 if (tile_size != gfx.standard_tile_size)
1183 /* get image with standard tile size */
1184 width_1 = width_final * gfx.standard_tile_size / tile_size;
1185 height_1 = height_final * gfx.standard_tile_size / tile_size;
1187 if (width_1 == old_width)
1188 tmp_bitmap_1 = old_bitmap;
1189 else if (width_1 == width_final)
1190 tmp_bitmap_1 = tmp_bitmap_final;
1191 else if (width_1 == width_0)
1192 tmp_bitmap_1 = tmp_bitmap_0;
1194 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1196 UPDATE_BUSY_STATE();
1199 /* calculate new image dimensions for small images */
1200 width_2 = width_1 / 2;
1201 height_2 = height_1 / 2;
1202 width_4 = width_1 / 4;
1203 height_4 = height_1 / 4;
1204 width_8 = width_1 / 8;
1205 height_8 = height_1 / 8;
1206 width_16 = width_1 / 16;
1207 height_16 = height_1 / 16;
1208 width_32 = width_1 / 32;
1209 height_32 = height_1 / 32;
1211 /* get image with 1/2 of normal size (for use in the level editor) */
1212 if (width_2 == old_width)
1213 tmp_bitmap_2 = old_bitmap;
1215 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1217 UPDATE_BUSY_STATE();
1219 /* get image with 1/4 of normal size (for use in the level editor) */
1220 if (width_4 == old_width)
1221 tmp_bitmap_4 = old_bitmap;
1223 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1225 UPDATE_BUSY_STATE();
1227 /* get image with 1/8 of normal size (for use on the preview screen) */
1228 if (width_8 == old_width)
1229 tmp_bitmap_8 = old_bitmap;
1231 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1233 UPDATE_BUSY_STATE();
1235 /* get image with 1/16 of normal size (for use on the preview screen) */
1236 if (width_16 == old_width)
1237 tmp_bitmap_16 = old_bitmap;
1239 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1241 UPDATE_BUSY_STATE();
1243 /* get image with 1/32 of normal size (for use on the preview screen) */
1244 if (width_32 == old_width)
1245 tmp_bitmap_32 = old_bitmap;
1247 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1249 UPDATE_BUSY_STATE();
1251 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1252 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1253 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1254 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1255 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1256 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1258 if (width_0 != width_1)
1259 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1261 if (bitmaps[IMG_BITMAP_CUSTOM])
1262 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1264 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1266 boolean free_old_bitmap = TRUE;
1268 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1269 if (bitmaps[i] == old_bitmap)
1270 free_old_bitmap = FALSE;
1272 if (free_old_bitmap)
1273 FreeBitmap(old_bitmap);
1277 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1280 UPDATE_BUSY_STATE();
1282 print_timestamp_done("CreateScaledBitmaps");
1285 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1288 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1291 void CreateBitmapTextures(Bitmap **bitmaps)
1293 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1296 void FreeBitmapTextures(Bitmap **bitmaps)
1298 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1301 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1303 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1307 /* ------------------------------------------------------------------------- */
1308 /* mouse pointer functions */
1309 /* ------------------------------------------------------------------------- */
1311 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1313 /* XPM image definitions */
1314 static const char *cursor_image_none[] =
1316 /* width height num_colors chars_per_pixel */
1346 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1347 static const char *cursor_image_dot[] =
1349 /* width height num_colors chars_per_pixel */
1378 static const char **cursor_image_playfield = cursor_image_dot;
1380 /* some people complained about a "white dot" on the screen and thought it
1381 was a graphical error... OK, let's just remove the whole pointer :-) */
1382 static const char **cursor_image_playfield = cursor_image_none;
1385 static const int cursor_bit_order = BIT_ORDER_MSB;
1387 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1389 struct MouseCursorInfo *cursor;
1390 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1391 int header_lines = 4;
1394 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1396 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1399 for (y = 0; y < cursor->width; y++)
1401 for (x = 0; x < cursor->height; x++)
1404 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1409 cursor->data[i] = cursor->mask[i] = 0;
1412 switch (image[header_lines + y][x])
1415 cursor->data[i] |= bit_mask;
1416 cursor->mask[i] |= bit_mask;
1420 cursor->mask[i] |= bit_mask;
1429 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1434 void SetMouseCursor(int mode)
1436 static struct MouseCursorInfo *cursor_none = NULL;
1437 static struct MouseCursorInfo *cursor_playfield = NULL;
1438 struct MouseCursorInfo *cursor_new;
1440 if (cursor_none == NULL)
1441 cursor_none = get_cursor_from_image(cursor_image_none);
1443 if (cursor_playfield == NULL)
1444 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1446 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1447 mode == CURSOR_NONE ? cursor_none :
1448 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1450 SDLSetMouseCursor(cursor_new);
1452 gfx.cursor_mode = mode;
1456 /* ========================================================================= */
1457 /* audio functions */
1458 /* ========================================================================= */
1460 void OpenAudio(void)
1462 /* always start with reliable default values */
1463 audio.sound_available = FALSE;
1464 audio.music_available = FALSE;
1465 audio.loops_available = FALSE;
1467 audio.sound_enabled = FALSE;
1468 audio.sound_deactivated = FALSE;
1470 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1471 audio.mixer_pid = 0;
1472 audio.device_name = NULL;
1473 audio.device_fd = -1;
1475 audio.num_channels = 0;
1476 audio.music_channel = 0;
1477 audio.first_sound_channel = 0;
1482 void CloseAudio(void)
1486 audio.sound_enabled = FALSE;
1489 void SetAudioMode(boolean enabled)
1491 if (!audio.sound_available)
1494 audio.sound_enabled = enabled;
1498 /* ========================================================================= */
1499 /* event functions */
1500 /* ========================================================================= */
1502 boolean PendingEvent(void)
1504 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1507 void WaitEvent(Event *event)
1509 SDLWaitEvent(event);
1512 void PeekEvent(Event *event)
1514 #if defined(TARGET_SDL2)
1515 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1517 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1521 void CheckQuitEvent(void)
1523 if (SDL_QuitRequested())
1524 program.exit_function(0);
1527 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1529 #if defined(TARGET_SDL2)
1530 /* key up/down events in SDL2 do not return text characters anymore */
1531 return event->keysym.sym;
1534 #if ENABLE_UNUSED_CODE
1535 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1536 (int)event->keysym.unicode,
1537 (int)event->keysym.sym,
1538 (int)SDL_GetModState());
1541 if (with_modifiers &&
1542 event->keysym.unicode > 0x0000 &&
1543 event->keysym.unicode < 0x2000)
1544 return event->keysym.unicode;
1546 return event->keysym.sym;
1551 KeyMod HandleKeyModState(Key key, int key_status)
1553 static KeyMod current_modifiers = KMOD_None;
1555 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1557 KeyMod new_modifier = KMOD_None;
1562 new_modifier = KMOD_Shift_L;
1565 new_modifier = KMOD_Shift_R;
1567 case KSYM_Control_L:
1568 new_modifier = KMOD_Control_L;
1570 case KSYM_Control_R:
1571 new_modifier = KMOD_Control_R;
1574 new_modifier = KMOD_Meta_L;
1577 new_modifier = KMOD_Meta_R;
1580 new_modifier = KMOD_Alt_L;
1583 new_modifier = KMOD_Alt_R;
1589 if (key_status == KEY_PRESSED)
1590 current_modifiers |= new_modifier;
1592 current_modifiers &= ~new_modifier;
1595 return current_modifiers;
1598 KeyMod GetKeyModState()
1600 return (KeyMod)SDL_GetModState();
1603 KeyMod GetKeyModStateFromEvents()
1605 /* always use key modifier state as tracked from key events (this is needed
1606 if the modifier key event was injected into the event queue, but the key
1607 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1608 query the keys as held pressed on the keyboard) -- this case is currently
1609 only used to filter out clipboard insert events from "True X-Mouse" tool */
1611 return HandleKeyModState(KSYM_UNDEFINED, 0);
1614 void StartTextInput(int x, int y, int width, int height)
1616 #if defined(TARGET_SDL2)
1617 #if defined(HAS_SCREEN_KEYBOARD)
1618 SDL_StartTextInput();
1620 if (y + height > SCREEN_KEYBOARD_POS(video.height))
1622 video.shifted_up_pos = y + height - SCREEN_KEYBOARD_POS(video.height);
1623 video.shifted_up_delay = SDL_GetTicks();
1624 video.shifted_up = TRUE;
1630 void StopTextInput()
1632 #if defined(TARGET_SDL2)
1633 #if defined(HAS_SCREEN_KEYBOARD)
1634 SDL_StopTextInput();
1636 if (video.shifted_up)
1638 video.shifted_up_pos = 0;
1639 video.shifted_up_delay = SDL_GetTicks();
1640 video.shifted_up = FALSE;
1646 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1648 if (event->type != EVENT_CLIENTMESSAGE)
1651 return TRUE; /* the only possible message here is SDL_QUIT */
1655 /* ========================================================================= */
1656 /* joystick functions */
1657 /* ========================================================================= */
1659 void InitJoysticks()
1663 #if defined(NO_JOYSTICK)
1664 return; /* joysticks generally deactivated by compile-time directive */
1667 /* always start with reliable default values */
1668 joystick.status = JOYSTICK_NOT_AVAILABLE;
1669 for (i = 0; i < MAX_PLAYERS; i++)
1670 joystick.nr[i] = -1; /* no joystick configured */
1675 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1677 return SDLReadJoystick(nr, x, y, b1, b2);
1680 boolean CheckJoystickOpened(int nr)
1682 return SDLCheckJoystickOpened(nr);
1685 void ClearJoystickState()
1687 SDLClearJoystickState();