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,
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.log_filename[LOG_OUT_ID] = getLogFilename(LOG_OUT_BASENAME);
96 program.log_filename[LOG_ERR_ID] = getLogFilename(LOG_ERR_BASENAME);
97 program.log_file[LOG_OUT_ID] = program.log_file_default[LOG_OUT_ID] = stdout;
98 program.log_file[LOG_ERR_ID] = program.log_file_default[LOG_ERR_ID] = stderr;
100 program.headless = FALSE;
103 void InitScoresInfo()
105 char *global_scores_dir = getPath2(getCommonDataDir(), SCORES_DIRECTORY);
107 program.global_scores = directoryExists(global_scores_dir);
108 program.many_scores_per_name = !program.global_scores;
112 if (program.global_scores)
114 Error(ERR_DEBUG, "Using global, multi-user scores directory '%s'.",
116 Error(ERR_DEBUG, "Remove to enable single-user scores directory.");
117 Error(ERR_DEBUG, "(This enables multipe score entries per user.)");
121 Error(ERR_DEBUG, "Using private, single-user scores directory.");
125 free(global_scores_dir);
128 void SetWindowTitle()
130 program.window_title = program.window_title_function();
135 void InitWindowTitleFunction(char *(*window_title_function)(void))
137 program.window_title_function = window_title_function;
140 void InitExitMessageFunction(void (*exit_message_function)(char *, va_list))
142 program.exit_message_function = exit_message_function;
145 void InitExitFunction(void (*exit_function)(int))
147 program.exit_function = exit_function;
149 /* set signal handlers to custom exit function */
150 // signal(SIGINT, exit_function);
151 signal(SIGTERM, exit_function);
153 /* set exit function to automatically cleanup SDL stuff after exit() */
157 void InitPlatformDependentStuff(void)
159 // this is initialized in GetOptions(), but may already be used before
160 options.verbose = TRUE;
164 #if defined(TARGET_SDL2)
165 int sdl_init_flags = SDL_INIT_EVENTS | SDL_INIT_NOPARACHUTE;
167 int sdl_init_flags = SDL_INIT_EVENTTHREAD | SDL_INIT_NOPARACHUTE;
170 if (SDL_Init(sdl_init_flags) < 0)
171 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
176 void ClosePlatformDependentStuff(void)
181 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
182 int real_sx, int real_sy,
183 int full_sxsize, int full_sysize,
184 Bitmap *field_save_buffer)
190 gfx.real_sx = real_sx;
191 gfx.real_sy = real_sy;
192 gfx.full_sxsize = full_sxsize;
193 gfx.full_sysize = full_sysize;
195 gfx.field_save_buffer = field_save_buffer;
197 SetDrawDeactivationMask(REDRAW_NONE); /* do not deactivate drawing */
198 SetDrawBackgroundMask(REDRAW_NONE); /* deactivate masked drawing */
201 void InitGfxTileSizeInfo(int game_tile_size, int standard_tile_size)
203 gfx.game_tile_size = game_tile_size;
204 gfx.standard_tile_size = standard_tile_size;
207 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
215 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
223 void InitGfxDoor3Info(int ex, int ey, int exsize, int eysize)
231 void InitGfxWindowInfo(int win_xsize, int win_ysize)
233 if (win_xsize != gfx.win_xsize || win_ysize != gfx.win_ysize)
235 ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize);
237 #if defined(TARGET_SDL2)
238 ReCreateBitmap(&gfx.final_screen_bitmap, win_xsize, win_ysize);
241 ReCreateBitmap(&gfx.fade_bitmap_backup, win_xsize, win_ysize);
242 ReCreateBitmap(&gfx.fade_bitmap_source, win_xsize, win_ysize);
243 ReCreateBitmap(&gfx.fade_bitmap_target, win_xsize, win_ysize);
244 ReCreateBitmap(&gfx.fade_bitmap_black, win_xsize, win_ysize);
246 ClearRectangle(gfx.fade_bitmap_black, 0, 0, win_xsize, win_ysize);
249 gfx.win_xsize = win_xsize;
250 gfx.win_ysize = win_ysize;
252 gfx.background_bitmap_mask = REDRAW_NONE;
255 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
257 /* currently only used by MSDOS code to alloc VRAM buffer, if available */
258 /* 2009-03-24: also (temporarily?) used for overlapping blit workaround */
259 gfx.scrollbuffer_width = scrollbuffer_width;
260 gfx.scrollbuffer_height = scrollbuffer_height;
263 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
265 gfx.clipping_enabled = enabled;
268 gfx.clip_width = width;
269 gfx.clip_height = height;
272 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
274 gfx.draw_busy_anim_function = draw_busy_anim_function;
277 void InitGfxDrawGlobalAnimFunction(void (*draw_global_anim_function)(int, int))
279 gfx.draw_global_anim_function = draw_global_anim_function;
282 void InitGfxDrawGlobalBorderFunction(void (*draw_global_border_function)(int))
284 gfx.draw_global_border_function = draw_global_border_function;
287 void InitGfxCustomArtworkInfo()
289 gfx.override_level_graphics = FALSE;
290 gfx.override_level_sounds = FALSE;
291 gfx.override_level_music = FALSE;
293 gfx.draw_init_text = TRUE;
296 void InitGfxOtherSettings()
298 gfx.cursor_mode = CURSOR_DEFAULT;
301 void InitOverlayInfo()
303 overlay.enabled = FALSE;
304 overlay.active = FALSE;
306 #if defined(PLATFORM_ANDROID)
307 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
308 overlay.enabled = TRUE;
312 void SetOverlayEnabled(boolean enabled)
314 overlay.enabled = enabled;
317 void SetOverlayActive(boolean active)
319 overlay.active = active;
322 boolean GetOverlayActive()
324 return overlay.active;
327 void SetDrawDeactivationMask(int draw_deactivation_mask)
329 gfx.draw_deactivation_mask = draw_deactivation_mask;
332 int GetDrawDeactivationMask()
334 return gfx.draw_deactivation_mask;
337 void SetDrawBackgroundMask(int draw_background_mask)
339 gfx.draw_background_mask = draw_background_mask;
342 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
344 if (background_bitmap_tile != NULL)
345 gfx.background_bitmap_mask |= mask;
347 gfx.background_bitmap_mask &= ~mask;
349 if (background_bitmap_tile == NULL) /* empty background requested */
352 if (mask == REDRAW_ALL)
353 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
354 0, 0, video.width, video.height);
355 else if (mask == REDRAW_FIELD)
356 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
357 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
358 else if (mask == REDRAW_DOOR_1)
359 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
360 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
363 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
365 /* remove every mask before setting mask for window */
366 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
367 SetBackgroundBitmap(NULL, 0xffff); /* !!! FIX THIS !!! */
368 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
371 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
373 /* remove window area mask before setting mask for main area */
374 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
375 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
376 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
379 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
381 /* remove window area mask before setting mask for door area */
382 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
383 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
384 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
388 /* ========================================================================= */
389 /* video functions */
390 /* ========================================================================= */
392 inline static int GetRealDepth(int depth)
394 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
397 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
398 int width, int height, Pixel color)
400 SDLFillRectangle(bitmap, x, y, width, height, color);
402 if (bitmap == backbuffer)
403 SetRedrawMaskFromArea(x, y, width, height);
406 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
407 int src_x, int src_y, int width, int height,
408 int dst_x, int dst_y, int mask_mode)
410 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
411 dst_x, dst_y, mask_mode);
413 if (dst_bitmap == backbuffer)
414 SetRedrawMaskFromArea(dst_x, dst_y, width, height);
417 void LimitScreenUpdates(boolean enable)
419 SDLLimitScreenUpdates(enable);
422 void InitVideoDisplay(void)
424 if (program.headless)
427 SDLInitVideoDisplay();
428 #if defined(TARGET_SDL2)
433 void CloseVideoDisplay(void)
435 KeyboardAutoRepeatOn();
437 SDL_QuitSubSystem(SDL_INIT_VIDEO);
440 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
443 video.height = height;
444 video.depth = GetRealDepth(depth);
446 video.screen_width = width;
447 video.screen_height = height;
448 video.screen_xoffset = 0;
449 video.screen_yoffset = 0;
451 video.fullscreen_available = FULLSCREEN_STATUS;
452 video.fullscreen_enabled = FALSE;
454 video.window_scaling_available = WINDOW_SCALING_STATUS;
456 video.frame_delay = 0;
457 video.frame_delay_value = GAME_FRAME_DELAY;
459 video.shifted_up = FALSE;
460 video.shifted_up_pos = 0;
461 video.shifted_up_pos_last = 0;
462 video.shifted_up_delay = 0;
463 video.shifted_up_delay_value = ONE_SECOND_DELAY / 4;
465 SDLInitVideoBuffer(fullscreen);
467 video.initialized = TRUE;
472 inline static void FreeBitmapPointers(Bitmap *bitmap)
477 SDLFreeBitmapPointers(bitmap);
479 checked_free(bitmap->source_filename);
480 bitmap->source_filename = NULL;
483 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
486 if (src_bitmap == NULL || dst_bitmap == NULL)
489 FreeBitmapPointers(dst_bitmap);
491 *dst_bitmap = *src_bitmap;
494 void FreeBitmap(Bitmap *bitmap)
499 FreeBitmapPointers(bitmap);
504 Bitmap *CreateBitmapStruct(void)
506 return checked_calloc(sizeof(Bitmap));
509 Bitmap *CreateBitmap(int width, int height, int depth)
511 Bitmap *new_bitmap = CreateBitmapStruct();
512 int real_width = MAX(1, width); /* prevent zero bitmap width */
513 int real_height = MAX(1, height); /* prevent zero bitmap height */
514 int real_depth = GetRealDepth(depth);
516 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
518 new_bitmap->width = real_width;
519 new_bitmap->height = real_height;
524 void ReCreateBitmap(Bitmap **bitmap, int width, int height)
526 Bitmap *new_bitmap = CreateBitmap(width, height, DEFAULT_DEPTH);
530 *bitmap = new_bitmap;
534 TransferBitmapPointers(new_bitmap, *bitmap);
539 void CloseWindow(DrawWindow *window)
543 void SetRedrawMaskFromArea(int x, int y, int width, int height)
547 int x2 = x + width - 1;
548 int y2 = y + height - 1;
550 if (width == 0 || height == 0)
553 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
554 redraw_mask |= REDRAW_FIELD;
555 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
556 redraw_mask |= REDRAW_DOOR_1;
557 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
558 redraw_mask |= REDRAW_DOOR_2;
559 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
560 redraw_mask |= REDRAW_DOOR_3;
562 redraw_mask = REDRAW_ALL;
565 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
568 if (draw_mask == REDRAW_NONE)
571 if (draw_mask & REDRAW_ALL)
574 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
577 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
580 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
583 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
589 boolean DrawingDeactivated(int x, int y, int width, int height)
591 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
594 boolean DrawingOnBackground(int x, int y)
596 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
597 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
600 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
601 int *width, int *height, boolean is_dest)
603 int clip_x, clip_y, clip_width, clip_height;
605 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
607 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
608 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
609 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
610 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
616 clip_width = bitmap->width;
617 clip_height = bitmap->height;
620 /* skip if rectangle completely outside bitmap */
622 if (*x + *width <= clip_x ||
623 *y + *height <= clip_y ||
624 *x >= clip_x + clip_width ||
625 *y >= clip_y + clip_height)
628 /* clip if rectangle overlaps bitmap */
632 *width -= clip_x - *x;
635 else if (*x + *width > clip_x + clip_width)
637 *width = clip_x + clip_width - *x;
642 *height -= clip_y - *y;
645 else if (*y + *height > clip_y + clip_height)
647 *height = clip_y + clip_height - *y;
653 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
654 int src_x, int src_y, int width, int height,
655 int dst_x, int dst_y)
657 int dst_x_unclipped = dst_x;
658 int dst_y_unclipped = dst_y;
660 if (program.headless)
663 if (src_bitmap == NULL || dst_bitmap == NULL)
666 if (DrawingDeactivated(dst_x, dst_y, width, height))
669 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
670 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
673 /* source x/y might need adjustment if destination x/y was clipped top/left */
674 src_x += dst_x - dst_x_unclipped;
675 src_y += dst_y - dst_y_unclipped;
677 #if defined(TARGET_SDL2)
678 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
679 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
680 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
681 but is already fixed in SVN and should therefore finally be fixed with
682 the next official SDL release, which is probably version 1.2.14.) */
683 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
685 if (src_bitmap == dst_bitmap)
687 /* needed when blitting directly to same bitmap -- should not be needed with
688 recent SDL libraries, but apparently does not work in 1.2.11 directly */
690 static Bitmap *tmp_bitmap = NULL;
691 static int tmp_bitmap_xsize = 0;
692 static int tmp_bitmap_ysize = 0;
694 /* start with largest static bitmaps for initial bitmap size ... */
695 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
697 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
698 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
701 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
702 if (src_bitmap->width > tmp_bitmap_xsize ||
703 src_bitmap->height > tmp_bitmap_ysize)
705 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
706 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
708 FreeBitmap(tmp_bitmap);
713 if (tmp_bitmap == NULL)
714 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
717 sysCopyArea(src_bitmap, tmp_bitmap,
718 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
719 sysCopyArea(tmp_bitmap, dst_bitmap,
720 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
726 sysCopyArea(src_bitmap, dst_bitmap,
727 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
730 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
731 int src_x, int src_y, int src_width, int src_height,
732 int dst_x, int dst_y, int dst_width, int dst_height)
734 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
735 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
736 int dst_xsize = dst_width;
737 int dst_ysize = dst_height;
738 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
739 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
742 for (y = 0; y < src_ysteps; y++)
744 for (x = 0; x < src_xsteps; x++)
746 int draw_x = dst_x + x * src_xsize;
747 int draw_y = dst_y + y * src_ysize;
748 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
749 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
751 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
757 void FadeRectangle(int x, int y, int width, int height,
758 int fade_mode, int fade_delay, int post_delay,
759 void (*draw_border_function)(void))
761 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
762 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
765 SDLFadeRectangle(x, y, width, height,
766 fade_mode, fade_delay, post_delay, draw_border_function);
769 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
772 if (DrawingDeactivated(x, y, width, height))
775 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
778 sysFillRectangle(bitmap, x, y, width, height, color);
781 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
783 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
786 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
787 int width, int height)
789 if (DrawingOnBackground(x, y))
790 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
792 ClearRectangle(bitmap, x, y, width, height);
795 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
796 int src_x, int src_y, int width, int height,
797 int dst_x, int dst_y)
799 if (DrawingDeactivated(dst_x, dst_y, width, height))
802 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
803 dst_x, dst_y, BLIT_MASKED);
806 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
807 int src_x, int src_y, int width, int height,
808 int dst_x, int dst_y)
810 if (DrawingOnBackground(dst_x, dst_y))
812 /* draw background */
813 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
816 /* draw foreground */
817 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
821 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
825 void BlitTexture(Bitmap *bitmap,
826 int src_x, int src_y, int width, int height,
827 int dst_x, int dst_y)
832 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
836 void BlitTextureMasked(Bitmap *bitmap,
837 int src_x, int src_y, int width, int height,
838 int dst_x, int dst_y)
843 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
847 void BlitToScreen(Bitmap *bitmap,
848 int src_x, int src_y, int width, int height,
849 int dst_x, int dst_y)
854 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
855 BlitBitmap(bitmap, gfx.final_screen_bitmap, src_x, src_y,
856 width, height, dst_x, dst_y);
858 BlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y);
861 void BlitToScreenMasked(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 BlitBitmapMasked(bitmap, gfx.final_screen_bitmap, src_x, src_y,
870 width, height, dst_x, dst_y);
872 BlitTextureMasked(bitmap, src_x, src_y, width, height, dst_x, dst_y);
875 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
878 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
881 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
884 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
887 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
888 int to_x, int to_y, Pixel pixel, int line_width)
892 for (x = 0; x < line_width; x++)
894 for (y = 0; y < line_width; y++)
896 int dx = x - line_width / 2;
897 int dy = y - line_width / 2;
899 if ((x == 0 && y == 0) ||
900 (x == 0 && y == line_width - 1) ||
901 (x == line_width - 1 && y == 0) ||
902 (x == line_width - 1 && y == line_width - 1))
906 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
911 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
916 for (i = 0; i < num_points - 1; i++)
917 DrawLine(bitmap, points[i].x, points[i].y,
918 points[i + 1].x, points[i + 1].y, pixel, line_width);
921 SDLDrawLines(bitmap->surface, points, num_points, pixel);
925 Pixel GetPixel(Bitmap *bitmap, int x, int y)
927 if (x < 0 || x >= bitmap->width ||
928 y < 0 || y >= bitmap->height)
931 return SDLGetPixel(bitmap, x, y);
934 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
935 unsigned int color_g, unsigned int color_b)
937 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
940 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
942 unsigned int color_r = (color >> 16) & 0xff;
943 unsigned int color_g = (color >> 8) & 0xff;
944 unsigned int color_b = (color >> 0) & 0xff;
946 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
949 void KeyboardAutoRepeatOn(void)
951 #if defined(TARGET_SDL2)
952 keyrepeat_status = TRUE;
954 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
955 SDL_DEFAULT_REPEAT_INTERVAL / 2);
956 SDL_EnableUNICODE(1);
960 void KeyboardAutoRepeatOff(void)
962 #if defined(TARGET_SDL2)
963 keyrepeat_status = FALSE;
965 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
966 SDL_EnableUNICODE(0);
970 boolean SetVideoMode(boolean fullscreen)
972 return SDLSetVideoMode(fullscreen);
975 void SetVideoFrameDelay(unsigned int frame_delay_value)
977 video.frame_delay_value = frame_delay_value;
980 unsigned int GetVideoFrameDelay()
982 return video.frame_delay_value;
985 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
987 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
988 (!fullscreen && video.fullscreen_enabled))
989 fullscreen = SetVideoMode(fullscreen);
994 Bitmap *LoadImage(char *filename)
998 new_bitmap = SDLLoadImage(filename);
1001 new_bitmap->source_filename = getStringCopy(filename);
1006 Bitmap *LoadCustomImage(char *basename)
1008 char *filename = getCustomImageFilename(basename);
1011 if (filename == NULL)
1012 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
1014 if ((new_bitmap = LoadImage(filename)) == NULL)
1015 Error(ERR_EXIT, "LoadImage('%s') failed: %s", basename, GetError());
1020 void ReloadCustomImage(Bitmap *bitmap, char *basename)
1022 char *filename = getCustomImageFilename(basename);
1025 if (filename == NULL) /* (should never happen) */
1027 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
1031 if (strEqual(filename, bitmap->source_filename))
1033 /* The old and new image are the same (have the same filename and path).
1034 This usually means that this image does not exist in this graphic set
1035 and a fallback to the existing image is done. */
1040 if ((new_bitmap = LoadImage(filename)) == NULL)
1042 Error(ERR_WARN, "LoadImage('%s') failed: %s", basename, GetError());
1046 if (bitmap->width != new_bitmap->width ||
1047 bitmap->height != new_bitmap->height)
1049 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
1051 FreeBitmap(new_bitmap);
1055 TransferBitmapPointers(new_bitmap, bitmap);
1059 static Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
1061 return SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
1064 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
1066 if (bitmaps[IMG_BITMAP_CUSTOM])
1068 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
1070 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
1073 if (gfx.game_tile_size == gfx.standard_tile_size)
1075 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1080 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
1081 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
1082 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
1084 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
1086 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
1087 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
1090 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1091 int tile_size, boolean create_small_bitmaps)
1093 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1094 Bitmap *tmp_bitmap_final = NULL;
1095 Bitmap *tmp_bitmap_0 = NULL;
1096 Bitmap *tmp_bitmap_1 = NULL;
1097 Bitmap *tmp_bitmap_2 = NULL;
1098 Bitmap *tmp_bitmap_4 = NULL;
1099 Bitmap *tmp_bitmap_8 = NULL;
1100 Bitmap *tmp_bitmap_16 = NULL;
1101 Bitmap *tmp_bitmap_32 = NULL;
1102 int width_final, height_final;
1103 int width_0, height_0;
1104 int width_1, height_1;
1105 int width_2, height_2;
1106 int width_4, height_4;
1107 int width_8, height_8;
1108 int width_16, height_16;
1109 int width_32, height_32;
1110 int old_width, old_height;
1113 print_timestamp_init("CreateScaledBitmaps");
1115 old_width = old_bitmap->width;
1116 old_height = old_bitmap->height;
1118 /* calculate new image dimensions for final image size */
1119 width_final = old_width * zoom_factor;
1120 height_final = old_height * zoom_factor;
1122 /* get image with final size (this might require scaling up) */
1123 /* ("final" size may result in non-standard tile size image) */
1124 if (zoom_factor != 1)
1125 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1127 tmp_bitmap_final = old_bitmap;
1129 UPDATE_BUSY_STATE();
1131 width_0 = width_1 = width_final;
1132 height_0 = height_1 = height_final;
1134 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1136 if (create_small_bitmaps)
1138 /* check if we have a non-gameplay tile size image */
1139 if (tile_size != gfx.game_tile_size)
1141 /* get image with gameplay tile size */
1142 width_0 = width_final * gfx.game_tile_size / tile_size;
1143 height_0 = height_final * gfx.game_tile_size / tile_size;
1145 if (width_0 == old_width)
1146 tmp_bitmap_0 = old_bitmap;
1147 else if (width_0 == width_final)
1148 tmp_bitmap_0 = tmp_bitmap_final;
1150 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1152 UPDATE_BUSY_STATE();
1155 /* check if we have a non-standard tile size image */
1156 if (tile_size != gfx.standard_tile_size)
1158 /* get image with standard tile size */
1159 width_1 = width_final * gfx.standard_tile_size / tile_size;
1160 height_1 = height_final * gfx.standard_tile_size / tile_size;
1162 if (width_1 == old_width)
1163 tmp_bitmap_1 = old_bitmap;
1164 else if (width_1 == width_final)
1165 tmp_bitmap_1 = tmp_bitmap_final;
1166 else if (width_1 == width_0)
1167 tmp_bitmap_1 = tmp_bitmap_0;
1169 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1171 UPDATE_BUSY_STATE();
1174 /* calculate new image dimensions for small images */
1175 width_2 = width_1 / 2;
1176 height_2 = height_1 / 2;
1177 width_4 = width_1 / 4;
1178 height_4 = height_1 / 4;
1179 width_8 = width_1 / 8;
1180 height_8 = height_1 / 8;
1181 width_16 = width_1 / 16;
1182 height_16 = height_1 / 16;
1183 width_32 = width_1 / 32;
1184 height_32 = height_1 / 32;
1186 /* get image with 1/2 of normal size (for use in the level editor) */
1187 if (width_2 == old_width)
1188 tmp_bitmap_2 = old_bitmap;
1190 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1192 UPDATE_BUSY_STATE();
1194 /* get image with 1/4 of normal size (for use in the level editor) */
1195 if (width_4 == old_width)
1196 tmp_bitmap_4 = old_bitmap;
1198 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1200 UPDATE_BUSY_STATE();
1202 /* get image with 1/8 of normal size (for use on the preview screen) */
1203 if (width_8 == old_width)
1204 tmp_bitmap_8 = old_bitmap;
1206 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1208 UPDATE_BUSY_STATE();
1210 /* get image with 1/16 of normal size (for use on the preview screen) */
1211 if (width_16 == old_width)
1212 tmp_bitmap_16 = old_bitmap;
1214 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1216 UPDATE_BUSY_STATE();
1218 /* get image with 1/32 of normal size (for use on the preview screen) */
1219 if (width_32 == old_width)
1220 tmp_bitmap_32 = old_bitmap;
1222 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1224 UPDATE_BUSY_STATE();
1226 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1227 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1228 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1229 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1230 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1231 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1233 if (width_0 != width_1)
1234 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1236 if (bitmaps[IMG_BITMAP_CUSTOM])
1237 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1239 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1241 boolean free_old_bitmap = TRUE;
1243 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1244 if (bitmaps[i] == old_bitmap)
1245 free_old_bitmap = FALSE;
1247 if (free_old_bitmap)
1248 FreeBitmap(old_bitmap);
1252 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1255 UPDATE_BUSY_STATE();
1257 print_timestamp_done("CreateScaledBitmaps");
1260 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1263 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1266 void CreateBitmapTextures(Bitmap **bitmaps)
1268 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1271 void FreeBitmapTextures(Bitmap **bitmaps)
1273 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1276 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1278 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1282 /* ------------------------------------------------------------------------- */
1283 /* mouse pointer functions */
1284 /* ------------------------------------------------------------------------- */
1286 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1288 /* XPM image definitions */
1289 static const char *cursor_image_none[] =
1291 /* width height num_colors chars_per_pixel */
1321 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1322 static const char *cursor_image_dot[] =
1324 /* width height num_colors chars_per_pixel */
1353 static const char **cursor_image_playfield = cursor_image_dot;
1355 /* some people complained about a "white dot" on the screen and thought it
1356 was a graphical error... OK, let's just remove the whole pointer :-) */
1357 static const char **cursor_image_playfield = cursor_image_none;
1360 static const int cursor_bit_order = BIT_ORDER_MSB;
1362 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1364 struct MouseCursorInfo *cursor;
1365 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1366 int header_lines = 4;
1369 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1371 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1374 for (y = 0; y < cursor->width; y++)
1376 for (x = 0; x < cursor->height; x++)
1379 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1384 cursor->data[i] = cursor->mask[i] = 0;
1387 switch (image[header_lines + y][x])
1390 cursor->data[i] |= bit_mask;
1391 cursor->mask[i] |= bit_mask;
1395 cursor->mask[i] |= bit_mask;
1404 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1409 void SetMouseCursor(int mode)
1411 static struct MouseCursorInfo *cursor_none = NULL;
1412 static struct MouseCursorInfo *cursor_playfield = NULL;
1413 struct MouseCursorInfo *cursor_new;
1415 if (cursor_none == NULL)
1416 cursor_none = get_cursor_from_image(cursor_image_none);
1418 if (cursor_playfield == NULL)
1419 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1421 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1422 mode == CURSOR_NONE ? cursor_none :
1423 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1425 SDLSetMouseCursor(cursor_new);
1427 gfx.cursor_mode = mode;
1431 /* ========================================================================= */
1432 /* audio functions */
1433 /* ========================================================================= */
1435 void OpenAudio(void)
1437 /* always start with reliable default values */
1438 audio.sound_available = FALSE;
1439 audio.music_available = FALSE;
1440 audio.loops_available = FALSE;
1442 audio.sound_enabled = FALSE;
1443 audio.sound_deactivated = FALSE;
1445 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1446 audio.mixer_pid = 0;
1447 audio.device_name = NULL;
1448 audio.device_fd = -1;
1450 audio.num_channels = 0;
1451 audio.music_channel = 0;
1452 audio.first_sound_channel = 0;
1457 void CloseAudio(void)
1461 audio.sound_enabled = FALSE;
1464 void SetAudioMode(boolean enabled)
1466 if (!audio.sound_available)
1469 audio.sound_enabled = enabled;
1473 /* ========================================================================= */
1474 /* event functions */
1475 /* ========================================================================= */
1477 boolean PendingEvent(void)
1479 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1482 void NextEvent(Event *event)
1484 SDLNextEvent(event);
1487 void PeekEvent(Event *event)
1489 #if defined(TARGET_SDL2)
1490 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1492 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1496 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1498 #if defined(TARGET_SDL2)
1499 /* key up/down events in SDL2 do not return text characters anymore */
1500 return event->keysym.sym;
1503 #if ENABLE_UNUSED_CODE
1504 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1505 (int)event->keysym.unicode,
1506 (int)event->keysym.sym,
1507 (int)SDL_GetModState());
1510 if (with_modifiers &&
1511 event->keysym.unicode > 0x0000 &&
1512 event->keysym.unicode < 0x2000)
1513 return event->keysym.unicode;
1515 return event->keysym.sym;
1520 KeyMod HandleKeyModState(Key key, int key_status)
1522 static KeyMod current_modifiers = KMOD_None;
1524 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1526 KeyMod new_modifier = KMOD_None;
1531 new_modifier = KMOD_Shift_L;
1534 new_modifier = KMOD_Shift_R;
1536 case KSYM_Control_L:
1537 new_modifier = KMOD_Control_L;
1539 case KSYM_Control_R:
1540 new_modifier = KMOD_Control_R;
1543 new_modifier = KMOD_Meta_L;
1546 new_modifier = KMOD_Meta_R;
1549 new_modifier = KMOD_Alt_L;
1552 new_modifier = KMOD_Alt_R;
1558 if (key_status == KEY_PRESSED)
1559 current_modifiers |= new_modifier;
1561 current_modifiers &= ~new_modifier;
1564 return current_modifiers;
1567 KeyMod GetKeyModState()
1569 return (KeyMod)SDL_GetModState();
1572 KeyMod GetKeyModStateFromEvents()
1574 /* always use key modifier state as tracked from key events (this is needed
1575 if the modifier key event was injected into the event queue, but the key
1576 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1577 query the keys as held pressed on the keyboard) -- this case is currently
1578 only used to filter out clipboard insert events from "True X-Mouse" tool */
1580 return HandleKeyModState(KSYM_UNDEFINED, 0);
1583 void StartTextInput(int x, int y, int width, int height)
1585 #if defined(TARGET_SDL2)
1586 SDL_StartTextInput();
1588 #if defined(HAS_SCREEN_KEYBOARD)
1589 if (y + height > SCREEN_KEYBOARD_POS(video.height))
1591 video.shifted_up_pos = y + height - SCREEN_KEYBOARD_POS(video.height);
1592 video.shifted_up_delay = SDL_GetTicks();
1593 video.shifted_up = TRUE;
1599 void StopTextInput()
1601 #if defined(TARGET_SDL2)
1602 SDL_StopTextInput();
1604 #if defined(HAS_SCREEN_KEYBOARD)
1605 if (video.shifted_up)
1607 video.shifted_up_pos = 0;
1608 video.shifted_up_delay = SDL_GetTicks();
1609 video.shifted_up = FALSE;
1615 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1617 if (event->type != EVENT_CLIENTMESSAGE)
1620 return TRUE; /* the only possible message here is SDL_QUIT */
1624 /* ========================================================================= */
1625 /* joystick functions */
1626 /* ========================================================================= */
1628 void InitJoysticks()
1632 #if defined(NO_JOYSTICK)
1633 return; /* joysticks generally deactivated by compile-time directive */
1636 /* always start with reliable default values */
1637 joystick.status = JOYSTICK_NOT_AVAILABLE;
1638 for (i = 0; i < MAX_PLAYERS; i++)
1639 joystick.nr[i] = -1; /* no joystick configured */
1644 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1646 return SDLReadJoystick(nr, x, y, b1, b2);
1649 boolean CheckJoystickOpened(int nr)
1651 return SDLCheckJoystickOpened(nr);
1654 void ClearJoystickState()
1656 SDLClearJoystickState();