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;
110 free(global_scores_dir);
113 void SetWindowTitle()
115 program.window_title = program.window_title_function();
120 void InitWindowTitleFunction(char *(*window_title_function)(void))
122 program.window_title_function = window_title_function;
125 void InitExitMessageFunction(void (*exit_message_function)(char *, va_list))
127 program.exit_message_function = exit_message_function;
130 void InitExitFunction(void (*exit_function)(int))
132 program.exit_function = exit_function;
134 /* set signal handlers to custom exit function */
135 // signal(SIGINT, exit_function);
136 signal(SIGTERM, exit_function);
138 /* set exit function to automatically cleanup SDL stuff after exit() */
142 void InitPlatformDependentStuff(void)
144 // this is initialized in GetOptions(), but may already be used before
145 options.verbose = TRUE;
149 #if defined(TARGET_SDL2)
150 int sdl_init_flags = SDL_INIT_EVENTS | SDL_INIT_NOPARACHUTE;
152 int sdl_init_flags = SDL_INIT_EVENTTHREAD | SDL_INIT_NOPARACHUTE;
155 if (SDL_Init(sdl_init_flags) < 0)
156 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
161 void ClosePlatformDependentStuff(void)
166 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
167 int real_sx, int real_sy,
168 int full_sxsize, int full_sysize,
169 Bitmap *field_save_buffer)
175 gfx.real_sx = real_sx;
176 gfx.real_sy = real_sy;
177 gfx.full_sxsize = full_sxsize;
178 gfx.full_sysize = full_sysize;
180 gfx.field_save_buffer = field_save_buffer;
182 SetDrawDeactivationMask(REDRAW_NONE); /* do not deactivate drawing */
183 SetDrawBackgroundMask(REDRAW_NONE); /* deactivate masked drawing */
186 void InitGfxTileSizeInfo(int game_tile_size, int standard_tile_size)
188 gfx.game_tile_size = game_tile_size;
189 gfx.standard_tile_size = standard_tile_size;
192 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
200 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
208 void InitGfxDoor3Info(int ex, int ey, int exsize, int eysize)
216 void InitGfxWindowInfo(int win_xsize, int win_ysize)
218 if (win_xsize != gfx.win_xsize || win_ysize != gfx.win_ysize)
220 ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize);
222 #if defined(TARGET_SDL2)
223 ReCreateBitmap(&gfx.final_screen_bitmap, win_xsize, win_ysize);
226 ReCreateBitmap(&gfx.fade_bitmap_backup, win_xsize, win_ysize);
227 ReCreateBitmap(&gfx.fade_bitmap_source, win_xsize, win_ysize);
228 ReCreateBitmap(&gfx.fade_bitmap_target, win_xsize, win_ysize);
229 ReCreateBitmap(&gfx.fade_bitmap_black, win_xsize, win_ysize);
231 ClearRectangle(gfx.fade_bitmap_black, 0, 0, win_xsize, win_ysize);
234 gfx.win_xsize = win_xsize;
235 gfx.win_ysize = win_ysize;
237 gfx.background_bitmap_mask = REDRAW_NONE;
240 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
242 /* currently only used by MSDOS code to alloc VRAM buffer, if available */
243 /* 2009-03-24: also (temporarily?) used for overlapping blit workaround */
244 gfx.scrollbuffer_width = scrollbuffer_width;
245 gfx.scrollbuffer_height = scrollbuffer_height;
248 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
250 gfx.clipping_enabled = enabled;
253 gfx.clip_width = width;
254 gfx.clip_height = height;
257 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
259 gfx.draw_busy_anim_function = draw_busy_anim_function;
262 void InitGfxDrawGlobalAnimFunction(void (*draw_global_anim_function)(int, int))
264 gfx.draw_global_anim_function = draw_global_anim_function;
267 void InitGfxDrawGlobalBorderFunction(void (*draw_global_border_function)(int))
269 gfx.draw_global_border_function = draw_global_border_function;
272 void InitGfxCustomArtworkInfo()
274 gfx.override_level_graphics = FALSE;
275 gfx.override_level_sounds = FALSE;
276 gfx.override_level_music = FALSE;
278 gfx.draw_init_text = TRUE;
281 void InitGfxOtherSettings()
283 gfx.cursor_mode = CURSOR_DEFAULT;
286 void InitOverlayInfo()
288 overlay.active = FALSE;
291 void SetOverlayActive(boolean active)
293 overlay.active = active;
296 boolean GetOverlayActive()
298 return overlay.active;
301 void SetDrawDeactivationMask(int draw_deactivation_mask)
303 gfx.draw_deactivation_mask = draw_deactivation_mask;
306 void SetDrawBackgroundMask(int draw_background_mask)
308 gfx.draw_background_mask = draw_background_mask;
311 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
313 if (background_bitmap_tile != NULL)
314 gfx.background_bitmap_mask |= mask;
316 gfx.background_bitmap_mask &= ~mask;
318 if (background_bitmap_tile == NULL) /* empty background requested */
321 if (mask == REDRAW_ALL)
322 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
323 0, 0, video.width, video.height);
324 else if (mask == REDRAW_FIELD)
325 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
326 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
327 else if (mask == REDRAW_DOOR_1)
328 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
329 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
332 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
334 /* remove every mask before setting mask for window */
335 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
336 SetBackgroundBitmap(NULL, 0xffff); /* !!! FIX THIS !!! */
337 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
340 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
342 /* remove window area mask before setting mask for main area */
343 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
344 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
345 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
348 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
350 /* remove window area mask before setting mask for door area */
351 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
352 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
353 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
357 /* ========================================================================= */
358 /* video functions */
359 /* ========================================================================= */
361 inline static int GetRealDepth(int depth)
363 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
366 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
367 int width, int height, Pixel color)
369 SDLFillRectangle(bitmap, x, y, width, height, color);
371 if (bitmap == backbuffer)
372 SetRedrawMaskFromArea(x, y, width, height);
375 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
376 int src_x, int src_y, int width, int height,
377 int dst_x, int dst_y, int mask_mode)
379 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
380 dst_x, dst_y, mask_mode);
382 if (dst_bitmap == backbuffer)
383 SetRedrawMaskFromArea(dst_x, dst_y, width, height);
386 void LimitScreenUpdates(boolean enable)
388 SDLLimitScreenUpdates(enable);
391 void InitVideoDisplay(void)
393 if (program.headless)
396 SDLInitVideoDisplay();
397 #if defined(TARGET_SDL2)
402 void CloseVideoDisplay(void)
404 KeyboardAutoRepeatOn();
406 SDL_QuitSubSystem(SDL_INIT_VIDEO);
409 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
412 video.height = height;
413 video.depth = GetRealDepth(depth);
415 video.screen_width = width;
416 video.screen_height = height;
417 video.screen_xoffset = 0;
418 video.screen_yoffset = 0;
420 video.fullscreen_available = FULLSCREEN_STATUS;
421 video.fullscreen_enabled = FALSE;
423 video.window_scaling_available = WINDOW_SCALING_STATUS;
425 video.frame_delay = 0;
426 video.frame_delay_value = GAME_FRAME_DELAY;
428 video.shifted_up = FALSE;
429 video.shifted_up_pos = 0;
430 video.shifted_up_pos_last = 0;
431 video.shifted_up_delay = 0;
432 video.shifted_up_delay_value = ONE_SECOND_DELAY / 4;
434 SDLInitVideoBuffer(fullscreen);
436 video.initialized = TRUE;
441 inline static void FreeBitmapPointers(Bitmap *bitmap)
446 SDLFreeBitmapPointers(bitmap);
448 checked_free(bitmap->source_filename);
449 bitmap->source_filename = NULL;
452 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
455 if (src_bitmap == NULL || dst_bitmap == NULL)
458 FreeBitmapPointers(dst_bitmap);
460 *dst_bitmap = *src_bitmap;
463 void FreeBitmap(Bitmap *bitmap)
468 FreeBitmapPointers(bitmap);
473 Bitmap *CreateBitmapStruct(void)
475 return checked_calloc(sizeof(Bitmap));
478 Bitmap *CreateBitmap(int width, int height, int depth)
480 Bitmap *new_bitmap = CreateBitmapStruct();
481 int real_width = MAX(1, width); /* prevent zero bitmap width */
482 int real_height = MAX(1, height); /* prevent zero bitmap height */
483 int real_depth = GetRealDepth(depth);
485 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
487 new_bitmap->width = real_width;
488 new_bitmap->height = real_height;
493 void ReCreateBitmap(Bitmap **bitmap, int width, int height)
495 Bitmap *new_bitmap = CreateBitmap(width, height, DEFAULT_DEPTH);
499 *bitmap = new_bitmap;
503 TransferBitmapPointers(new_bitmap, *bitmap);
508 void CloseWindow(DrawWindow *window)
512 void SetRedrawMaskFromArea(int x, int y, int width, int height)
516 int x2 = x + width - 1;
517 int y2 = y + height - 1;
519 if (width == 0 || height == 0)
522 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
523 redraw_mask |= REDRAW_FIELD;
524 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
525 redraw_mask |= REDRAW_DOOR_1;
526 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
527 redraw_mask |= REDRAW_DOOR_2;
528 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
529 redraw_mask |= REDRAW_DOOR_3;
531 redraw_mask = REDRAW_ALL;
534 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
537 if (draw_mask == REDRAW_NONE)
540 if (draw_mask & REDRAW_ALL)
543 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
546 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
549 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
552 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
558 boolean DrawingDeactivated(int x, int y, int width, int height)
560 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
563 boolean DrawingOnBackground(int x, int y)
565 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
566 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
569 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
570 int *width, int *height, boolean is_dest)
572 int clip_x, clip_y, clip_width, clip_height;
574 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
576 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
577 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
578 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
579 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
585 clip_width = bitmap->width;
586 clip_height = bitmap->height;
589 /* skip if rectangle completely outside bitmap */
591 if (*x + *width <= clip_x ||
592 *y + *height <= clip_y ||
593 *x >= clip_x + clip_width ||
594 *y >= clip_y + clip_height)
597 /* clip if rectangle overlaps bitmap */
601 *width -= clip_x - *x;
604 else if (*x + *width > clip_x + clip_width)
606 *width = clip_x + clip_width - *x;
611 *height -= clip_y - *y;
614 else if (*y + *height > clip_y + clip_height)
616 *height = clip_y + clip_height - *y;
622 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
623 int src_x, int src_y, int width, int height,
624 int dst_x, int dst_y)
626 int dst_x_unclipped = dst_x;
627 int dst_y_unclipped = dst_y;
629 if (program.headless)
632 if (src_bitmap == NULL || dst_bitmap == NULL)
635 if (DrawingDeactivated(dst_x, dst_y, width, height))
638 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
639 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
642 /* source x/y might need adjustment if destination x/y was clipped top/left */
643 src_x += dst_x - dst_x_unclipped;
644 src_y += dst_y - dst_y_unclipped;
646 #if defined(TARGET_SDL2)
647 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
648 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
649 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
650 but is already fixed in SVN and should therefore finally be fixed with
651 the next official SDL release, which is probably version 1.2.14.) */
652 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
654 if (src_bitmap == dst_bitmap)
656 /* needed when blitting directly to same bitmap -- should not be needed with
657 recent SDL libraries, but apparently does not work in 1.2.11 directly */
659 static Bitmap *tmp_bitmap = NULL;
660 static int tmp_bitmap_xsize = 0;
661 static int tmp_bitmap_ysize = 0;
663 /* start with largest static bitmaps for initial bitmap size ... */
664 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
666 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
667 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
670 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
671 if (src_bitmap->width > tmp_bitmap_xsize ||
672 src_bitmap->height > tmp_bitmap_ysize)
674 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
675 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
677 FreeBitmap(tmp_bitmap);
682 if (tmp_bitmap == NULL)
683 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
686 sysCopyArea(src_bitmap, tmp_bitmap,
687 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
688 sysCopyArea(tmp_bitmap, dst_bitmap,
689 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
695 sysCopyArea(src_bitmap, dst_bitmap,
696 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
699 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
700 int src_x, int src_y, int src_width, int src_height,
701 int dst_x, int dst_y, int dst_width, int dst_height)
703 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
704 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
705 int dst_xsize = dst_width;
706 int dst_ysize = dst_height;
707 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
708 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
711 for (y = 0; y < src_ysteps; y++)
713 for (x = 0; x < src_xsteps; x++)
715 int draw_x = dst_x + x * src_xsize;
716 int draw_y = dst_y + y * src_ysize;
717 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
718 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
720 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
726 void FadeRectangle(int x, int y, int width, int height,
727 int fade_mode, int fade_delay, int post_delay,
728 void (*draw_border_function)(void))
730 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
731 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
734 SDLFadeRectangle(x, y, width, height,
735 fade_mode, fade_delay, post_delay, draw_border_function);
738 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
741 if (DrawingDeactivated(x, y, width, height))
744 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
747 sysFillRectangle(bitmap, x, y, width, height, color);
750 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
752 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
755 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
756 int width, int height)
758 if (DrawingOnBackground(x, y))
759 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
761 ClearRectangle(bitmap, x, y, width, height);
764 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
765 int src_x, int src_y, int width, int height,
766 int dst_x, int dst_y)
768 if (DrawingDeactivated(dst_x, dst_y, width, height))
771 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
772 dst_x, dst_y, BLIT_MASKED);
775 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
776 int src_x, int src_y, int width, int height,
777 int dst_x, int dst_y)
779 if (DrawingOnBackground(dst_x, dst_y))
781 /* draw background */
782 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
785 /* draw foreground */
786 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
790 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
794 void BlitTexture(Bitmap *bitmap,
795 int src_x, int src_y, int width, int height,
796 int dst_x, int dst_y)
801 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
805 void BlitTextureMasked(Bitmap *bitmap,
806 int src_x, int src_y, int width, int height,
807 int dst_x, int dst_y)
812 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
816 void BlitToScreen(Bitmap *bitmap,
817 int src_x, int src_y, int width, int height,
818 int dst_x, int dst_y)
823 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
824 BlitBitmap(bitmap, gfx.final_screen_bitmap, src_x, src_y,
825 width, height, dst_x, dst_y);
827 BlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y);
830 void BlitToScreenMasked(Bitmap *bitmap,
831 int src_x, int src_y, int width, int height,
832 int dst_x, int dst_y)
837 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
838 BlitBitmapMasked(bitmap, gfx.final_screen_bitmap, src_x, src_y,
839 width, height, dst_x, dst_y);
841 BlitTextureMasked(bitmap, src_x, src_y, width, height, dst_x, dst_y);
844 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
847 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
850 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
853 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
856 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
857 int to_x, int to_y, Pixel pixel, int line_width)
861 for (x = 0; x < line_width; x++)
863 for (y = 0; y < line_width; y++)
865 int dx = x - line_width / 2;
866 int dy = y - line_width / 2;
868 if ((x == 0 && y == 0) ||
869 (x == 0 && y == line_width - 1) ||
870 (x == line_width - 1 && y == 0) ||
871 (x == line_width - 1 && y == line_width - 1))
875 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
880 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
885 for (i = 0; i < num_points - 1; i++)
886 DrawLine(bitmap, points[i].x, points[i].y,
887 points[i + 1].x, points[i + 1].y, pixel, line_width);
890 SDLDrawLines(bitmap->surface, points, num_points, pixel);
894 Pixel GetPixel(Bitmap *bitmap, int x, int y)
896 if (x < 0 || x >= bitmap->width ||
897 y < 0 || y >= bitmap->height)
900 return SDLGetPixel(bitmap, x, y);
903 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
904 unsigned int color_g, unsigned int color_b)
906 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
909 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
911 unsigned int color_r = (color >> 16) & 0xff;
912 unsigned int color_g = (color >> 8) & 0xff;
913 unsigned int color_b = (color >> 0) & 0xff;
915 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
918 void KeyboardAutoRepeatOn(void)
920 #if defined(TARGET_SDL2)
921 keyrepeat_status = TRUE;
923 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
924 SDL_DEFAULT_REPEAT_INTERVAL / 2);
925 SDL_EnableUNICODE(1);
929 void KeyboardAutoRepeatOff(void)
931 #if defined(TARGET_SDL2)
932 keyrepeat_status = FALSE;
934 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
935 SDL_EnableUNICODE(0);
939 boolean SetVideoMode(boolean fullscreen)
941 return SDLSetVideoMode(fullscreen);
944 void SetVideoFrameDelay(unsigned int frame_delay_value)
946 video.frame_delay_value = frame_delay_value;
949 unsigned int GetVideoFrameDelay()
951 return video.frame_delay_value;
954 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
956 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
957 (!fullscreen && video.fullscreen_enabled))
958 fullscreen = SetVideoMode(fullscreen);
963 Bitmap *LoadImage(char *filename)
967 new_bitmap = SDLLoadImage(filename);
970 new_bitmap->source_filename = getStringCopy(filename);
975 Bitmap *LoadCustomImage(char *basename)
977 char *filename = getCustomImageFilename(basename);
980 if (filename == NULL)
981 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
983 if ((new_bitmap = LoadImage(filename)) == NULL)
984 Error(ERR_EXIT, "LoadImage('%s') failed: %s", basename, GetError());
989 void ReloadCustomImage(Bitmap *bitmap, char *basename)
991 char *filename = getCustomImageFilename(basename);
994 if (filename == NULL) /* (should never happen) */
996 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
1000 if (strEqual(filename, bitmap->source_filename))
1002 /* The old and new image are the same (have the same filename and path).
1003 This usually means that this image does not exist in this graphic set
1004 and a fallback to the existing image is done. */
1009 if ((new_bitmap = LoadImage(filename)) == NULL)
1011 Error(ERR_WARN, "LoadImage('%s') failed: %s", basename, GetError());
1015 if (bitmap->width != new_bitmap->width ||
1016 bitmap->height != new_bitmap->height)
1018 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
1020 FreeBitmap(new_bitmap);
1024 TransferBitmapPointers(new_bitmap, bitmap);
1028 static Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
1030 return SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
1033 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
1035 if (bitmaps[IMG_BITMAP_CUSTOM])
1037 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
1039 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
1042 if (gfx.game_tile_size == gfx.standard_tile_size)
1044 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1049 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
1050 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
1051 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
1053 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
1055 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
1056 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
1059 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1060 int tile_size, boolean create_small_bitmaps)
1062 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1063 Bitmap *tmp_bitmap_final = NULL;
1064 Bitmap *tmp_bitmap_0 = NULL;
1065 Bitmap *tmp_bitmap_1 = NULL;
1066 Bitmap *tmp_bitmap_2 = NULL;
1067 Bitmap *tmp_bitmap_4 = NULL;
1068 Bitmap *tmp_bitmap_8 = NULL;
1069 Bitmap *tmp_bitmap_16 = NULL;
1070 Bitmap *tmp_bitmap_32 = NULL;
1071 int width_final, height_final;
1072 int width_0, height_0;
1073 int width_1, height_1;
1074 int width_2, height_2;
1075 int width_4, height_4;
1076 int width_8, height_8;
1077 int width_16, height_16;
1078 int width_32, height_32;
1079 int old_width, old_height;
1082 print_timestamp_init("CreateScaledBitmaps");
1084 old_width = old_bitmap->width;
1085 old_height = old_bitmap->height;
1087 /* calculate new image dimensions for final image size */
1088 width_final = old_width * zoom_factor;
1089 height_final = old_height * zoom_factor;
1091 /* get image with final size (this might require scaling up) */
1092 /* ("final" size may result in non-standard tile size image) */
1093 if (zoom_factor != 1)
1094 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1096 tmp_bitmap_final = old_bitmap;
1098 UPDATE_BUSY_STATE();
1100 width_0 = width_1 = width_final;
1101 height_0 = height_1 = height_final;
1103 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1105 if (create_small_bitmaps)
1107 /* check if we have a non-gameplay tile size image */
1108 if (tile_size != gfx.game_tile_size)
1110 /* get image with gameplay tile size */
1111 width_0 = width_final * gfx.game_tile_size / tile_size;
1112 height_0 = height_final * gfx.game_tile_size / tile_size;
1114 if (width_0 == old_width)
1115 tmp_bitmap_0 = old_bitmap;
1116 else if (width_0 == width_final)
1117 tmp_bitmap_0 = tmp_bitmap_final;
1119 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1121 UPDATE_BUSY_STATE();
1124 /* check if we have a non-standard tile size image */
1125 if (tile_size != gfx.standard_tile_size)
1127 /* get image with standard tile size */
1128 width_1 = width_final * gfx.standard_tile_size / tile_size;
1129 height_1 = height_final * gfx.standard_tile_size / tile_size;
1131 if (width_1 == old_width)
1132 tmp_bitmap_1 = old_bitmap;
1133 else if (width_1 == width_final)
1134 tmp_bitmap_1 = tmp_bitmap_final;
1135 else if (width_1 == width_0)
1136 tmp_bitmap_1 = tmp_bitmap_0;
1138 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1140 UPDATE_BUSY_STATE();
1143 /* calculate new image dimensions for small images */
1144 width_2 = width_1 / 2;
1145 height_2 = height_1 / 2;
1146 width_4 = width_1 / 4;
1147 height_4 = height_1 / 4;
1148 width_8 = width_1 / 8;
1149 height_8 = height_1 / 8;
1150 width_16 = width_1 / 16;
1151 height_16 = height_1 / 16;
1152 width_32 = width_1 / 32;
1153 height_32 = height_1 / 32;
1155 /* get image with 1/2 of normal size (for use in the level editor) */
1156 if (width_2 == old_width)
1157 tmp_bitmap_2 = old_bitmap;
1159 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1161 UPDATE_BUSY_STATE();
1163 /* get image with 1/4 of normal size (for use in the level editor) */
1164 if (width_4 == old_width)
1165 tmp_bitmap_4 = old_bitmap;
1167 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1169 UPDATE_BUSY_STATE();
1171 /* get image with 1/8 of normal size (for use on the preview screen) */
1172 if (width_8 == old_width)
1173 tmp_bitmap_8 = old_bitmap;
1175 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1177 UPDATE_BUSY_STATE();
1179 /* get image with 1/16 of normal size (for use on the preview screen) */
1180 if (width_16 == old_width)
1181 tmp_bitmap_16 = old_bitmap;
1183 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1185 UPDATE_BUSY_STATE();
1187 /* get image with 1/32 of normal size (for use on the preview screen) */
1188 if (width_32 == old_width)
1189 tmp_bitmap_32 = old_bitmap;
1191 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1193 UPDATE_BUSY_STATE();
1195 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1196 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1197 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1198 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1199 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1200 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1202 if (width_0 != width_1)
1203 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1205 if (bitmaps[IMG_BITMAP_CUSTOM])
1206 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1208 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1210 boolean free_old_bitmap = TRUE;
1212 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1213 if (bitmaps[i] == old_bitmap)
1214 free_old_bitmap = FALSE;
1216 if (free_old_bitmap)
1217 FreeBitmap(old_bitmap);
1221 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1224 UPDATE_BUSY_STATE();
1226 print_timestamp_done("CreateScaledBitmaps");
1229 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1232 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1235 void CreateBitmapTextures(Bitmap **bitmaps)
1237 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1240 void FreeBitmapTextures(Bitmap **bitmaps)
1242 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1245 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1247 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1251 /* ------------------------------------------------------------------------- */
1252 /* mouse pointer functions */
1253 /* ------------------------------------------------------------------------- */
1255 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1257 /* XPM image definitions */
1258 static const char *cursor_image_none[] =
1260 /* width height num_colors chars_per_pixel */
1290 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1291 static const char *cursor_image_dot[] =
1293 /* width height num_colors chars_per_pixel */
1322 static const char **cursor_image_playfield = cursor_image_dot;
1324 /* some people complained about a "white dot" on the screen and thought it
1325 was a graphical error... OK, let's just remove the whole pointer :-) */
1326 static const char **cursor_image_playfield = cursor_image_none;
1329 static const int cursor_bit_order = BIT_ORDER_MSB;
1331 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1333 struct MouseCursorInfo *cursor;
1334 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1335 int header_lines = 4;
1338 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1340 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1343 for (y = 0; y < cursor->width; y++)
1345 for (x = 0; x < cursor->height; x++)
1348 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1353 cursor->data[i] = cursor->mask[i] = 0;
1356 switch (image[header_lines + y][x])
1359 cursor->data[i] |= bit_mask;
1360 cursor->mask[i] |= bit_mask;
1364 cursor->mask[i] |= bit_mask;
1373 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1378 void SetMouseCursor(int mode)
1380 static struct MouseCursorInfo *cursor_none = NULL;
1381 static struct MouseCursorInfo *cursor_playfield = NULL;
1382 struct MouseCursorInfo *cursor_new;
1384 if (cursor_none == NULL)
1385 cursor_none = get_cursor_from_image(cursor_image_none);
1387 if (cursor_playfield == NULL)
1388 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1390 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1391 mode == CURSOR_NONE ? cursor_none :
1392 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1394 SDLSetMouseCursor(cursor_new);
1396 gfx.cursor_mode = mode;
1400 /* ========================================================================= */
1401 /* audio functions */
1402 /* ========================================================================= */
1404 void OpenAudio(void)
1406 /* always start with reliable default values */
1407 audio.sound_available = FALSE;
1408 audio.music_available = FALSE;
1409 audio.loops_available = FALSE;
1411 audio.sound_enabled = FALSE;
1412 audio.sound_deactivated = FALSE;
1414 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1415 audio.mixer_pid = 0;
1416 audio.device_name = NULL;
1417 audio.device_fd = -1;
1419 audio.num_channels = 0;
1420 audio.music_channel = 0;
1421 audio.first_sound_channel = 0;
1426 void CloseAudio(void)
1430 audio.sound_enabled = FALSE;
1433 void SetAudioMode(boolean enabled)
1435 if (!audio.sound_available)
1438 audio.sound_enabled = enabled;
1442 /* ========================================================================= */
1443 /* event functions */
1444 /* ========================================================================= */
1446 boolean PendingEvent(void)
1448 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1451 void NextEvent(Event *event)
1453 SDLNextEvent(event);
1456 void PeekEvent(Event *event)
1458 #if defined(TARGET_SDL2)
1459 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1461 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1465 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1467 #if defined(TARGET_SDL2)
1468 /* key up/down events in SDL2 do not return text characters anymore */
1469 return event->keysym.sym;
1472 #if ENABLE_UNUSED_CODE
1473 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1474 (int)event->keysym.unicode,
1475 (int)event->keysym.sym,
1476 (int)SDL_GetModState());
1479 if (with_modifiers &&
1480 event->keysym.unicode > 0x0000 &&
1481 event->keysym.unicode < 0x2000)
1482 return event->keysym.unicode;
1484 return event->keysym.sym;
1489 KeyMod HandleKeyModState(Key key, int key_status)
1491 static KeyMod current_modifiers = KMOD_None;
1493 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1495 KeyMod new_modifier = KMOD_None;
1500 new_modifier = KMOD_Shift_L;
1503 new_modifier = KMOD_Shift_R;
1505 case KSYM_Control_L:
1506 new_modifier = KMOD_Control_L;
1508 case KSYM_Control_R:
1509 new_modifier = KMOD_Control_R;
1512 new_modifier = KMOD_Meta_L;
1515 new_modifier = KMOD_Meta_R;
1518 new_modifier = KMOD_Alt_L;
1521 new_modifier = KMOD_Alt_R;
1527 if (key_status == KEY_PRESSED)
1528 current_modifiers |= new_modifier;
1530 current_modifiers &= ~new_modifier;
1533 return current_modifiers;
1536 KeyMod GetKeyModState()
1538 return (KeyMod)SDL_GetModState();
1541 KeyMod GetKeyModStateFromEvents()
1543 /* always use key modifier state as tracked from key events (this is needed
1544 if the modifier key event was injected into the event queue, but the key
1545 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1546 query the keys as held pressed on the keyboard) -- this case is currently
1547 only used to filter out clipboard insert events from "True X-Mouse" tool */
1549 return HandleKeyModState(KSYM_UNDEFINED, 0);
1552 void StartTextInput(int x, int y, int width, int height)
1554 #if defined(TARGET_SDL2)
1555 SDL_StartTextInput();
1557 #if defined(HAS_SCREEN_KEYBOARD)
1558 if (y + height > SCREEN_KEYBOARD_POS(video.height))
1560 video.shifted_up_pos = y + height - SCREEN_KEYBOARD_POS(video.height);
1561 video.shifted_up_delay = SDL_GetTicks();
1562 video.shifted_up = TRUE;
1568 void StopTextInput()
1570 #if defined(TARGET_SDL2)
1571 SDL_StopTextInput();
1573 #if defined(HAS_SCREEN_KEYBOARD)
1574 if (video.shifted_up)
1576 video.shifted_up_pos = 0;
1577 video.shifted_up_delay = SDL_GetTicks();
1578 video.shifted_up = FALSE;
1584 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1586 if (event->type != EVENT_CLIENTMESSAGE)
1589 return TRUE; /* the only possible message here is SDL_QUIT */
1593 /* ========================================================================= */
1594 /* joystick functions */
1595 /* ========================================================================= */
1597 void InitJoysticks()
1601 #if defined(NO_JOYSTICK)
1602 return; /* joysticks generally deactivated by compile-time directive */
1605 /* always start with reliable default values */
1606 joystick.status = JOYSTICK_NOT_AVAILABLE;
1607 for (i = 0; i < MAX_PLAYERS; i++)
1608 joystick.fd[i] = -1; /* joystick device closed */
1613 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1615 return SDLReadJoystick(nr, x, y, b1, b2);