1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
24 #define ENABLE_UNUSED_CODE 0 /* currently unused functions */
27 /* ========================================================================= */
28 /* exported variables */
29 /* ========================================================================= */
31 struct ProgramInfo program;
32 struct OptionInfo options;
33 struct VideoSystemInfo video;
34 struct AudioSystemInfo audio;
36 struct OverlayInfo overlay;
37 struct ArtworkInfo artwork;
38 struct JoystickInfo joystick;
39 struct SetupInfo setup;
41 LevelDirTree *leveldir_first_all = NULL;
42 LevelDirTree *leveldir_first = NULL;
43 LevelDirTree *leveldir_current = NULL;
46 struct LevelStats level_stats[MAX_LEVELS];
48 DrawWindow *window = NULL;
49 DrawBuffer *backbuffer = NULL;
50 DrawBuffer *drawto = NULL;
52 int button_status = MB_NOT_PRESSED;
53 boolean motion_status = FALSE;
54 int wheel_steps = DEFAULT_WHEEL_STEPS;
55 #if defined(TARGET_SDL2)
56 boolean keyrepeat_status = TRUE;
59 int redraw_mask = REDRAW_NONE;
64 /* ========================================================================= */
65 /* init/close functions */
66 /* ========================================================================= */
68 void InitProgramInfo(char *argv0, char *config_filename, char *userdata_subdir,
69 char *program_title, char *icon_title,
70 char *icon_filename, char *cookie_prefix,
71 char *program_version_string, int program_version)
73 program.command_basepath = getBasePath(argv0);
74 program.command_basename = getBaseName(argv0);
76 program.config_filename = config_filename;
78 program.userdata_subdir = userdata_subdir;
79 program.userdata_path = getUserGameDataDir();
81 program.program_title = program_title;
82 program.window_title = "(undefined)";
83 program.icon_title = icon_title;
85 program.icon_filename = icon_filename;
87 program.cookie_prefix = cookie_prefix;
89 program.version_major = VERSION_MAJOR(program_version);
90 program.version_minor = VERSION_MINOR(program_version);
91 program.version_patch = VERSION_PATCH(program_version);
92 program.version_build = VERSION_BUILD(program_version);
93 program.version_ident = program_version;
95 program.version_string = program_version_string;
97 program.log_filename[LOG_OUT_ID] = getLogFilename(LOG_OUT_BASENAME);
98 program.log_filename[LOG_ERR_ID] = getLogFilename(LOG_ERR_BASENAME);
99 program.log_file[LOG_OUT_ID] = program.log_file_default[LOG_OUT_ID] = stdout;
100 program.log_file[LOG_ERR_ID] = program.log_file_default[LOG_ERR_ID] = stderr;
102 program.headless = FALSE;
105 void InitScoresInfo()
107 char *global_scores_dir = getPath2(getCommonDataDir(), SCORES_DIRECTORY);
109 program.global_scores = directoryExists(global_scores_dir);
110 program.many_scores_per_name = !program.global_scores;
114 if (program.global_scores)
116 Error(ERR_DEBUG, "Using global, multi-user scores directory '%s'.",
118 Error(ERR_DEBUG, "Remove to enable single-user scores directory.");
119 Error(ERR_DEBUG, "(This enables multipe score entries per user.)");
123 Error(ERR_DEBUG, "Using private, single-user scores directory.");
127 free(global_scores_dir);
130 void SetWindowTitle()
132 program.window_title = program.window_title_function();
137 void InitWindowTitleFunction(char *(*window_title_function)(void))
139 program.window_title_function = window_title_function;
142 void InitExitMessageFunction(void (*exit_message_function)(char *, va_list))
144 program.exit_message_function = exit_message_function;
147 void InitExitFunction(void (*exit_function)(int))
149 program.exit_function = exit_function;
151 /* set signal handlers to custom exit function */
152 // signal(SIGINT, exit_function);
153 signal(SIGTERM, exit_function);
155 /* set exit function to automatically cleanup SDL stuff after exit() */
159 void InitPlatformDependentStuff(void)
161 // this is initialized in GetOptions(), but may already be used before
162 options.verbose = TRUE;
166 #if defined(TARGET_SDL2)
167 int sdl_init_flags = SDL_INIT_EVENTS | SDL_INIT_NOPARACHUTE;
169 int sdl_init_flags = SDL_INIT_EVENTTHREAD | SDL_INIT_NOPARACHUTE;
172 if (SDL_Init(sdl_init_flags) < 0)
173 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
178 void ClosePlatformDependentStuff(void)
183 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
184 int real_sx, int real_sy,
185 int full_sxsize, int full_sysize,
186 Bitmap *field_save_buffer)
192 gfx.real_sx = real_sx;
193 gfx.real_sy = real_sy;
194 gfx.full_sxsize = full_sxsize;
195 gfx.full_sysize = full_sysize;
197 gfx.field_save_buffer = field_save_buffer;
199 SetDrawDeactivationMask(REDRAW_NONE); /* do not deactivate drawing */
200 SetDrawBackgroundMask(REDRAW_NONE); /* deactivate masked drawing */
203 void InitGfxTileSizeInfo(int game_tile_size, int standard_tile_size)
205 gfx.game_tile_size = game_tile_size;
206 gfx.standard_tile_size = standard_tile_size;
209 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
217 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
225 void InitGfxDoor3Info(int ex, int ey, int exsize, int eysize)
233 void InitGfxWindowInfo(int win_xsize, int win_ysize)
235 if (win_xsize != gfx.win_xsize || win_ysize != gfx.win_ysize)
237 ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize);
239 #if defined(TARGET_SDL2)
240 ReCreateBitmap(&gfx.final_screen_bitmap, win_xsize, win_ysize);
243 ReCreateBitmap(&gfx.fade_bitmap_backup, win_xsize, win_ysize);
244 ReCreateBitmap(&gfx.fade_bitmap_source, win_xsize, win_ysize);
245 ReCreateBitmap(&gfx.fade_bitmap_target, win_xsize, win_ysize);
246 ReCreateBitmap(&gfx.fade_bitmap_black, win_xsize, win_ysize);
248 ClearRectangle(gfx.fade_bitmap_black, 0, 0, win_xsize, win_ysize);
251 gfx.win_xsize = win_xsize;
252 gfx.win_ysize = win_ysize;
254 gfx.background_bitmap_mask = REDRAW_NONE;
257 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
259 /* currently only used by MSDOS code to alloc VRAM buffer, if available */
260 /* 2009-03-24: also (temporarily?) used for overlapping blit workaround */
261 gfx.scrollbuffer_width = scrollbuffer_width;
262 gfx.scrollbuffer_height = scrollbuffer_height;
265 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
267 gfx.clipping_enabled = enabled;
270 gfx.clip_width = width;
271 gfx.clip_height = height;
274 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
276 gfx.draw_busy_anim_function = draw_busy_anim_function;
279 void InitGfxDrawGlobalAnimFunction(void (*draw_global_anim_function)(int, int))
281 gfx.draw_global_anim_function = draw_global_anim_function;
284 void InitGfxDrawGlobalBorderFunction(void (*draw_global_border_function)(int))
286 gfx.draw_global_border_function = draw_global_border_function;
289 void InitGfxCustomArtworkInfo()
291 gfx.override_level_graphics = FALSE;
292 gfx.override_level_sounds = FALSE;
293 gfx.override_level_music = FALSE;
295 gfx.draw_init_text = TRUE;
298 void InitGfxOtherSettings()
300 gfx.cursor_mode = CURSOR_DEFAULT;
303 void InitOverlayInfo()
305 overlay.enabled = FALSE;
306 overlay.active = FALSE;
308 #if defined(PLATFORM_ANDROID)
309 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
310 overlay.enabled = TRUE;
314 void SetOverlayEnabled(boolean enabled)
316 overlay.enabled = enabled;
319 void SetOverlayActive(boolean active)
321 overlay.active = active;
324 boolean GetOverlayActive()
326 return overlay.active;
329 void SetDrawDeactivationMask(int draw_deactivation_mask)
331 gfx.draw_deactivation_mask = draw_deactivation_mask;
334 int GetDrawDeactivationMask()
336 return gfx.draw_deactivation_mask;
339 void SetDrawBackgroundMask(int draw_background_mask)
341 gfx.draw_background_mask = draw_background_mask;
344 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
346 if (background_bitmap_tile != NULL)
347 gfx.background_bitmap_mask |= mask;
349 gfx.background_bitmap_mask &= ~mask;
351 if (background_bitmap_tile == NULL) /* empty background requested */
354 if (mask == REDRAW_ALL)
355 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
356 0, 0, video.width, video.height);
357 else if (mask == REDRAW_FIELD)
358 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
359 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
360 else if (mask == REDRAW_DOOR_1)
361 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
362 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
365 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
367 /* remove every mask before setting mask for window */
368 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
369 SetBackgroundBitmap(NULL, 0xffff); /* !!! FIX THIS !!! */
370 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
373 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
375 /* remove window area mask before setting mask for main area */
376 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
377 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
378 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
381 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
383 /* remove window area mask before setting mask for door area */
384 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
385 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
386 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
390 /* ========================================================================= */
391 /* video functions */
392 /* ========================================================================= */
394 inline static int GetRealDepth(int depth)
396 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
399 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
400 int width, int height, Pixel color)
402 SDLFillRectangle(bitmap, x, y, width, height, color);
404 if (bitmap == backbuffer)
405 SetRedrawMaskFromArea(x, y, width, height);
408 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
409 int src_x, int src_y, int width, int height,
410 int dst_x, int dst_y, int mask_mode)
412 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
413 dst_x, dst_y, mask_mode);
415 if (dst_bitmap == backbuffer)
416 SetRedrawMaskFromArea(dst_x, dst_y, width, height);
419 void LimitScreenUpdates(boolean enable)
421 SDLLimitScreenUpdates(enable);
424 void InitVideoDisplay(void)
426 if (program.headless)
429 SDLInitVideoDisplay();
430 #if defined(TARGET_SDL2)
435 void CloseVideoDisplay(void)
437 KeyboardAutoRepeatOn();
439 SDL_QuitSubSystem(SDL_INIT_VIDEO);
442 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
445 video.height = height;
446 video.depth = GetRealDepth(depth);
448 video.screen_width = width;
449 video.screen_height = height;
450 video.screen_xoffset = 0;
451 video.screen_yoffset = 0;
453 video.fullscreen_available = FULLSCREEN_STATUS;
454 video.fullscreen_enabled = FALSE;
456 video.window_scaling_available = WINDOW_SCALING_STATUS;
458 video.frame_delay = 0;
459 video.frame_delay_value = GAME_FRAME_DELAY;
461 video.shifted_up = FALSE;
462 video.shifted_up_pos = 0;
463 video.shifted_up_pos_last = 0;
464 video.shifted_up_delay = 0;
465 video.shifted_up_delay_value = ONE_SECOND_DELAY / 4;
467 SDLInitVideoBuffer(fullscreen);
469 video.initialized = TRUE;
474 inline static void FreeBitmapPointers(Bitmap *bitmap)
479 SDLFreeBitmapPointers(bitmap);
481 checked_free(bitmap->source_filename);
482 bitmap->source_filename = NULL;
485 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
488 if (src_bitmap == NULL || dst_bitmap == NULL)
491 FreeBitmapPointers(dst_bitmap);
493 *dst_bitmap = *src_bitmap;
496 void FreeBitmap(Bitmap *bitmap)
501 FreeBitmapPointers(bitmap);
506 Bitmap *CreateBitmapStruct(void)
508 return checked_calloc(sizeof(Bitmap));
511 Bitmap *CreateBitmap(int width, int height, int depth)
513 Bitmap *new_bitmap = CreateBitmapStruct();
514 int real_width = MAX(1, width); /* prevent zero bitmap width */
515 int real_height = MAX(1, height); /* prevent zero bitmap height */
516 int real_depth = GetRealDepth(depth);
518 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
520 new_bitmap->width = real_width;
521 new_bitmap->height = real_height;
526 void ReCreateBitmap(Bitmap **bitmap, int width, int height)
528 Bitmap *new_bitmap = CreateBitmap(width, height, DEFAULT_DEPTH);
532 *bitmap = new_bitmap;
536 TransferBitmapPointers(new_bitmap, *bitmap);
541 void CloseWindow(DrawWindow *window)
545 void SetRedrawMaskFromArea(int x, int y, int width, int height)
549 int x2 = x + width - 1;
550 int y2 = y + height - 1;
552 if (width == 0 || height == 0)
555 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
556 redraw_mask |= REDRAW_FIELD;
557 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
558 redraw_mask |= REDRAW_DOOR_1;
559 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
560 redraw_mask |= REDRAW_DOOR_2;
561 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
562 redraw_mask |= REDRAW_DOOR_3;
564 redraw_mask = REDRAW_ALL;
567 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
570 if (draw_mask == REDRAW_NONE)
573 if (draw_mask & REDRAW_ALL)
576 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
579 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
582 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
585 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
591 boolean DrawingDeactivated(int x, int y, int width, int height)
593 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
596 boolean DrawingOnBackground(int x, int y)
598 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
599 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
602 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
603 int *width, int *height, boolean is_dest)
605 int clip_x, clip_y, clip_width, clip_height;
607 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
609 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
610 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
611 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
612 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
618 clip_width = bitmap->width;
619 clip_height = bitmap->height;
622 /* skip if rectangle completely outside bitmap */
624 if (*x + *width <= clip_x ||
625 *y + *height <= clip_y ||
626 *x >= clip_x + clip_width ||
627 *y >= clip_y + clip_height)
630 /* clip if rectangle overlaps bitmap */
634 *width -= clip_x - *x;
637 else if (*x + *width > clip_x + clip_width)
639 *width = clip_x + clip_width - *x;
644 *height -= clip_y - *y;
647 else if (*y + *height > clip_y + clip_height)
649 *height = clip_y + clip_height - *y;
655 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
656 int src_x, int src_y, int width, int height,
657 int dst_x, int dst_y)
659 int dst_x_unclipped = dst_x;
660 int dst_y_unclipped = dst_y;
662 if (program.headless)
665 if (src_bitmap == NULL || dst_bitmap == NULL)
668 if (DrawingDeactivated(dst_x, dst_y, width, height))
671 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
672 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
675 /* source x/y might need adjustment if destination x/y was clipped top/left */
676 src_x += dst_x - dst_x_unclipped;
677 src_y += dst_y - dst_y_unclipped;
679 #if defined(TARGET_SDL2)
680 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
681 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
682 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
683 but is already fixed in SVN and should therefore finally be fixed with
684 the next official SDL release, which is probably version 1.2.14.) */
685 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
687 if (src_bitmap == dst_bitmap)
689 /* needed when blitting directly to same bitmap -- should not be needed with
690 recent SDL libraries, but apparently does not work in 1.2.11 directly */
692 static Bitmap *tmp_bitmap = NULL;
693 static int tmp_bitmap_xsize = 0;
694 static int tmp_bitmap_ysize = 0;
696 /* start with largest static bitmaps for initial bitmap size ... */
697 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
699 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
700 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
703 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
704 if (src_bitmap->width > tmp_bitmap_xsize ||
705 src_bitmap->height > tmp_bitmap_ysize)
707 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
708 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
710 FreeBitmap(tmp_bitmap);
715 if (tmp_bitmap == NULL)
716 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
719 sysCopyArea(src_bitmap, tmp_bitmap,
720 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
721 sysCopyArea(tmp_bitmap, dst_bitmap,
722 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
728 sysCopyArea(src_bitmap, dst_bitmap,
729 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
732 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
733 int src_x, int src_y, int src_width, int src_height,
734 int dst_x, int dst_y, int dst_width, int dst_height)
736 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
737 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
738 int dst_xsize = dst_width;
739 int dst_ysize = dst_height;
740 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
741 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
744 for (y = 0; y < src_ysteps; y++)
746 for (x = 0; x < src_xsteps; x++)
748 int draw_x = dst_x + x * src_xsize;
749 int draw_y = dst_y + y * src_ysize;
750 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
751 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
753 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
759 void FadeRectangle(int x, int y, int width, int height,
760 int fade_mode, int fade_delay, int post_delay,
761 void (*draw_border_function)(void))
763 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
764 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
767 SDLFadeRectangle(x, y, width, height,
768 fade_mode, fade_delay, post_delay, draw_border_function);
771 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
774 if (DrawingDeactivated(x, y, width, height))
777 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
780 sysFillRectangle(bitmap, x, y, width, height, color);
783 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
785 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
788 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
789 int width, int height)
791 if (DrawingOnBackground(x, y))
792 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
794 ClearRectangle(bitmap, x, y, width, height);
797 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
798 int src_x, int src_y, int width, int height,
799 int dst_x, int dst_y)
801 if (DrawingDeactivated(dst_x, dst_y, width, height))
804 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
805 dst_x, dst_y, BLIT_MASKED);
808 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
809 int src_x, int src_y, int width, int height,
810 int dst_x, int dst_y)
812 if (DrawingOnBackground(dst_x, dst_y))
814 /* draw background */
815 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
818 /* draw foreground */
819 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
823 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
827 void BlitTexture(Bitmap *bitmap,
828 int src_x, int src_y, int width, int height,
829 int dst_x, int dst_y)
834 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
838 void BlitTextureMasked(Bitmap *bitmap,
839 int src_x, int src_y, int width, int height,
840 int dst_x, int dst_y)
845 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
849 void BlitToScreen(Bitmap *bitmap,
850 int src_x, int src_y, int width, int height,
851 int dst_x, int dst_y)
856 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
857 BlitBitmap(bitmap, gfx.final_screen_bitmap, src_x, src_y,
858 width, height, dst_x, dst_y);
860 BlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y);
863 void BlitToScreenMasked(Bitmap *bitmap,
864 int src_x, int src_y, int width, int height,
865 int dst_x, int dst_y)
870 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
871 BlitBitmapMasked(bitmap, gfx.final_screen_bitmap, src_x, src_y,
872 width, height, dst_x, dst_y);
874 BlitTextureMasked(bitmap, src_x, src_y, width, height, dst_x, dst_y);
877 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
880 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
883 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
886 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
889 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
890 int to_x, int to_y, Pixel pixel, int line_width)
894 for (x = 0; x < line_width; x++)
896 for (y = 0; y < line_width; y++)
898 int dx = x - line_width / 2;
899 int dy = y - line_width / 2;
901 if ((x == 0 && y == 0) ||
902 (x == 0 && y == line_width - 1) ||
903 (x == line_width - 1 && y == 0) ||
904 (x == line_width - 1 && y == line_width - 1))
908 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
913 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
918 for (i = 0; i < num_points - 1; i++)
919 DrawLine(bitmap, points[i].x, points[i].y,
920 points[i + 1].x, points[i + 1].y, pixel, line_width);
923 SDLDrawLines(bitmap->surface, points, num_points, pixel);
927 Pixel GetPixel(Bitmap *bitmap, int x, int y)
929 if (x < 0 || x >= bitmap->width ||
930 y < 0 || y >= bitmap->height)
933 return SDLGetPixel(bitmap, x, y);
936 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
937 unsigned int color_g, unsigned int color_b)
939 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
942 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
944 unsigned int color_r = (color >> 16) & 0xff;
945 unsigned int color_g = (color >> 8) & 0xff;
946 unsigned int color_b = (color >> 0) & 0xff;
948 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
951 void KeyboardAutoRepeatOn(void)
953 #if defined(TARGET_SDL2)
954 keyrepeat_status = TRUE;
956 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
957 SDL_DEFAULT_REPEAT_INTERVAL / 2);
958 SDL_EnableUNICODE(1);
962 void KeyboardAutoRepeatOff(void)
964 #if defined(TARGET_SDL2)
965 keyrepeat_status = FALSE;
967 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
968 SDL_EnableUNICODE(0);
972 boolean SetVideoMode(boolean fullscreen)
974 return SDLSetVideoMode(fullscreen);
977 void SetVideoFrameDelay(unsigned int frame_delay_value)
979 video.frame_delay_value = frame_delay_value;
982 unsigned int GetVideoFrameDelay()
984 return video.frame_delay_value;
987 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
989 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
990 (!fullscreen && video.fullscreen_enabled))
991 fullscreen = SetVideoMode(fullscreen);
996 Bitmap *LoadImage(char *filename)
1000 new_bitmap = SDLLoadImage(filename);
1003 new_bitmap->source_filename = getStringCopy(filename);
1008 Bitmap *LoadCustomImage(char *basename)
1010 char *filename = getCustomImageFilename(basename);
1013 if (filename == NULL)
1014 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
1016 if ((new_bitmap = LoadImage(filename)) == NULL)
1017 Error(ERR_EXIT, "LoadImage('%s') failed: %s", basename, GetError());
1022 void ReloadCustomImage(Bitmap *bitmap, char *basename)
1024 char *filename = getCustomImageFilename(basename);
1027 if (filename == NULL) /* (should never happen) */
1029 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
1033 if (strEqual(filename, bitmap->source_filename))
1035 /* The old and new image are the same (have the same filename and path).
1036 This usually means that this image does not exist in this graphic set
1037 and a fallback to the existing image is done. */
1042 if ((new_bitmap = LoadImage(filename)) == NULL)
1044 Error(ERR_WARN, "LoadImage('%s') failed: %s", basename, GetError());
1048 if (bitmap->width != new_bitmap->width ||
1049 bitmap->height != new_bitmap->height)
1051 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
1053 FreeBitmap(new_bitmap);
1057 TransferBitmapPointers(new_bitmap, bitmap);
1061 static Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
1063 return SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
1066 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
1068 if (bitmaps[IMG_BITMAP_CUSTOM])
1070 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
1072 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
1075 if (gfx.game_tile_size == gfx.standard_tile_size)
1077 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1082 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
1083 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
1084 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
1086 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
1088 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
1089 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
1092 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1093 int tile_size, boolean create_small_bitmaps)
1095 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1096 Bitmap *tmp_bitmap_final = NULL;
1097 Bitmap *tmp_bitmap_0 = NULL;
1098 Bitmap *tmp_bitmap_1 = NULL;
1099 Bitmap *tmp_bitmap_2 = NULL;
1100 Bitmap *tmp_bitmap_4 = NULL;
1101 Bitmap *tmp_bitmap_8 = NULL;
1102 Bitmap *tmp_bitmap_16 = NULL;
1103 Bitmap *tmp_bitmap_32 = NULL;
1104 int width_final, height_final;
1105 int width_0, height_0;
1106 int width_1, height_1;
1107 int width_2, height_2;
1108 int width_4, height_4;
1109 int width_8, height_8;
1110 int width_16, height_16;
1111 int width_32, height_32;
1112 int old_width, old_height;
1115 print_timestamp_init("CreateScaledBitmaps");
1117 old_width = old_bitmap->width;
1118 old_height = old_bitmap->height;
1120 /* calculate new image dimensions for final image size */
1121 width_final = old_width * zoom_factor;
1122 height_final = old_height * zoom_factor;
1124 /* get image with final size (this might require scaling up) */
1125 /* ("final" size may result in non-standard tile size image) */
1126 if (zoom_factor != 1)
1127 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1129 tmp_bitmap_final = old_bitmap;
1131 UPDATE_BUSY_STATE();
1133 width_0 = width_1 = width_final;
1134 height_0 = height_1 = height_final;
1136 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1138 if (create_small_bitmaps)
1140 /* check if we have a non-gameplay tile size image */
1141 if (tile_size != gfx.game_tile_size)
1143 /* get image with gameplay tile size */
1144 width_0 = width_final * gfx.game_tile_size / tile_size;
1145 height_0 = height_final * gfx.game_tile_size / tile_size;
1147 if (width_0 == old_width)
1148 tmp_bitmap_0 = old_bitmap;
1149 else if (width_0 == width_final)
1150 tmp_bitmap_0 = tmp_bitmap_final;
1152 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1154 UPDATE_BUSY_STATE();
1157 /* check if we have a non-standard tile size image */
1158 if (tile_size != gfx.standard_tile_size)
1160 /* get image with standard tile size */
1161 width_1 = width_final * gfx.standard_tile_size / tile_size;
1162 height_1 = height_final * gfx.standard_tile_size / tile_size;
1164 if (width_1 == old_width)
1165 tmp_bitmap_1 = old_bitmap;
1166 else if (width_1 == width_final)
1167 tmp_bitmap_1 = tmp_bitmap_final;
1168 else if (width_1 == width_0)
1169 tmp_bitmap_1 = tmp_bitmap_0;
1171 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1173 UPDATE_BUSY_STATE();
1176 /* calculate new image dimensions for small images */
1177 width_2 = width_1 / 2;
1178 height_2 = height_1 / 2;
1179 width_4 = width_1 / 4;
1180 height_4 = height_1 / 4;
1181 width_8 = width_1 / 8;
1182 height_8 = height_1 / 8;
1183 width_16 = width_1 / 16;
1184 height_16 = height_1 / 16;
1185 width_32 = width_1 / 32;
1186 height_32 = height_1 / 32;
1188 /* get image with 1/2 of normal size (for use in the level editor) */
1189 if (width_2 == old_width)
1190 tmp_bitmap_2 = old_bitmap;
1192 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1194 UPDATE_BUSY_STATE();
1196 /* get image with 1/4 of normal size (for use in the level editor) */
1197 if (width_4 == old_width)
1198 tmp_bitmap_4 = old_bitmap;
1200 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1202 UPDATE_BUSY_STATE();
1204 /* get image with 1/8 of normal size (for use on the preview screen) */
1205 if (width_8 == old_width)
1206 tmp_bitmap_8 = old_bitmap;
1208 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1210 UPDATE_BUSY_STATE();
1212 /* get image with 1/16 of normal size (for use on the preview screen) */
1213 if (width_16 == old_width)
1214 tmp_bitmap_16 = old_bitmap;
1216 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1218 UPDATE_BUSY_STATE();
1220 /* get image with 1/32 of normal size (for use on the preview screen) */
1221 if (width_32 == old_width)
1222 tmp_bitmap_32 = old_bitmap;
1224 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1226 UPDATE_BUSY_STATE();
1228 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1229 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1230 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1231 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1232 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1233 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1235 if (width_0 != width_1)
1236 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1238 if (bitmaps[IMG_BITMAP_CUSTOM])
1239 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1241 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1243 boolean free_old_bitmap = TRUE;
1245 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1246 if (bitmaps[i] == old_bitmap)
1247 free_old_bitmap = FALSE;
1249 if (free_old_bitmap)
1250 FreeBitmap(old_bitmap);
1254 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1257 UPDATE_BUSY_STATE();
1259 print_timestamp_done("CreateScaledBitmaps");
1262 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1265 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1268 void CreateBitmapTextures(Bitmap **bitmaps)
1270 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1273 void FreeBitmapTextures(Bitmap **bitmaps)
1275 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1278 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1280 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1284 /* ------------------------------------------------------------------------- */
1285 /* mouse pointer functions */
1286 /* ------------------------------------------------------------------------- */
1288 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1290 /* XPM image definitions */
1291 static const char *cursor_image_none[] =
1293 /* width height num_colors chars_per_pixel */
1323 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1324 static const char *cursor_image_dot[] =
1326 /* width height num_colors chars_per_pixel */
1355 static const char **cursor_image_playfield = cursor_image_dot;
1357 /* some people complained about a "white dot" on the screen and thought it
1358 was a graphical error... OK, let's just remove the whole pointer :-) */
1359 static const char **cursor_image_playfield = cursor_image_none;
1362 static const int cursor_bit_order = BIT_ORDER_MSB;
1364 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1366 struct MouseCursorInfo *cursor;
1367 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1368 int header_lines = 4;
1371 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1373 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1376 for (y = 0; y < cursor->width; y++)
1378 for (x = 0; x < cursor->height; x++)
1381 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1386 cursor->data[i] = cursor->mask[i] = 0;
1389 switch (image[header_lines + y][x])
1392 cursor->data[i] |= bit_mask;
1393 cursor->mask[i] |= bit_mask;
1397 cursor->mask[i] |= bit_mask;
1406 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1411 void SetMouseCursor(int mode)
1413 static struct MouseCursorInfo *cursor_none = NULL;
1414 static struct MouseCursorInfo *cursor_playfield = NULL;
1415 struct MouseCursorInfo *cursor_new;
1417 if (cursor_none == NULL)
1418 cursor_none = get_cursor_from_image(cursor_image_none);
1420 if (cursor_playfield == NULL)
1421 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1423 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1424 mode == CURSOR_NONE ? cursor_none :
1425 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1427 SDLSetMouseCursor(cursor_new);
1429 gfx.cursor_mode = mode;
1433 /* ========================================================================= */
1434 /* audio functions */
1435 /* ========================================================================= */
1437 void OpenAudio(void)
1439 /* always start with reliable default values */
1440 audio.sound_available = FALSE;
1441 audio.music_available = FALSE;
1442 audio.loops_available = FALSE;
1444 audio.sound_enabled = FALSE;
1445 audio.sound_deactivated = FALSE;
1447 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1448 audio.mixer_pid = 0;
1449 audio.device_name = NULL;
1450 audio.device_fd = -1;
1452 audio.num_channels = 0;
1453 audio.music_channel = 0;
1454 audio.first_sound_channel = 0;
1459 void CloseAudio(void)
1463 audio.sound_enabled = FALSE;
1466 void SetAudioMode(boolean enabled)
1468 if (!audio.sound_available)
1471 audio.sound_enabled = enabled;
1475 /* ========================================================================= */
1476 /* event functions */
1477 /* ========================================================================= */
1479 boolean PendingEvent(void)
1481 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1484 void NextEvent(Event *event)
1486 SDLNextEvent(event);
1489 void PeekEvent(Event *event)
1491 #if defined(TARGET_SDL2)
1492 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1494 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1498 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1500 #if defined(TARGET_SDL2)
1501 /* key up/down events in SDL2 do not return text characters anymore */
1502 return event->keysym.sym;
1505 #if ENABLE_UNUSED_CODE
1506 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1507 (int)event->keysym.unicode,
1508 (int)event->keysym.sym,
1509 (int)SDL_GetModState());
1512 if (with_modifiers &&
1513 event->keysym.unicode > 0x0000 &&
1514 event->keysym.unicode < 0x2000)
1515 return event->keysym.unicode;
1517 return event->keysym.sym;
1522 KeyMod HandleKeyModState(Key key, int key_status)
1524 static KeyMod current_modifiers = KMOD_None;
1526 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1528 KeyMod new_modifier = KMOD_None;
1533 new_modifier = KMOD_Shift_L;
1536 new_modifier = KMOD_Shift_R;
1538 case KSYM_Control_L:
1539 new_modifier = KMOD_Control_L;
1541 case KSYM_Control_R:
1542 new_modifier = KMOD_Control_R;
1545 new_modifier = KMOD_Meta_L;
1548 new_modifier = KMOD_Meta_R;
1551 new_modifier = KMOD_Alt_L;
1554 new_modifier = KMOD_Alt_R;
1560 if (key_status == KEY_PRESSED)
1561 current_modifiers |= new_modifier;
1563 current_modifiers &= ~new_modifier;
1566 return current_modifiers;
1569 KeyMod GetKeyModState()
1571 return (KeyMod)SDL_GetModState();
1574 KeyMod GetKeyModStateFromEvents()
1576 /* always use key modifier state as tracked from key events (this is needed
1577 if the modifier key event was injected into the event queue, but the key
1578 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1579 query the keys as held pressed on the keyboard) -- this case is currently
1580 only used to filter out clipboard insert events from "True X-Mouse" tool */
1582 return HandleKeyModState(KSYM_UNDEFINED, 0);
1585 void StartTextInput(int x, int y, int width, int height)
1587 #if defined(TARGET_SDL2)
1588 #if defined(HAS_SCREEN_KEYBOARD)
1589 SDL_StartTextInput();
1591 if (y + height > SCREEN_KEYBOARD_POS(video.height))
1593 video.shifted_up_pos = y + height - SCREEN_KEYBOARD_POS(video.height);
1594 video.shifted_up_delay = SDL_GetTicks();
1595 video.shifted_up = TRUE;
1601 void StopTextInput()
1603 #if defined(TARGET_SDL2)
1604 #if defined(HAS_SCREEN_KEYBOARD)
1605 SDL_StopTextInput();
1607 if (video.shifted_up)
1609 video.shifted_up_pos = 0;
1610 video.shifted_up_delay = SDL_GetTicks();
1611 video.shifted_up = FALSE;
1617 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1619 if (event->type != EVENT_CLIENTMESSAGE)
1622 return TRUE; /* the only possible message here is SDL_QUIT */
1626 /* ========================================================================= */
1627 /* joystick functions */
1628 /* ========================================================================= */
1630 void InitJoysticks()
1634 #if defined(NO_JOYSTICK)
1635 return; /* joysticks generally deactivated by compile-time directive */
1638 /* always start with reliable default values */
1639 joystick.status = JOYSTICK_NOT_AVAILABLE;
1640 for (i = 0; i < MAX_PLAYERS; i++)
1641 joystick.nr[i] = -1; /* no joystick configured */
1646 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1648 return SDLReadJoystick(nr, x, y, b1, b2);
1651 boolean CheckJoystickOpened(int nr)
1653 return SDLCheckJoystickOpened(nr);
1656 void ClearJoystickState()
1658 SDLClearJoystickState();