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 if (program.headless)
920 for (x = 0; x < line_width; x++)
922 for (y = 0; y < line_width; y++)
924 int dx = x - line_width / 2;
925 int dy = y - line_width / 2;
927 if ((x == 0 && y == 0) ||
928 (x == 0 && y == line_width - 1) ||
929 (x == line_width - 1 && y == 0) ||
930 (x == line_width - 1 && y == line_width - 1))
934 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
939 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
944 for (i = 0; i < num_points - 1; i++)
945 DrawLine(bitmap, points[i].x, points[i].y,
946 points[i + 1].x, points[i + 1].y, pixel, line_width);
949 SDLDrawLines(bitmap->surface, points, num_points, pixel);
953 Pixel GetPixel(Bitmap *bitmap, int x, int y)
955 if (program.headless)
958 if (x < 0 || x >= bitmap->width ||
959 y < 0 || y >= bitmap->height)
962 return SDLGetPixel(bitmap, x, y);
965 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
966 unsigned int color_g, unsigned int color_b)
968 if (program.headless)
971 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
974 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
976 unsigned int color_r = (color >> 16) & 0xff;
977 unsigned int color_g = (color >> 8) & 0xff;
978 unsigned int color_b = (color >> 0) & 0xff;
980 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
983 void KeyboardAutoRepeatOn(void)
985 #if defined(TARGET_SDL2)
986 keyrepeat_status = TRUE;
988 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
989 SDL_DEFAULT_REPEAT_INTERVAL / 2);
990 SDL_EnableUNICODE(1);
994 void KeyboardAutoRepeatOff(void)
996 #if defined(TARGET_SDL2)
997 keyrepeat_status = FALSE;
999 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
1000 SDL_EnableUNICODE(0);
1004 boolean SetVideoMode(boolean fullscreen)
1006 return SDLSetVideoMode(fullscreen);
1009 void SetVideoFrameDelay(unsigned int frame_delay_value)
1011 video.frame_delay_value = frame_delay_value;
1014 unsigned int GetVideoFrameDelay()
1016 return video.frame_delay_value;
1019 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
1021 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
1022 (!fullscreen && video.fullscreen_enabled))
1023 fullscreen = SetVideoMode(fullscreen);
1028 Bitmap *LoadImage(char *filename)
1032 new_bitmap = SDLLoadImage(filename);
1035 new_bitmap->source_filename = getStringCopy(filename);
1040 Bitmap *LoadCustomImage(char *basename)
1042 char *filename = getCustomImageFilename(basename);
1045 if (filename == NULL)
1046 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
1048 if ((new_bitmap = LoadImage(filename)) == NULL)
1049 Error(ERR_EXIT, "LoadImage('%s') failed: %s", basename, GetError());
1054 void ReloadCustomImage(Bitmap *bitmap, char *basename)
1056 char *filename = getCustomImageFilename(basename);
1059 if (filename == NULL) /* (should never happen) */
1061 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
1065 if (strEqual(filename, bitmap->source_filename))
1067 /* The old and new image are the same (have the same filename and path).
1068 This usually means that this image does not exist in this graphic set
1069 and a fallback to the existing image is done. */
1074 if ((new_bitmap = LoadImage(filename)) == NULL)
1076 Error(ERR_WARN, "LoadImage('%s') failed: %s", basename, GetError());
1080 if (bitmap->width != new_bitmap->width ||
1081 bitmap->height != new_bitmap->height)
1083 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
1085 FreeBitmap(new_bitmap);
1089 TransferBitmapPointers(new_bitmap, bitmap);
1093 static Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
1095 return SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
1098 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
1100 if (bitmaps[IMG_BITMAP_CUSTOM])
1102 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
1104 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
1107 if (gfx.game_tile_size == gfx.standard_tile_size)
1109 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1114 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
1115 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
1116 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
1118 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
1120 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
1121 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
1124 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1125 int tile_size, boolean create_small_bitmaps)
1127 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1128 Bitmap *tmp_bitmap_final = NULL;
1129 Bitmap *tmp_bitmap_0 = NULL;
1130 Bitmap *tmp_bitmap_1 = NULL;
1131 Bitmap *tmp_bitmap_2 = NULL;
1132 Bitmap *tmp_bitmap_4 = NULL;
1133 Bitmap *tmp_bitmap_8 = NULL;
1134 Bitmap *tmp_bitmap_16 = NULL;
1135 Bitmap *tmp_bitmap_32 = NULL;
1136 int width_final, height_final;
1137 int width_0, height_0;
1138 int width_1, height_1;
1139 int width_2, height_2;
1140 int width_4, height_4;
1141 int width_8, height_8;
1142 int width_16, height_16;
1143 int width_32, height_32;
1144 int old_width, old_height;
1147 print_timestamp_init("CreateScaledBitmaps");
1149 old_width = old_bitmap->width;
1150 old_height = old_bitmap->height;
1152 /* calculate new image dimensions for final image size */
1153 width_final = old_width * zoom_factor;
1154 height_final = old_height * zoom_factor;
1156 /* get image with final size (this might require scaling up) */
1157 /* ("final" size may result in non-standard tile size image) */
1158 if (zoom_factor != 1)
1159 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1161 tmp_bitmap_final = old_bitmap;
1163 UPDATE_BUSY_STATE();
1165 width_0 = width_1 = width_final;
1166 height_0 = height_1 = height_final;
1168 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1170 if (create_small_bitmaps)
1172 /* check if we have a non-gameplay tile size image */
1173 if (tile_size != gfx.game_tile_size)
1175 /* get image with gameplay tile size */
1176 width_0 = width_final * gfx.game_tile_size / tile_size;
1177 height_0 = height_final * gfx.game_tile_size / tile_size;
1179 if (width_0 == old_width)
1180 tmp_bitmap_0 = old_bitmap;
1181 else if (width_0 == width_final)
1182 tmp_bitmap_0 = tmp_bitmap_final;
1184 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1186 UPDATE_BUSY_STATE();
1189 /* check if we have a non-standard tile size image */
1190 if (tile_size != gfx.standard_tile_size)
1192 /* get image with standard tile size */
1193 width_1 = width_final * gfx.standard_tile_size / tile_size;
1194 height_1 = height_final * gfx.standard_tile_size / tile_size;
1196 if (width_1 == old_width)
1197 tmp_bitmap_1 = old_bitmap;
1198 else if (width_1 == width_final)
1199 tmp_bitmap_1 = tmp_bitmap_final;
1200 else if (width_1 == width_0)
1201 tmp_bitmap_1 = tmp_bitmap_0;
1203 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1205 UPDATE_BUSY_STATE();
1208 /* calculate new image dimensions for small images */
1209 width_2 = width_1 / 2;
1210 height_2 = height_1 / 2;
1211 width_4 = width_1 / 4;
1212 height_4 = height_1 / 4;
1213 width_8 = width_1 / 8;
1214 height_8 = height_1 / 8;
1215 width_16 = width_1 / 16;
1216 height_16 = height_1 / 16;
1217 width_32 = width_1 / 32;
1218 height_32 = height_1 / 32;
1220 /* get image with 1/2 of normal size (for use in the level editor) */
1221 if (width_2 == old_width)
1222 tmp_bitmap_2 = old_bitmap;
1224 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1226 UPDATE_BUSY_STATE();
1228 /* get image with 1/4 of normal size (for use in the level editor) */
1229 if (width_4 == old_width)
1230 tmp_bitmap_4 = old_bitmap;
1232 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1234 UPDATE_BUSY_STATE();
1236 /* get image with 1/8 of normal size (for use on the preview screen) */
1237 if (width_8 == old_width)
1238 tmp_bitmap_8 = old_bitmap;
1240 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1242 UPDATE_BUSY_STATE();
1244 /* get image with 1/16 of normal size (for use on the preview screen) */
1245 if (width_16 == old_width)
1246 tmp_bitmap_16 = old_bitmap;
1248 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1250 UPDATE_BUSY_STATE();
1252 /* get image with 1/32 of normal size (for use on the preview screen) */
1253 if (width_32 == old_width)
1254 tmp_bitmap_32 = old_bitmap;
1256 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1258 UPDATE_BUSY_STATE();
1260 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1261 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1262 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1263 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1264 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1265 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1267 if (width_0 != width_1)
1268 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1270 if (bitmaps[IMG_BITMAP_CUSTOM])
1271 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1273 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1275 boolean free_old_bitmap = TRUE;
1277 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1278 if (bitmaps[i] == old_bitmap)
1279 free_old_bitmap = FALSE;
1281 if (free_old_bitmap)
1282 FreeBitmap(old_bitmap);
1286 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1289 UPDATE_BUSY_STATE();
1291 print_timestamp_done("CreateScaledBitmaps");
1294 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1297 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1300 void CreateBitmapTextures(Bitmap **bitmaps)
1302 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1305 void FreeBitmapTextures(Bitmap **bitmaps)
1307 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1310 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1312 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1316 /* ------------------------------------------------------------------------- */
1317 /* mouse pointer functions */
1318 /* ------------------------------------------------------------------------- */
1320 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1322 /* XPM image definitions */
1323 static const char *cursor_image_none[] =
1325 /* width height num_colors chars_per_pixel */
1355 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1356 static const char *cursor_image_dot[] =
1358 /* width height num_colors chars_per_pixel */
1387 static const char **cursor_image_playfield = cursor_image_dot;
1389 /* some people complained about a "white dot" on the screen and thought it
1390 was a graphical error... OK, let's just remove the whole pointer :-) */
1391 static const char **cursor_image_playfield = cursor_image_none;
1394 static const int cursor_bit_order = BIT_ORDER_MSB;
1396 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1398 struct MouseCursorInfo *cursor;
1399 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1400 int header_lines = 4;
1403 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1405 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1408 for (y = 0; y < cursor->width; y++)
1410 for (x = 0; x < cursor->height; x++)
1413 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1418 cursor->data[i] = cursor->mask[i] = 0;
1421 switch (image[header_lines + y][x])
1424 cursor->data[i] |= bit_mask;
1425 cursor->mask[i] |= bit_mask;
1429 cursor->mask[i] |= bit_mask;
1438 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1443 void SetMouseCursor(int mode)
1445 static struct MouseCursorInfo *cursor_none = NULL;
1446 static struct MouseCursorInfo *cursor_playfield = NULL;
1447 struct MouseCursorInfo *cursor_new;
1449 if (cursor_none == NULL)
1450 cursor_none = get_cursor_from_image(cursor_image_none);
1452 if (cursor_playfield == NULL)
1453 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1455 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1456 mode == CURSOR_NONE ? cursor_none :
1457 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1459 SDLSetMouseCursor(cursor_new);
1461 gfx.cursor_mode = mode;
1465 /* ========================================================================= */
1466 /* audio functions */
1467 /* ========================================================================= */
1469 void OpenAudio(void)
1471 /* always start with reliable default values */
1472 audio.sound_available = FALSE;
1473 audio.music_available = FALSE;
1474 audio.loops_available = FALSE;
1476 audio.sound_enabled = FALSE;
1477 audio.sound_deactivated = FALSE;
1479 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1480 audio.mixer_pid = 0;
1481 audio.device_name = NULL;
1482 audio.device_fd = -1;
1484 audio.num_channels = 0;
1485 audio.music_channel = 0;
1486 audio.first_sound_channel = 0;
1491 void CloseAudio(void)
1495 audio.sound_enabled = FALSE;
1498 void SetAudioMode(boolean enabled)
1500 if (!audio.sound_available)
1503 audio.sound_enabled = enabled;
1507 /* ========================================================================= */
1508 /* event functions */
1509 /* ========================================================================= */
1511 boolean PendingEvent(void)
1513 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1516 void WaitEvent(Event *event)
1518 SDLWaitEvent(event);
1521 void PeekEvent(Event *event)
1523 #if defined(TARGET_SDL2)
1524 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1526 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1530 void CheckQuitEvent(void)
1532 if (SDL_QuitRequested())
1533 program.exit_function(0);
1536 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1538 #if defined(TARGET_SDL2)
1539 /* key up/down events in SDL2 do not return text characters anymore */
1540 return event->keysym.sym;
1543 #if ENABLE_UNUSED_CODE
1544 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1545 (int)event->keysym.unicode,
1546 (int)event->keysym.sym,
1547 (int)SDL_GetModState());
1550 if (with_modifiers &&
1551 event->keysym.unicode > 0x0000 &&
1552 event->keysym.unicode < 0x2000)
1553 return event->keysym.unicode;
1555 return event->keysym.sym;
1560 KeyMod HandleKeyModState(Key key, int key_status)
1562 static KeyMod current_modifiers = KMOD_None;
1564 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1566 KeyMod new_modifier = KMOD_None;
1571 new_modifier = KMOD_Shift_L;
1574 new_modifier = KMOD_Shift_R;
1576 case KSYM_Control_L:
1577 new_modifier = KMOD_Control_L;
1579 case KSYM_Control_R:
1580 new_modifier = KMOD_Control_R;
1583 new_modifier = KMOD_Meta_L;
1586 new_modifier = KMOD_Meta_R;
1589 new_modifier = KMOD_Alt_L;
1592 new_modifier = KMOD_Alt_R;
1598 if (key_status == KEY_PRESSED)
1599 current_modifiers |= new_modifier;
1601 current_modifiers &= ~new_modifier;
1604 return current_modifiers;
1607 KeyMod GetKeyModState()
1609 return (KeyMod)SDL_GetModState();
1612 KeyMod GetKeyModStateFromEvents()
1614 /* always use key modifier state as tracked from key events (this is needed
1615 if the modifier key event was injected into the event queue, but the key
1616 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1617 query the keys as held pressed on the keyboard) -- this case is currently
1618 only used to filter out clipboard insert events from "True X-Mouse" tool */
1620 return HandleKeyModState(KSYM_UNDEFINED, 0);
1623 void StartTextInput(int x, int y, int width, int height)
1625 #if defined(TARGET_SDL2)
1626 #if defined(HAS_SCREEN_KEYBOARD)
1627 SDL_StartTextInput();
1629 if (y + height > SCREEN_KEYBOARD_POS(video.height))
1631 video.shifted_up_pos = y + height - SCREEN_KEYBOARD_POS(video.height);
1632 video.shifted_up_delay = SDL_GetTicks();
1633 video.shifted_up = TRUE;
1639 void StopTextInput()
1641 #if defined(TARGET_SDL2)
1642 #if defined(HAS_SCREEN_KEYBOARD)
1643 SDL_StopTextInput();
1645 if (video.shifted_up)
1647 video.shifted_up_pos = 0;
1648 video.shifted_up_delay = SDL_GetTicks();
1649 video.shifted_up = FALSE;
1655 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1657 if (event->type != EVENT_CLIENTMESSAGE)
1660 return TRUE; /* the only possible message here is SDL_QUIT */
1664 /* ========================================================================= */
1665 /* joystick functions */
1666 /* ========================================================================= */
1668 void InitJoysticks()
1672 #if defined(NO_JOYSTICK)
1673 return; /* joysticks generally deactivated by compile-time directive */
1676 /* always start with reliable default values */
1677 joystick.status = JOYSTICK_NOT_AVAILABLE;
1678 for (i = 0; i < MAX_PLAYERS; i++)
1679 joystick.nr[i] = -1; /* no joystick configured */
1684 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1686 return SDLReadJoystick(nr, x, y, b1, b2);
1689 boolean CheckJoystickOpened(int nr)
1691 return SDLCheckJoystickOpened(nr);
1694 void ClearJoystickState()
1696 SDLClearJoystickState();