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.active = FALSE;
306 void SetOverlayActive(boolean active)
308 overlay.active = active;
311 boolean GetOverlayActive()
313 return overlay.active;
316 void SetDrawDeactivationMask(int draw_deactivation_mask)
318 gfx.draw_deactivation_mask = draw_deactivation_mask;
321 void SetDrawBackgroundMask(int draw_background_mask)
323 gfx.draw_background_mask = draw_background_mask;
326 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
328 if (background_bitmap_tile != NULL)
329 gfx.background_bitmap_mask |= mask;
331 gfx.background_bitmap_mask &= ~mask;
333 if (background_bitmap_tile == NULL) /* empty background requested */
336 if (mask == REDRAW_ALL)
337 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
338 0, 0, video.width, video.height);
339 else if (mask == REDRAW_FIELD)
340 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
341 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
342 else if (mask == REDRAW_DOOR_1)
343 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
344 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
347 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
349 /* remove every mask before setting mask for window */
350 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
351 SetBackgroundBitmap(NULL, 0xffff); /* !!! FIX THIS !!! */
352 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
355 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
357 /* remove window area mask before setting mask for main area */
358 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
359 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
360 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
363 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
365 /* remove window area mask before setting mask for door area */
366 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
367 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
368 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
372 /* ========================================================================= */
373 /* video functions */
374 /* ========================================================================= */
376 inline static int GetRealDepth(int depth)
378 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
381 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
382 int width, int height, Pixel color)
384 SDLFillRectangle(bitmap, x, y, width, height, color);
386 if (bitmap == backbuffer)
387 SetRedrawMaskFromArea(x, y, width, height);
390 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
391 int src_x, int src_y, int width, int height,
392 int dst_x, int dst_y, int mask_mode)
394 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
395 dst_x, dst_y, mask_mode);
397 if (dst_bitmap == backbuffer)
398 SetRedrawMaskFromArea(dst_x, dst_y, width, height);
401 void LimitScreenUpdates(boolean enable)
403 SDLLimitScreenUpdates(enable);
406 void InitVideoDisplay(void)
408 if (program.headless)
411 SDLInitVideoDisplay();
412 #if defined(TARGET_SDL2)
417 void CloseVideoDisplay(void)
419 KeyboardAutoRepeatOn();
421 SDL_QuitSubSystem(SDL_INIT_VIDEO);
424 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
427 video.height = height;
428 video.depth = GetRealDepth(depth);
430 video.screen_width = width;
431 video.screen_height = height;
432 video.screen_xoffset = 0;
433 video.screen_yoffset = 0;
435 video.fullscreen_available = FULLSCREEN_STATUS;
436 video.fullscreen_enabled = FALSE;
438 video.window_scaling_available = WINDOW_SCALING_STATUS;
440 video.frame_delay = 0;
441 video.frame_delay_value = GAME_FRAME_DELAY;
443 video.shifted_up = FALSE;
444 video.shifted_up_pos = 0;
445 video.shifted_up_pos_last = 0;
446 video.shifted_up_delay = 0;
447 video.shifted_up_delay_value = ONE_SECOND_DELAY / 4;
449 SDLInitVideoBuffer(fullscreen);
451 video.initialized = TRUE;
456 inline static void FreeBitmapPointers(Bitmap *bitmap)
461 SDLFreeBitmapPointers(bitmap);
463 checked_free(bitmap->source_filename);
464 bitmap->source_filename = NULL;
467 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
470 if (src_bitmap == NULL || dst_bitmap == NULL)
473 FreeBitmapPointers(dst_bitmap);
475 *dst_bitmap = *src_bitmap;
478 void FreeBitmap(Bitmap *bitmap)
483 FreeBitmapPointers(bitmap);
488 Bitmap *CreateBitmapStruct(void)
490 return checked_calloc(sizeof(Bitmap));
493 Bitmap *CreateBitmap(int width, int height, int depth)
495 Bitmap *new_bitmap = CreateBitmapStruct();
496 int real_width = MAX(1, width); /* prevent zero bitmap width */
497 int real_height = MAX(1, height); /* prevent zero bitmap height */
498 int real_depth = GetRealDepth(depth);
500 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
502 new_bitmap->width = real_width;
503 new_bitmap->height = real_height;
508 void ReCreateBitmap(Bitmap **bitmap, int width, int height)
510 Bitmap *new_bitmap = CreateBitmap(width, height, DEFAULT_DEPTH);
514 *bitmap = new_bitmap;
518 TransferBitmapPointers(new_bitmap, *bitmap);
523 void CloseWindow(DrawWindow *window)
527 void SetRedrawMaskFromArea(int x, int y, int width, int height)
531 int x2 = x + width - 1;
532 int y2 = y + height - 1;
534 if (width == 0 || height == 0)
537 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
538 redraw_mask |= REDRAW_FIELD;
539 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
540 redraw_mask |= REDRAW_DOOR_1;
541 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
542 redraw_mask |= REDRAW_DOOR_2;
543 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
544 redraw_mask |= REDRAW_DOOR_3;
546 redraw_mask = REDRAW_ALL;
549 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
552 if (draw_mask == REDRAW_NONE)
555 if (draw_mask & REDRAW_ALL)
558 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
561 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
564 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
567 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
573 boolean DrawingDeactivated(int x, int y, int width, int height)
575 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
578 boolean DrawingOnBackground(int x, int y)
580 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
581 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
584 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
585 int *width, int *height, boolean is_dest)
587 int clip_x, clip_y, clip_width, clip_height;
589 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
591 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
592 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
593 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
594 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
600 clip_width = bitmap->width;
601 clip_height = bitmap->height;
604 /* skip if rectangle completely outside bitmap */
606 if (*x + *width <= clip_x ||
607 *y + *height <= clip_y ||
608 *x >= clip_x + clip_width ||
609 *y >= clip_y + clip_height)
612 /* clip if rectangle overlaps bitmap */
616 *width -= clip_x - *x;
619 else if (*x + *width > clip_x + clip_width)
621 *width = clip_x + clip_width - *x;
626 *height -= clip_y - *y;
629 else if (*y + *height > clip_y + clip_height)
631 *height = clip_y + clip_height - *y;
637 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
638 int src_x, int src_y, int width, int height,
639 int dst_x, int dst_y)
641 int dst_x_unclipped = dst_x;
642 int dst_y_unclipped = dst_y;
644 if (program.headless)
647 if (src_bitmap == NULL || dst_bitmap == NULL)
650 if (DrawingDeactivated(dst_x, dst_y, width, height))
653 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
654 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
657 /* source x/y might need adjustment if destination x/y was clipped top/left */
658 src_x += dst_x - dst_x_unclipped;
659 src_y += dst_y - dst_y_unclipped;
661 #if defined(TARGET_SDL2)
662 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
663 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
664 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
665 but is already fixed in SVN and should therefore finally be fixed with
666 the next official SDL release, which is probably version 1.2.14.) */
667 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
669 if (src_bitmap == dst_bitmap)
671 /* needed when blitting directly to same bitmap -- should not be needed with
672 recent SDL libraries, but apparently does not work in 1.2.11 directly */
674 static Bitmap *tmp_bitmap = NULL;
675 static int tmp_bitmap_xsize = 0;
676 static int tmp_bitmap_ysize = 0;
678 /* start with largest static bitmaps for initial bitmap size ... */
679 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
681 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
682 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
685 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
686 if (src_bitmap->width > tmp_bitmap_xsize ||
687 src_bitmap->height > tmp_bitmap_ysize)
689 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
690 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
692 FreeBitmap(tmp_bitmap);
697 if (tmp_bitmap == NULL)
698 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
701 sysCopyArea(src_bitmap, tmp_bitmap,
702 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
703 sysCopyArea(tmp_bitmap, dst_bitmap,
704 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
710 sysCopyArea(src_bitmap, dst_bitmap,
711 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
714 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
715 int src_x, int src_y, int src_width, int src_height,
716 int dst_x, int dst_y, int dst_width, int dst_height)
718 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
719 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
720 int dst_xsize = dst_width;
721 int dst_ysize = dst_height;
722 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
723 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
726 for (y = 0; y < src_ysteps; y++)
728 for (x = 0; x < src_xsteps; x++)
730 int draw_x = dst_x + x * src_xsize;
731 int draw_y = dst_y + y * src_ysize;
732 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
733 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
735 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
741 void FadeRectangle(int x, int y, int width, int height,
742 int fade_mode, int fade_delay, int post_delay,
743 void (*draw_border_function)(void))
745 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
746 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
749 SDLFadeRectangle(x, y, width, height,
750 fade_mode, fade_delay, post_delay, draw_border_function);
753 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
756 if (DrawingDeactivated(x, y, width, height))
759 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
762 sysFillRectangle(bitmap, x, y, width, height, color);
765 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
767 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
770 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
771 int width, int height)
773 if (DrawingOnBackground(x, y))
774 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
776 ClearRectangle(bitmap, x, y, width, height);
779 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
780 int src_x, int src_y, int width, int height,
781 int dst_x, int dst_y)
783 if (DrawingDeactivated(dst_x, dst_y, width, height))
786 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
787 dst_x, dst_y, BLIT_MASKED);
790 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
791 int src_x, int src_y, int width, int height,
792 int dst_x, int dst_y)
794 if (DrawingOnBackground(dst_x, dst_y))
796 /* draw background */
797 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
800 /* draw foreground */
801 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
805 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
809 void BlitTexture(Bitmap *bitmap,
810 int src_x, int src_y, int width, int height,
811 int dst_x, int dst_y)
816 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
820 void BlitTextureMasked(Bitmap *bitmap,
821 int src_x, int src_y, int width, int height,
822 int dst_x, int dst_y)
827 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
831 void BlitToScreen(Bitmap *bitmap,
832 int src_x, int src_y, int width, int height,
833 int dst_x, int dst_y)
838 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
839 BlitBitmap(bitmap, gfx.final_screen_bitmap, src_x, src_y,
840 width, height, dst_x, dst_y);
842 BlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y);
845 void BlitToScreenMasked(Bitmap *bitmap,
846 int src_x, int src_y, int width, int height,
847 int dst_x, int dst_y)
852 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
853 BlitBitmapMasked(bitmap, gfx.final_screen_bitmap, src_x, src_y,
854 width, height, dst_x, dst_y);
856 BlitTextureMasked(bitmap, src_x, src_y, width, height, dst_x, dst_y);
859 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
862 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
865 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
868 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
871 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
872 int to_x, int to_y, Pixel pixel, int line_width)
876 for (x = 0; x < line_width; x++)
878 for (y = 0; y < line_width; y++)
880 int dx = x - line_width / 2;
881 int dy = y - line_width / 2;
883 if ((x == 0 && y == 0) ||
884 (x == 0 && y == line_width - 1) ||
885 (x == line_width - 1 && y == 0) ||
886 (x == line_width - 1 && y == line_width - 1))
890 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
895 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
900 for (i = 0; i < num_points - 1; i++)
901 DrawLine(bitmap, points[i].x, points[i].y,
902 points[i + 1].x, points[i + 1].y, pixel, line_width);
905 SDLDrawLines(bitmap->surface, points, num_points, pixel);
909 Pixel GetPixel(Bitmap *bitmap, int x, int y)
911 if (x < 0 || x >= bitmap->width ||
912 y < 0 || y >= bitmap->height)
915 return SDLGetPixel(bitmap, x, y);
918 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
919 unsigned int color_g, unsigned int color_b)
921 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
924 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
926 unsigned int color_r = (color >> 16) & 0xff;
927 unsigned int color_g = (color >> 8) & 0xff;
928 unsigned int color_b = (color >> 0) & 0xff;
930 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
933 void KeyboardAutoRepeatOn(void)
935 #if defined(TARGET_SDL2)
936 keyrepeat_status = TRUE;
938 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
939 SDL_DEFAULT_REPEAT_INTERVAL / 2);
940 SDL_EnableUNICODE(1);
944 void KeyboardAutoRepeatOff(void)
946 #if defined(TARGET_SDL2)
947 keyrepeat_status = FALSE;
949 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
950 SDL_EnableUNICODE(0);
954 boolean SetVideoMode(boolean fullscreen)
956 return SDLSetVideoMode(fullscreen);
959 void SetVideoFrameDelay(unsigned int frame_delay_value)
961 video.frame_delay_value = frame_delay_value;
964 unsigned int GetVideoFrameDelay()
966 return video.frame_delay_value;
969 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
971 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
972 (!fullscreen && video.fullscreen_enabled))
973 fullscreen = SetVideoMode(fullscreen);
978 Bitmap *LoadImage(char *filename)
982 new_bitmap = SDLLoadImage(filename);
985 new_bitmap->source_filename = getStringCopy(filename);
990 Bitmap *LoadCustomImage(char *basename)
992 char *filename = getCustomImageFilename(basename);
995 if (filename == NULL)
996 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
998 if ((new_bitmap = LoadImage(filename)) == NULL)
999 Error(ERR_EXIT, "LoadImage('%s') failed: %s", basename, GetError());
1004 void ReloadCustomImage(Bitmap *bitmap, char *basename)
1006 char *filename = getCustomImageFilename(basename);
1009 if (filename == NULL) /* (should never happen) */
1011 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
1015 if (strEqual(filename, bitmap->source_filename))
1017 /* The old and new image are the same (have the same filename and path).
1018 This usually means that this image does not exist in this graphic set
1019 and a fallback to the existing image is done. */
1024 if ((new_bitmap = LoadImage(filename)) == NULL)
1026 Error(ERR_WARN, "LoadImage('%s') failed: %s", basename, GetError());
1030 if (bitmap->width != new_bitmap->width ||
1031 bitmap->height != new_bitmap->height)
1033 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
1035 FreeBitmap(new_bitmap);
1039 TransferBitmapPointers(new_bitmap, bitmap);
1043 static Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
1045 return SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
1048 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
1050 if (bitmaps[IMG_BITMAP_CUSTOM])
1052 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
1054 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
1057 if (gfx.game_tile_size == gfx.standard_tile_size)
1059 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1064 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
1065 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
1066 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
1068 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
1070 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
1071 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
1074 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1075 int tile_size, boolean create_small_bitmaps)
1077 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1078 Bitmap *tmp_bitmap_final = NULL;
1079 Bitmap *tmp_bitmap_0 = NULL;
1080 Bitmap *tmp_bitmap_1 = NULL;
1081 Bitmap *tmp_bitmap_2 = NULL;
1082 Bitmap *tmp_bitmap_4 = NULL;
1083 Bitmap *tmp_bitmap_8 = NULL;
1084 Bitmap *tmp_bitmap_16 = NULL;
1085 Bitmap *tmp_bitmap_32 = NULL;
1086 int width_final, height_final;
1087 int width_0, height_0;
1088 int width_1, height_1;
1089 int width_2, height_2;
1090 int width_4, height_4;
1091 int width_8, height_8;
1092 int width_16, height_16;
1093 int width_32, height_32;
1094 int old_width, old_height;
1097 print_timestamp_init("CreateScaledBitmaps");
1099 old_width = old_bitmap->width;
1100 old_height = old_bitmap->height;
1102 /* calculate new image dimensions for final image size */
1103 width_final = old_width * zoom_factor;
1104 height_final = old_height * zoom_factor;
1106 /* get image with final size (this might require scaling up) */
1107 /* ("final" size may result in non-standard tile size image) */
1108 if (zoom_factor != 1)
1109 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1111 tmp_bitmap_final = old_bitmap;
1113 UPDATE_BUSY_STATE();
1115 width_0 = width_1 = width_final;
1116 height_0 = height_1 = height_final;
1118 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1120 if (create_small_bitmaps)
1122 /* check if we have a non-gameplay tile size image */
1123 if (tile_size != gfx.game_tile_size)
1125 /* get image with gameplay tile size */
1126 width_0 = width_final * gfx.game_tile_size / tile_size;
1127 height_0 = height_final * gfx.game_tile_size / tile_size;
1129 if (width_0 == old_width)
1130 tmp_bitmap_0 = old_bitmap;
1131 else if (width_0 == width_final)
1132 tmp_bitmap_0 = tmp_bitmap_final;
1134 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1136 UPDATE_BUSY_STATE();
1139 /* check if we have a non-standard tile size image */
1140 if (tile_size != gfx.standard_tile_size)
1142 /* get image with standard tile size */
1143 width_1 = width_final * gfx.standard_tile_size / tile_size;
1144 height_1 = height_final * gfx.standard_tile_size / tile_size;
1146 if (width_1 == old_width)
1147 tmp_bitmap_1 = old_bitmap;
1148 else if (width_1 == width_final)
1149 tmp_bitmap_1 = tmp_bitmap_final;
1150 else if (width_1 == width_0)
1151 tmp_bitmap_1 = tmp_bitmap_0;
1153 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1155 UPDATE_BUSY_STATE();
1158 /* calculate new image dimensions for small images */
1159 width_2 = width_1 / 2;
1160 height_2 = height_1 / 2;
1161 width_4 = width_1 / 4;
1162 height_4 = height_1 / 4;
1163 width_8 = width_1 / 8;
1164 height_8 = height_1 / 8;
1165 width_16 = width_1 / 16;
1166 height_16 = height_1 / 16;
1167 width_32 = width_1 / 32;
1168 height_32 = height_1 / 32;
1170 /* get image with 1/2 of normal size (for use in the level editor) */
1171 if (width_2 == old_width)
1172 tmp_bitmap_2 = old_bitmap;
1174 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1176 UPDATE_BUSY_STATE();
1178 /* get image with 1/4 of normal size (for use in the level editor) */
1179 if (width_4 == old_width)
1180 tmp_bitmap_4 = old_bitmap;
1182 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1184 UPDATE_BUSY_STATE();
1186 /* get image with 1/8 of normal size (for use on the preview screen) */
1187 if (width_8 == old_width)
1188 tmp_bitmap_8 = old_bitmap;
1190 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1192 UPDATE_BUSY_STATE();
1194 /* get image with 1/16 of normal size (for use on the preview screen) */
1195 if (width_16 == old_width)
1196 tmp_bitmap_16 = old_bitmap;
1198 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1200 UPDATE_BUSY_STATE();
1202 /* get image with 1/32 of normal size (for use on the preview screen) */
1203 if (width_32 == old_width)
1204 tmp_bitmap_32 = old_bitmap;
1206 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1208 UPDATE_BUSY_STATE();
1210 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1211 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1212 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1213 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1214 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1215 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1217 if (width_0 != width_1)
1218 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1220 if (bitmaps[IMG_BITMAP_CUSTOM])
1221 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1223 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1225 boolean free_old_bitmap = TRUE;
1227 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1228 if (bitmaps[i] == old_bitmap)
1229 free_old_bitmap = FALSE;
1231 if (free_old_bitmap)
1232 FreeBitmap(old_bitmap);
1236 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1239 UPDATE_BUSY_STATE();
1241 print_timestamp_done("CreateScaledBitmaps");
1244 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1247 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1250 void CreateBitmapTextures(Bitmap **bitmaps)
1252 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1255 void FreeBitmapTextures(Bitmap **bitmaps)
1257 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1260 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1262 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1266 /* ------------------------------------------------------------------------- */
1267 /* mouse pointer functions */
1268 /* ------------------------------------------------------------------------- */
1270 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1272 /* XPM image definitions */
1273 static const char *cursor_image_none[] =
1275 /* width height num_colors chars_per_pixel */
1305 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1306 static const char *cursor_image_dot[] =
1308 /* width height num_colors chars_per_pixel */
1337 static const char **cursor_image_playfield = cursor_image_dot;
1339 /* some people complained about a "white dot" on the screen and thought it
1340 was a graphical error... OK, let's just remove the whole pointer :-) */
1341 static const char **cursor_image_playfield = cursor_image_none;
1344 static const int cursor_bit_order = BIT_ORDER_MSB;
1346 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1348 struct MouseCursorInfo *cursor;
1349 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1350 int header_lines = 4;
1353 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1355 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1358 for (y = 0; y < cursor->width; y++)
1360 for (x = 0; x < cursor->height; x++)
1363 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1368 cursor->data[i] = cursor->mask[i] = 0;
1371 switch (image[header_lines + y][x])
1374 cursor->data[i] |= bit_mask;
1375 cursor->mask[i] |= bit_mask;
1379 cursor->mask[i] |= bit_mask;
1388 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1393 void SetMouseCursor(int mode)
1395 static struct MouseCursorInfo *cursor_none = NULL;
1396 static struct MouseCursorInfo *cursor_playfield = NULL;
1397 struct MouseCursorInfo *cursor_new;
1399 if (cursor_none == NULL)
1400 cursor_none = get_cursor_from_image(cursor_image_none);
1402 if (cursor_playfield == NULL)
1403 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1405 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1406 mode == CURSOR_NONE ? cursor_none :
1407 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1409 SDLSetMouseCursor(cursor_new);
1411 gfx.cursor_mode = mode;
1415 /* ========================================================================= */
1416 /* audio functions */
1417 /* ========================================================================= */
1419 void OpenAudio(void)
1421 /* always start with reliable default values */
1422 audio.sound_available = FALSE;
1423 audio.music_available = FALSE;
1424 audio.loops_available = FALSE;
1426 audio.sound_enabled = FALSE;
1427 audio.sound_deactivated = FALSE;
1429 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1430 audio.mixer_pid = 0;
1431 audio.device_name = NULL;
1432 audio.device_fd = -1;
1434 audio.num_channels = 0;
1435 audio.music_channel = 0;
1436 audio.first_sound_channel = 0;
1441 void CloseAudio(void)
1445 audio.sound_enabled = FALSE;
1448 void SetAudioMode(boolean enabled)
1450 if (!audio.sound_available)
1453 audio.sound_enabled = enabled;
1457 /* ========================================================================= */
1458 /* event functions */
1459 /* ========================================================================= */
1461 boolean PendingEvent(void)
1463 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1466 void NextEvent(Event *event)
1468 SDLNextEvent(event);
1471 void PeekEvent(Event *event)
1473 #if defined(TARGET_SDL2)
1474 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1476 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1480 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1482 #if defined(TARGET_SDL2)
1483 /* key up/down events in SDL2 do not return text characters anymore */
1484 return event->keysym.sym;
1487 #if ENABLE_UNUSED_CODE
1488 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1489 (int)event->keysym.unicode,
1490 (int)event->keysym.sym,
1491 (int)SDL_GetModState());
1494 if (with_modifiers &&
1495 event->keysym.unicode > 0x0000 &&
1496 event->keysym.unicode < 0x2000)
1497 return event->keysym.unicode;
1499 return event->keysym.sym;
1504 KeyMod HandleKeyModState(Key key, int key_status)
1506 static KeyMod current_modifiers = KMOD_None;
1508 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1510 KeyMod new_modifier = KMOD_None;
1515 new_modifier = KMOD_Shift_L;
1518 new_modifier = KMOD_Shift_R;
1520 case KSYM_Control_L:
1521 new_modifier = KMOD_Control_L;
1523 case KSYM_Control_R:
1524 new_modifier = KMOD_Control_R;
1527 new_modifier = KMOD_Meta_L;
1530 new_modifier = KMOD_Meta_R;
1533 new_modifier = KMOD_Alt_L;
1536 new_modifier = KMOD_Alt_R;
1542 if (key_status == KEY_PRESSED)
1543 current_modifiers |= new_modifier;
1545 current_modifiers &= ~new_modifier;
1548 return current_modifiers;
1551 KeyMod GetKeyModState()
1553 return (KeyMod)SDL_GetModState();
1556 KeyMod GetKeyModStateFromEvents()
1558 /* always use key modifier state as tracked from key events (this is needed
1559 if the modifier key event was injected into the event queue, but the key
1560 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1561 query the keys as held pressed on the keyboard) -- this case is currently
1562 only used to filter out clipboard insert events from "True X-Mouse" tool */
1564 return HandleKeyModState(KSYM_UNDEFINED, 0);
1567 void StartTextInput(int x, int y, int width, int height)
1569 #if defined(TARGET_SDL2)
1570 SDL_StartTextInput();
1572 #if defined(HAS_SCREEN_KEYBOARD)
1573 if (y + height > SCREEN_KEYBOARD_POS(video.height))
1575 video.shifted_up_pos = y + height - SCREEN_KEYBOARD_POS(video.height);
1576 video.shifted_up_delay = SDL_GetTicks();
1577 video.shifted_up = TRUE;
1583 void StopTextInput()
1585 #if defined(TARGET_SDL2)
1586 SDL_StopTextInput();
1588 #if defined(HAS_SCREEN_KEYBOARD)
1589 if (video.shifted_up)
1591 video.shifted_up_pos = 0;
1592 video.shifted_up_delay = SDL_GetTicks();
1593 video.shifted_up = FALSE;
1599 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1601 if (event->type != EVENT_CLIENTMESSAGE)
1604 return TRUE; /* the only possible message here is SDL_QUIT */
1608 /* ========================================================================= */
1609 /* joystick functions */
1610 /* ========================================================================= */
1612 void InitJoysticks()
1616 #if defined(NO_JOYSTICK)
1617 return; /* joysticks generally deactivated by compile-time directive */
1620 /* always start with reliable default values */
1621 joystick.status = JOYSTICK_NOT_AVAILABLE;
1622 for (i = 0; i < MAX_PLAYERS; i++)
1623 joystick.nr[i] = -1; /* no joystick configured */
1628 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1630 return SDLReadJoystick(nr, x, y, b1, b2);
1633 boolean CheckJoystickOpened(int nr)
1635 return SDLCheckJoystickOpened(nr);