1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
24 #define ENABLE_UNUSED_CODE 0 /* currently unused functions */
27 /* ========================================================================= */
28 /* exported variables */
29 /* ========================================================================= */
31 struct ProgramInfo program;
32 struct OptionInfo options;
33 struct VideoSystemInfo video;
34 struct AudioSystemInfo audio;
36 struct OverlayInfo overlay;
37 struct ArtworkInfo artwork;
38 struct JoystickInfo joystick;
39 struct SetupInfo setup;
41 LevelDirTree *leveldir_first_all = NULL;
42 LevelDirTree *leveldir_first = NULL;
43 LevelDirTree *leveldir_current = NULL;
46 struct LevelStats level_stats[MAX_LEVELS];
48 DrawWindow *window = NULL;
49 DrawBuffer *backbuffer = NULL;
50 DrawBuffer *drawto = NULL;
52 int button_status = MB_NOT_PRESSED;
53 boolean motion_status = FALSE;
54 int wheel_steps = DEFAULT_WHEEL_STEPS;
55 #if defined(TARGET_SDL2)
56 boolean keyrepeat_status = TRUE;
59 int redraw_mask = REDRAW_NONE;
64 /* ========================================================================= */
65 /* init/close functions */
66 /* ========================================================================= */
68 void InitProgramInfo(char *argv0, char *config_filename, char *userdata_subdir,
69 char *program_title, char *icon_title,
70 char *icon_filename, char *cookie_prefix,
73 program.command_basepath = getBasePath(argv0);
74 program.command_basename = getBaseName(argv0);
76 program.config_filename = config_filename;
78 program.userdata_subdir = userdata_subdir;
79 program.userdata_path = getUserGameDataDir();
81 program.program_title = program_title;
82 program.window_title = "(undefined)";
83 program.icon_title = icon_title;
85 program.icon_filename = icon_filename;
87 program.cookie_prefix = cookie_prefix;
89 program.version_major = VERSION_MAJOR(program_version);
90 program.version_minor = VERSION_MINOR(program_version);
91 program.version_patch = VERSION_PATCH(program_version);
92 program.version_build = VERSION_BUILD(program_version);
93 program.version_ident = program_version;
95 program.log_filename[LOG_OUT_ID] = getLogFilename(LOG_OUT_BASENAME);
96 program.log_filename[LOG_ERR_ID] = getLogFilename(LOG_ERR_BASENAME);
97 program.log_file[LOG_OUT_ID] = program.log_file_default[LOG_OUT_ID] = stdout;
98 program.log_file[LOG_ERR_ID] = program.log_file_default[LOG_ERR_ID] = stderr;
100 program.headless = FALSE;
103 void InitScoresInfo()
105 char *global_scores_dir = getPath2(getCommonDataDir(), SCORES_DIRECTORY);
107 program.global_scores = directoryExists(global_scores_dir);
108 program.many_scores_per_name = !program.global_scores;
112 if (program.global_scores)
114 Error(ERR_DEBUG, "Using global, multi-user scores directory '%s'.",
116 Error(ERR_DEBUG, "Remove to enable single-user scores directory.");
117 Error(ERR_DEBUG, "(This enables multipe score entries per user.)");
121 Error(ERR_DEBUG, "Using private, single-user scores directory.");
125 free(global_scores_dir);
128 void SetWindowTitle()
130 program.window_title = program.window_title_function();
135 void InitWindowTitleFunction(char *(*window_title_function)(void))
137 program.window_title_function = window_title_function;
140 void InitExitMessageFunction(void (*exit_message_function)(char *, va_list))
142 program.exit_message_function = exit_message_function;
145 void InitExitFunction(void (*exit_function)(int))
147 program.exit_function = exit_function;
149 /* set signal handlers to custom exit function */
150 // signal(SIGINT, exit_function);
151 signal(SIGTERM, exit_function);
153 /* set exit function to automatically cleanup SDL stuff after exit() */
157 void InitPlatformDependentStuff(void)
159 // this is initialized in GetOptions(), but may already be used before
160 options.verbose = TRUE;
164 #if defined(TARGET_SDL2)
165 int sdl_init_flags = SDL_INIT_EVENTS | SDL_INIT_NOPARACHUTE;
167 int sdl_init_flags = SDL_INIT_EVENTTHREAD | SDL_INIT_NOPARACHUTE;
170 if (SDL_Init(sdl_init_flags) < 0)
171 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
176 void ClosePlatformDependentStuff(void)
181 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
182 int real_sx, int real_sy,
183 int full_sxsize, int full_sysize,
184 Bitmap *field_save_buffer)
190 gfx.real_sx = real_sx;
191 gfx.real_sy = real_sy;
192 gfx.full_sxsize = full_sxsize;
193 gfx.full_sysize = full_sysize;
195 gfx.field_save_buffer = field_save_buffer;
197 SetDrawDeactivationMask(REDRAW_NONE); /* do not deactivate drawing */
198 SetDrawBackgroundMask(REDRAW_NONE); /* deactivate masked drawing */
201 void InitGfxTileSizeInfo(int game_tile_size, int standard_tile_size)
203 gfx.game_tile_size = game_tile_size;
204 gfx.standard_tile_size = standard_tile_size;
207 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
215 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
223 void InitGfxDoor3Info(int ex, int ey, int exsize, int eysize)
231 void InitGfxWindowInfo(int win_xsize, int win_ysize)
233 if (win_xsize != gfx.win_xsize || win_ysize != gfx.win_ysize)
235 ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize);
237 #if defined(TARGET_SDL2)
238 ReCreateBitmap(&gfx.final_screen_bitmap, win_xsize, win_ysize);
241 ReCreateBitmap(&gfx.fade_bitmap_backup, win_xsize, win_ysize);
242 ReCreateBitmap(&gfx.fade_bitmap_source, win_xsize, win_ysize);
243 ReCreateBitmap(&gfx.fade_bitmap_target, win_xsize, win_ysize);
244 ReCreateBitmap(&gfx.fade_bitmap_black, win_xsize, win_ysize);
246 ClearRectangle(gfx.fade_bitmap_black, 0, 0, win_xsize, win_ysize);
249 gfx.win_xsize = win_xsize;
250 gfx.win_ysize = win_ysize;
252 gfx.background_bitmap_mask = REDRAW_NONE;
255 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
257 /* currently only used by MSDOS code to alloc VRAM buffer, if available */
258 /* 2009-03-24: also (temporarily?) used for overlapping blit workaround */
259 gfx.scrollbuffer_width = scrollbuffer_width;
260 gfx.scrollbuffer_height = scrollbuffer_height;
263 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
265 gfx.clipping_enabled = enabled;
268 gfx.clip_width = width;
269 gfx.clip_height = height;
272 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
274 gfx.draw_busy_anim_function = draw_busy_anim_function;
277 void InitGfxDrawGlobalAnimFunction(void (*draw_global_anim_function)(int, int))
279 gfx.draw_global_anim_function = draw_global_anim_function;
282 void InitGfxDrawGlobalBorderFunction(void (*draw_global_border_function)(int))
284 gfx.draw_global_border_function = draw_global_border_function;
287 void InitGfxCustomArtworkInfo()
289 gfx.override_level_graphics = FALSE;
290 gfx.override_level_sounds = FALSE;
291 gfx.override_level_music = FALSE;
293 gfx.draw_init_text = TRUE;
296 void InitGfxOtherSettings()
298 gfx.cursor_mode = CURSOR_DEFAULT;
301 void InitOverlayInfo()
303 overlay.enabled = FALSE;
304 overlay.active = FALSE;
306 #if defined(PLATFORM_ANDROID)
307 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
308 overlay.enabled = TRUE;
312 void SetOverlayEnabled(boolean enabled)
314 overlay.enabled = enabled;
317 void SetOverlayActive(boolean active)
319 overlay.active = active;
322 boolean GetOverlayActive()
324 return overlay.active;
327 void SetDrawDeactivationMask(int draw_deactivation_mask)
329 gfx.draw_deactivation_mask = draw_deactivation_mask;
332 void SetDrawBackgroundMask(int draw_background_mask)
334 gfx.draw_background_mask = draw_background_mask;
337 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
339 if (background_bitmap_tile != NULL)
340 gfx.background_bitmap_mask |= mask;
342 gfx.background_bitmap_mask &= ~mask;
344 if (background_bitmap_tile == NULL) /* empty background requested */
347 if (mask == REDRAW_ALL)
348 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
349 0, 0, video.width, video.height);
350 else if (mask == REDRAW_FIELD)
351 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
352 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
353 else if (mask == REDRAW_DOOR_1)
354 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
355 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
358 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
360 /* remove every mask before setting mask for window */
361 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
362 SetBackgroundBitmap(NULL, 0xffff); /* !!! FIX THIS !!! */
363 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
366 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
368 /* remove window area mask before setting mask for main area */
369 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
370 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
371 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
374 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
376 /* remove window area mask before setting mask for door area */
377 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
378 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
379 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
383 /* ========================================================================= */
384 /* video functions */
385 /* ========================================================================= */
387 inline static int GetRealDepth(int depth)
389 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
392 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
393 int width, int height, Pixel color)
395 SDLFillRectangle(bitmap, x, y, width, height, color);
397 if (bitmap == backbuffer)
398 SetRedrawMaskFromArea(x, y, width, height);
401 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
402 int src_x, int src_y, int width, int height,
403 int dst_x, int dst_y, int mask_mode)
405 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
406 dst_x, dst_y, mask_mode);
408 if (dst_bitmap == backbuffer)
409 SetRedrawMaskFromArea(dst_x, dst_y, width, height);
412 void LimitScreenUpdates(boolean enable)
414 SDLLimitScreenUpdates(enable);
417 void InitVideoDisplay(void)
419 if (program.headless)
422 SDLInitVideoDisplay();
423 #if defined(TARGET_SDL2)
428 void CloseVideoDisplay(void)
430 KeyboardAutoRepeatOn();
432 SDL_QuitSubSystem(SDL_INIT_VIDEO);
435 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
438 video.height = height;
439 video.depth = GetRealDepth(depth);
441 video.screen_width = width;
442 video.screen_height = height;
443 video.screen_xoffset = 0;
444 video.screen_yoffset = 0;
446 video.fullscreen_available = FULLSCREEN_STATUS;
447 video.fullscreen_enabled = FALSE;
449 video.window_scaling_available = WINDOW_SCALING_STATUS;
451 video.frame_delay = 0;
452 video.frame_delay_value = GAME_FRAME_DELAY;
454 video.shifted_up = FALSE;
455 video.shifted_up_pos = 0;
456 video.shifted_up_pos_last = 0;
457 video.shifted_up_delay = 0;
458 video.shifted_up_delay_value = ONE_SECOND_DELAY / 4;
460 SDLInitVideoBuffer(fullscreen);
462 video.initialized = TRUE;
467 inline static void FreeBitmapPointers(Bitmap *bitmap)
472 SDLFreeBitmapPointers(bitmap);
474 checked_free(bitmap->source_filename);
475 bitmap->source_filename = NULL;
478 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
481 if (src_bitmap == NULL || dst_bitmap == NULL)
484 FreeBitmapPointers(dst_bitmap);
486 *dst_bitmap = *src_bitmap;
489 void FreeBitmap(Bitmap *bitmap)
494 FreeBitmapPointers(bitmap);
499 Bitmap *CreateBitmapStruct(void)
501 return checked_calloc(sizeof(Bitmap));
504 Bitmap *CreateBitmap(int width, int height, int depth)
506 Bitmap *new_bitmap = CreateBitmapStruct();
507 int real_width = MAX(1, width); /* prevent zero bitmap width */
508 int real_height = MAX(1, height); /* prevent zero bitmap height */
509 int real_depth = GetRealDepth(depth);
511 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
513 new_bitmap->width = real_width;
514 new_bitmap->height = real_height;
519 void ReCreateBitmap(Bitmap **bitmap, int width, int height)
521 Bitmap *new_bitmap = CreateBitmap(width, height, DEFAULT_DEPTH);
525 *bitmap = new_bitmap;
529 TransferBitmapPointers(new_bitmap, *bitmap);
534 void CloseWindow(DrawWindow *window)
538 void SetRedrawMaskFromArea(int x, int y, int width, int height)
542 int x2 = x + width - 1;
543 int y2 = y + height - 1;
545 if (width == 0 || height == 0)
548 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
549 redraw_mask |= REDRAW_FIELD;
550 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
551 redraw_mask |= REDRAW_DOOR_1;
552 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
553 redraw_mask |= REDRAW_DOOR_2;
554 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
555 redraw_mask |= REDRAW_DOOR_3;
557 redraw_mask = REDRAW_ALL;
560 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
563 if (draw_mask == REDRAW_NONE)
566 if (draw_mask & REDRAW_ALL)
569 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
572 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
575 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
578 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
584 boolean DrawingDeactivated(int x, int y, int width, int height)
586 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
589 boolean DrawingOnBackground(int x, int y)
591 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
592 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
595 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
596 int *width, int *height, boolean is_dest)
598 int clip_x, clip_y, clip_width, clip_height;
600 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
602 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
603 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
604 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
605 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
611 clip_width = bitmap->width;
612 clip_height = bitmap->height;
615 /* skip if rectangle completely outside bitmap */
617 if (*x + *width <= clip_x ||
618 *y + *height <= clip_y ||
619 *x >= clip_x + clip_width ||
620 *y >= clip_y + clip_height)
623 /* clip if rectangle overlaps bitmap */
627 *width -= clip_x - *x;
630 else if (*x + *width > clip_x + clip_width)
632 *width = clip_x + clip_width - *x;
637 *height -= clip_y - *y;
640 else if (*y + *height > clip_y + clip_height)
642 *height = clip_y + clip_height - *y;
648 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
649 int src_x, int src_y, int width, int height,
650 int dst_x, int dst_y)
652 int dst_x_unclipped = dst_x;
653 int dst_y_unclipped = dst_y;
655 if (program.headless)
658 if (src_bitmap == NULL || dst_bitmap == NULL)
661 if (DrawingDeactivated(dst_x, dst_y, width, height))
664 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
665 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
668 /* source x/y might need adjustment if destination x/y was clipped top/left */
669 src_x += dst_x - dst_x_unclipped;
670 src_y += dst_y - dst_y_unclipped;
672 #if defined(TARGET_SDL2)
673 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
674 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
675 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
676 but is already fixed in SVN and should therefore finally be fixed with
677 the next official SDL release, which is probably version 1.2.14.) */
678 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
680 if (src_bitmap == dst_bitmap)
682 /* needed when blitting directly to same bitmap -- should not be needed with
683 recent SDL libraries, but apparently does not work in 1.2.11 directly */
685 static Bitmap *tmp_bitmap = NULL;
686 static int tmp_bitmap_xsize = 0;
687 static int tmp_bitmap_ysize = 0;
689 /* start with largest static bitmaps for initial bitmap size ... */
690 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
692 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
693 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
696 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
697 if (src_bitmap->width > tmp_bitmap_xsize ||
698 src_bitmap->height > tmp_bitmap_ysize)
700 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
701 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
703 FreeBitmap(tmp_bitmap);
708 if (tmp_bitmap == NULL)
709 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
712 sysCopyArea(src_bitmap, tmp_bitmap,
713 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
714 sysCopyArea(tmp_bitmap, dst_bitmap,
715 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
721 sysCopyArea(src_bitmap, dst_bitmap,
722 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
725 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
726 int src_x, int src_y, int src_width, int src_height,
727 int dst_x, int dst_y, int dst_width, int dst_height)
729 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
730 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
731 int dst_xsize = dst_width;
732 int dst_ysize = dst_height;
733 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
734 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
737 for (y = 0; y < src_ysteps; y++)
739 for (x = 0; x < src_xsteps; x++)
741 int draw_x = dst_x + x * src_xsize;
742 int draw_y = dst_y + y * src_ysize;
743 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
744 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
746 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
752 void FadeRectangle(int x, int y, int width, int height,
753 int fade_mode, int fade_delay, int post_delay,
754 void (*draw_border_function)(void))
756 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
757 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
760 SDLFadeRectangle(x, y, width, height,
761 fade_mode, fade_delay, post_delay, draw_border_function);
764 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
767 if (DrawingDeactivated(x, y, width, height))
770 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
773 sysFillRectangle(bitmap, x, y, width, height, color);
776 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
778 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
781 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
782 int width, int height)
784 if (DrawingOnBackground(x, y))
785 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
787 ClearRectangle(bitmap, x, y, width, height);
790 void BlitBitmapMasked(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 (DrawingDeactivated(dst_x, dst_y, width, height))
797 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
798 dst_x, dst_y, BLIT_MASKED);
801 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
802 int src_x, int src_y, int width, int height,
803 int dst_x, int dst_y)
805 if (DrawingOnBackground(dst_x, dst_y))
807 /* draw background */
808 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
811 /* draw foreground */
812 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
816 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
820 void BlitTexture(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 BlitTextureMasked(Bitmap *bitmap,
832 int src_x, int src_y, int width, int height,
833 int dst_x, int dst_y)
838 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
842 void BlitToScreen(Bitmap *bitmap,
843 int src_x, int src_y, int width, int height,
844 int dst_x, int dst_y)
849 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
850 BlitBitmap(bitmap, gfx.final_screen_bitmap, src_x, src_y,
851 width, height, dst_x, dst_y);
853 BlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y);
856 void BlitToScreenMasked(Bitmap *bitmap,
857 int src_x, int src_y, int width, int height,
858 int dst_x, int dst_y)
863 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
864 BlitBitmapMasked(bitmap, gfx.final_screen_bitmap, src_x, src_y,
865 width, height, dst_x, dst_y);
867 BlitTextureMasked(bitmap, src_x, src_y, width, height, dst_x, dst_y);
870 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
873 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
876 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
879 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
882 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
883 int to_x, int to_y, Pixel pixel, int line_width)
887 for (x = 0; x < line_width; x++)
889 for (y = 0; y < line_width; y++)
891 int dx = x - line_width / 2;
892 int dy = y - line_width / 2;
894 if ((x == 0 && y == 0) ||
895 (x == 0 && y == line_width - 1) ||
896 (x == line_width - 1 && y == 0) ||
897 (x == line_width - 1 && y == line_width - 1))
901 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
906 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
911 for (i = 0; i < num_points - 1; i++)
912 DrawLine(bitmap, points[i].x, points[i].y,
913 points[i + 1].x, points[i + 1].y, pixel, line_width);
916 SDLDrawLines(bitmap->surface, points, num_points, pixel);
920 Pixel GetPixel(Bitmap *bitmap, int x, int y)
922 if (x < 0 || x >= bitmap->width ||
923 y < 0 || y >= bitmap->height)
926 return SDLGetPixel(bitmap, x, y);
929 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
930 unsigned int color_g, unsigned int color_b)
932 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
935 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
937 unsigned int color_r = (color >> 16) & 0xff;
938 unsigned int color_g = (color >> 8) & 0xff;
939 unsigned int color_b = (color >> 0) & 0xff;
941 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
944 void KeyboardAutoRepeatOn(void)
946 #if defined(TARGET_SDL2)
947 keyrepeat_status = TRUE;
949 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
950 SDL_DEFAULT_REPEAT_INTERVAL / 2);
951 SDL_EnableUNICODE(1);
955 void KeyboardAutoRepeatOff(void)
957 #if defined(TARGET_SDL2)
958 keyrepeat_status = FALSE;
960 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
961 SDL_EnableUNICODE(0);
965 boolean SetVideoMode(boolean fullscreen)
967 return SDLSetVideoMode(fullscreen);
970 void SetVideoFrameDelay(unsigned int frame_delay_value)
972 video.frame_delay_value = frame_delay_value;
975 unsigned int GetVideoFrameDelay()
977 return video.frame_delay_value;
980 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
982 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
983 (!fullscreen && video.fullscreen_enabled))
984 fullscreen = SetVideoMode(fullscreen);
989 Bitmap *LoadImage(char *filename)
993 new_bitmap = SDLLoadImage(filename);
996 new_bitmap->source_filename = getStringCopy(filename);
1001 Bitmap *LoadCustomImage(char *basename)
1003 char *filename = getCustomImageFilename(basename);
1006 if (filename == NULL)
1007 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
1009 if ((new_bitmap = LoadImage(filename)) == NULL)
1010 Error(ERR_EXIT, "LoadImage('%s') failed: %s", basename, GetError());
1015 void ReloadCustomImage(Bitmap *bitmap, char *basename)
1017 char *filename = getCustomImageFilename(basename);
1020 if (filename == NULL) /* (should never happen) */
1022 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
1026 if (strEqual(filename, bitmap->source_filename))
1028 /* The old and new image are the same (have the same filename and path).
1029 This usually means that this image does not exist in this graphic set
1030 and a fallback to the existing image is done. */
1035 if ((new_bitmap = LoadImage(filename)) == NULL)
1037 Error(ERR_WARN, "LoadImage('%s') failed: %s", basename, GetError());
1041 if (bitmap->width != new_bitmap->width ||
1042 bitmap->height != new_bitmap->height)
1044 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
1046 FreeBitmap(new_bitmap);
1050 TransferBitmapPointers(new_bitmap, bitmap);
1054 static Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
1056 return SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
1059 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
1061 if (bitmaps[IMG_BITMAP_CUSTOM])
1063 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
1065 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
1068 if (gfx.game_tile_size == gfx.standard_tile_size)
1070 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1075 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
1076 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
1077 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
1079 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
1081 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
1082 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
1085 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1086 int tile_size, boolean create_small_bitmaps)
1088 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1089 Bitmap *tmp_bitmap_final = NULL;
1090 Bitmap *tmp_bitmap_0 = NULL;
1091 Bitmap *tmp_bitmap_1 = NULL;
1092 Bitmap *tmp_bitmap_2 = NULL;
1093 Bitmap *tmp_bitmap_4 = NULL;
1094 Bitmap *tmp_bitmap_8 = NULL;
1095 Bitmap *tmp_bitmap_16 = NULL;
1096 Bitmap *tmp_bitmap_32 = NULL;
1097 int width_final, height_final;
1098 int width_0, height_0;
1099 int width_1, height_1;
1100 int width_2, height_2;
1101 int width_4, height_4;
1102 int width_8, height_8;
1103 int width_16, height_16;
1104 int width_32, height_32;
1105 int old_width, old_height;
1108 print_timestamp_init("CreateScaledBitmaps");
1110 old_width = old_bitmap->width;
1111 old_height = old_bitmap->height;
1113 /* calculate new image dimensions for final image size */
1114 width_final = old_width * zoom_factor;
1115 height_final = old_height * zoom_factor;
1117 /* get image with final size (this might require scaling up) */
1118 /* ("final" size may result in non-standard tile size image) */
1119 if (zoom_factor != 1)
1120 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1122 tmp_bitmap_final = old_bitmap;
1124 UPDATE_BUSY_STATE();
1126 width_0 = width_1 = width_final;
1127 height_0 = height_1 = height_final;
1129 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1131 if (create_small_bitmaps)
1133 /* check if we have a non-gameplay tile size image */
1134 if (tile_size != gfx.game_tile_size)
1136 /* get image with gameplay tile size */
1137 width_0 = width_final * gfx.game_tile_size / tile_size;
1138 height_0 = height_final * gfx.game_tile_size / tile_size;
1140 if (width_0 == old_width)
1141 tmp_bitmap_0 = old_bitmap;
1142 else if (width_0 == width_final)
1143 tmp_bitmap_0 = tmp_bitmap_final;
1145 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1147 UPDATE_BUSY_STATE();
1150 /* check if we have a non-standard tile size image */
1151 if (tile_size != gfx.standard_tile_size)
1153 /* get image with standard tile size */
1154 width_1 = width_final * gfx.standard_tile_size / tile_size;
1155 height_1 = height_final * gfx.standard_tile_size / tile_size;
1157 if (width_1 == old_width)
1158 tmp_bitmap_1 = old_bitmap;
1159 else if (width_1 == width_final)
1160 tmp_bitmap_1 = tmp_bitmap_final;
1161 else if (width_1 == width_0)
1162 tmp_bitmap_1 = tmp_bitmap_0;
1164 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1166 UPDATE_BUSY_STATE();
1169 /* calculate new image dimensions for small images */
1170 width_2 = width_1 / 2;
1171 height_2 = height_1 / 2;
1172 width_4 = width_1 / 4;
1173 height_4 = height_1 / 4;
1174 width_8 = width_1 / 8;
1175 height_8 = height_1 / 8;
1176 width_16 = width_1 / 16;
1177 height_16 = height_1 / 16;
1178 width_32 = width_1 / 32;
1179 height_32 = height_1 / 32;
1181 /* get image with 1/2 of normal size (for use in the level editor) */
1182 if (width_2 == old_width)
1183 tmp_bitmap_2 = old_bitmap;
1185 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1187 UPDATE_BUSY_STATE();
1189 /* get image with 1/4 of normal size (for use in the level editor) */
1190 if (width_4 == old_width)
1191 tmp_bitmap_4 = old_bitmap;
1193 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1195 UPDATE_BUSY_STATE();
1197 /* get image with 1/8 of normal size (for use on the preview screen) */
1198 if (width_8 == old_width)
1199 tmp_bitmap_8 = old_bitmap;
1201 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1203 UPDATE_BUSY_STATE();
1205 /* get image with 1/16 of normal size (for use on the preview screen) */
1206 if (width_16 == old_width)
1207 tmp_bitmap_16 = old_bitmap;
1209 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1211 UPDATE_BUSY_STATE();
1213 /* get image with 1/32 of normal size (for use on the preview screen) */
1214 if (width_32 == old_width)
1215 tmp_bitmap_32 = old_bitmap;
1217 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1219 UPDATE_BUSY_STATE();
1221 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1222 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1223 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1224 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1225 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1226 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1228 if (width_0 != width_1)
1229 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1231 if (bitmaps[IMG_BITMAP_CUSTOM])
1232 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1234 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1236 boolean free_old_bitmap = TRUE;
1238 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1239 if (bitmaps[i] == old_bitmap)
1240 free_old_bitmap = FALSE;
1242 if (free_old_bitmap)
1243 FreeBitmap(old_bitmap);
1247 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1250 UPDATE_BUSY_STATE();
1252 print_timestamp_done("CreateScaledBitmaps");
1255 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1258 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1261 void CreateBitmapTextures(Bitmap **bitmaps)
1263 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1266 void FreeBitmapTextures(Bitmap **bitmaps)
1268 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1271 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1273 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1277 /* ------------------------------------------------------------------------- */
1278 /* mouse pointer functions */
1279 /* ------------------------------------------------------------------------- */
1281 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1283 /* XPM image definitions */
1284 static const char *cursor_image_none[] =
1286 /* width height num_colors chars_per_pixel */
1316 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1317 static const char *cursor_image_dot[] =
1319 /* width height num_colors chars_per_pixel */
1348 static const char **cursor_image_playfield = cursor_image_dot;
1350 /* some people complained about a "white dot" on the screen and thought it
1351 was a graphical error... OK, let's just remove the whole pointer :-) */
1352 static const char **cursor_image_playfield = cursor_image_none;
1355 static const int cursor_bit_order = BIT_ORDER_MSB;
1357 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1359 struct MouseCursorInfo *cursor;
1360 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1361 int header_lines = 4;
1364 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1366 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1369 for (y = 0; y < cursor->width; y++)
1371 for (x = 0; x < cursor->height; x++)
1374 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1379 cursor->data[i] = cursor->mask[i] = 0;
1382 switch (image[header_lines + y][x])
1385 cursor->data[i] |= bit_mask;
1386 cursor->mask[i] |= bit_mask;
1390 cursor->mask[i] |= bit_mask;
1399 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1404 void SetMouseCursor(int mode)
1406 static struct MouseCursorInfo *cursor_none = NULL;
1407 static struct MouseCursorInfo *cursor_playfield = NULL;
1408 struct MouseCursorInfo *cursor_new;
1410 if (cursor_none == NULL)
1411 cursor_none = get_cursor_from_image(cursor_image_none);
1413 if (cursor_playfield == NULL)
1414 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1416 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1417 mode == CURSOR_NONE ? cursor_none :
1418 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1420 SDLSetMouseCursor(cursor_new);
1422 gfx.cursor_mode = mode;
1426 /* ========================================================================= */
1427 /* audio functions */
1428 /* ========================================================================= */
1430 void OpenAudio(void)
1432 /* always start with reliable default values */
1433 audio.sound_available = FALSE;
1434 audio.music_available = FALSE;
1435 audio.loops_available = FALSE;
1437 audio.sound_enabled = FALSE;
1438 audio.sound_deactivated = FALSE;
1440 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1441 audio.mixer_pid = 0;
1442 audio.device_name = NULL;
1443 audio.device_fd = -1;
1445 audio.num_channels = 0;
1446 audio.music_channel = 0;
1447 audio.first_sound_channel = 0;
1452 void CloseAudio(void)
1456 audio.sound_enabled = FALSE;
1459 void SetAudioMode(boolean enabled)
1461 if (!audio.sound_available)
1464 audio.sound_enabled = enabled;
1468 /* ========================================================================= */
1469 /* event functions */
1470 /* ========================================================================= */
1472 boolean PendingEvent(void)
1474 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1477 void NextEvent(Event *event)
1479 SDLNextEvent(event);
1482 void PeekEvent(Event *event)
1484 #if defined(TARGET_SDL2)
1485 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1487 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1491 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1493 #if defined(TARGET_SDL2)
1494 /* key up/down events in SDL2 do not return text characters anymore */
1495 return event->keysym.sym;
1498 #if ENABLE_UNUSED_CODE
1499 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1500 (int)event->keysym.unicode,
1501 (int)event->keysym.sym,
1502 (int)SDL_GetModState());
1505 if (with_modifiers &&
1506 event->keysym.unicode > 0x0000 &&
1507 event->keysym.unicode < 0x2000)
1508 return event->keysym.unicode;
1510 return event->keysym.sym;
1515 KeyMod HandleKeyModState(Key key, int key_status)
1517 static KeyMod current_modifiers = KMOD_None;
1519 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1521 KeyMod new_modifier = KMOD_None;
1526 new_modifier = KMOD_Shift_L;
1529 new_modifier = KMOD_Shift_R;
1531 case KSYM_Control_L:
1532 new_modifier = KMOD_Control_L;
1534 case KSYM_Control_R:
1535 new_modifier = KMOD_Control_R;
1538 new_modifier = KMOD_Meta_L;
1541 new_modifier = KMOD_Meta_R;
1544 new_modifier = KMOD_Alt_L;
1547 new_modifier = KMOD_Alt_R;
1553 if (key_status == KEY_PRESSED)
1554 current_modifiers |= new_modifier;
1556 current_modifiers &= ~new_modifier;
1559 return current_modifiers;
1562 KeyMod GetKeyModState()
1564 return (KeyMod)SDL_GetModState();
1567 KeyMod GetKeyModStateFromEvents()
1569 /* always use key modifier state as tracked from key events (this is needed
1570 if the modifier key event was injected into the event queue, but the key
1571 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1572 query the keys as held pressed on the keyboard) -- this case is currently
1573 only used to filter out clipboard insert events from "True X-Mouse" tool */
1575 return HandleKeyModState(KSYM_UNDEFINED, 0);
1578 void StartTextInput(int x, int y, int width, int height)
1580 #if defined(TARGET_SDL2)
1581 SDL_StartTextInput();
1583 #if defined(HAS_SCREEN_KEYBOARD)
1584 if (y + height > SCREEN_KEYBOARD_POS(video.height))
1586 video.shifted_up_pos = y + height - SCREEN_KEYBOARD_POS(video.height);
1587 video.shifted_up_delay = SDL_GetTicks();
1588 video.shifted_up = TRUE;
1594 void StopTextInput()
1596 #if defined(TARGET_SDL2)
1597 SDL_StopTextInput();
1599 #if defined(HAS_SCREEN_KEYBOARD)
1600 if (video.shifted_up)
1602 video.shifted_up_pos = 0;
1603 video.shifted_up_delay = SDL_GetTicks();
1604 video.shifted_up = FALSE;
1610 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1612 if (event->type != EVENT_CLIENTMESSAGE)
1615 return TRUE; /* the only possible message here is SDL_QUIT */
1619 /* ========================================================================= */
1620 /* joystick functions */
1621 /* ========================================================================= */
1623 void InitJoysticks()
1627 #if defined(NO_JOYSTICK)
1628 return; /* joysticks generally deactivated by compile-time directive */
1631 /* always start with reliable default values */
1632 joystick.status = JOYSTICK_NOT_AVAILABLE;
1633 for (i = 0; i < MAX_PLAYERS; i++)
1634 joystick.nr[i] = -1; /* no joystick configured */
1639 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1641 return SDLReadJoystick(nr, x, y, b1, b2);
1644 boolean CheckJoystickOpened(int nr)
1646 return SDLCheckJoystickOpened(nr);
1649 void ClearJoystickState()
1651 SDLClearJoystickState();