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;
101 void InitScoresInfo()
103 char *global_scores_dir = getPath2(getCommonDataDir(), SCORES_DIRECTORY);
105 program.global_scores = directoryExists(global_scores_dir);
106 program.many_scores_per_name = !program.global_scores;
108 free(global_scores_dir);
111 void SetWindowTitle()
113 program.window_title = program.window_title_function();
118 void InitWindowTitleFunction(char *(*window_title_function)(void))
120 program.window_title_function = window_title_function;
123 void InitExitMessageFunction(void (*exit_message_function)(char *, va_list))
125 program.exit_message_function = exit_message_function;
128 void InitExitFunction(void (*exit_function)(int))
130 program.exit_function = exit_function;
132 /* set signal handlers to custom exit function */
133 // signal(SIGINT, exit_function);
134 signal(SIGTERM, exit_function);
136 /* set exit function to automatically cleanup SDL stuff after exit() */
140 void InitPlatformDependentStuff(void)
142 // this is initialized in GetOptions(), but may already be used before
143 options.verbose = TRUE;
147 #if defined(TARGET_SDL2)
148 int sdl_init_flags = SDL_INIT_EVENTS | SDL_INIT_NOPARACHUTE;
150 int sdl_init_flags = SDL_INIT_EVENTTHREAD | SDL_INIT_NOPARACHUTE;
153 if (SDL_Init(sdl_init_flags) < 0)
154 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
159 void ClosePlatformDependentStuff(void)
164 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
165 int real_sx, int real_sy,
166 int full_sxsize, int full_sysize,
167 Bitmap *field_save_buffer)
173 gfx.real_sx = real_sx;
174 gfx.real_sy = real_sy;
175 gfx.full_sxsize = full_sxsize;
176 gfx.full_sysize = full_sysize;
178 gfx.field_save_buffer = field_save_buffer;
180 SetDrawDeactivationMask(REDRAW_NONE); /* do not deactivate drawing */
181 SetDrawBackgroundMask(REDRAW_NONE); /* deactivate masked drawing */
184 void InitGfxTileSizeInfo(int game_tile_size, int standard_tile_size)
186 gfx.game_tile_size = game_tile_size;
187 gfx.standard_tile_size = standard_tile_size;
190 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
198 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
206 void InitGfxDoor3Info(int ex, int ey, int exsize, int eysize)
214 void InitGfxWindowInfo(int win_xsize, int win_ysize)
216 if (win_xsize != gfx.win_xsize || win_ysize != gfx.win_ysize)
218 ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize);
220 #if defined(TARGET_SDL2)
221 ReCreateBitmap(&gfx.final_screen_bitmap, win_xsize, win_ysize);
224 ReCreateBitmap(&gfx.fade_bitmap_backup, win_xsize, win_ysize);
225 ReCreateBitmap(&gfx.fade_bitmap_source, win_xsize, win_ysize);
226 ReCreateBitmap(&gfx.fade_bitmap_target, win_xsize, win_ysize);
227 ReCreateBitmap(&gfx.fade_bitmap_black, win_xsize, win_ysize);
229 ClearRectangle(gfx.fade_bitmap_black, 0, 0, win_xsize, win_ysize);
232 gfx.win_xsize = win_xsize;
233 gfx.win_ysize = win_ysize;
235 gfx.background_bitmap_mask = REDRAW_NONE;
238 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
240 /* currently only used by MSDOS code to alloc VRAM buffer, if available */
241 /* 2009-03-24: also (temporarily?) used for overlapping blit workaround */
242 gfx.scrollbuffer_width = scrollbuffer_width;
243 gfx.scrollbuffer_height = scrollbuffer_height;
246 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
248 gfx.clipping_enabled = enabled;
251 gfx.clip_width = width;
252 gfx.clip_height = height;
255 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
257 gfx.draw_busy_anim_function = draw_busy_anim_function;
260 void InitGfxDrawGlobalAnimFunction(void (*draw_global_anim_function)(int, int))
262 gfx.draw_global_anim_function = draw_global_anim_function;
265 void InitGfxDrawGlobalBorderFunction(void (*draw_global_border_function)(int))
267 gfx.draw_global_border_function = draw_global_border_function;
270 void InitGfxCustomArtworkInfo()
272 gfx.override_level_graphics = FALSE;
273 gfx.override_level_sounds = FALSE;
274 gfx.override_level_music = FALSE;
276 gfx.draw_init_text = TRUE;
279 void InitGfxOtherSettings()
281 gfx.cursor_mode = CURSOR_DEFAULT;
284 void InitOverlayInfo()
286 overlay.active = FALSE;
289 void SetOverlayActive(boolean active)
291 overlay.active = active;
294 boolean GetOverlayActive()
296 return overlay.active;
299 void SetDrawDeactivationMask(int draw_deactivation_mask)
301 gfx.draw_deactivation_mask = draw_deactivation_mask;
304 void SetDrawBackgroundMask(int draw_background_mask)
306 gfx.draw_background_mask = draw_background_mask;
309 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
311 if (background_bitmap_tile != NULL)
312 gfx.background_bitmap_mask |= mask;
314 gfx.background_bitmap_mask &= ~mask;
316 if (background_bitmap_tile == NULL) /* empty background requested */
319 if (mask == REDRAW_ALL)
320 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
321 0, 0, video.width, video.height);
322 else if (mask == REDRAW_FIELD)
323 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
324 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
325 else if (mask == REDRAW_DOOR_1)
326 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
327 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
330 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
332 /* remove every mask before setting mask for window */
333 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
334 SetBackgroundBitmap(NULL, 0xffff); /* !!! FIX THIS !!! */
335 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
338 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
340 /* remove window area mask before setting mask for main area */
341 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
342 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
343 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
346 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
348 /* remove window area mask before setting mask for door area */
349 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
350 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
351 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
355 /* ========================================================================= */
356 /* video functions */
357 /* ========================================================================= */
359 inline static int GetRealDepth(int depth)
361 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
364 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
365 int width, int height, Pixel color)
367 SDLFillRectangle(bitmap, x, y, width, height, color);
369 if (bitmap == backbuffer)
370 SetRedrawMaskFromArea(x, y, width, height);
373 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
374 int src_x, int src_y, int width, int height,
375 int dst_x, int dst_y, int mask_mode)
377 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
378 dst_x, dst_y, mask_mode);
380 if (dst_bitmap == backbuffer)
381 SetRedrawMaskFromArea(dst_x, dst_y, width, height);
384 void LimitScreenUpdates(boolean enable)
386 SDLLimitScreenUpdates(enable);
389 void InitVideoDisplay(void)
391 SDLInitVideoDisplay();
392 #if defined(TARGET_SDL2)
397 void CloseVideoDisplay(void)
399 KeyboardAutoRepeatOn();
401 SDL_QuitSubSystem(SDL_INIT_VIDEO);
404 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
407 video.height = height;
408 video.depth = GetRealDepth(depth);
410 video.screen_width = width;
411 video.screen_height = height;
412 video.screen_xoffset = 0;
413 video.screen_yoffset = 0;
415 video.fullscreen_available = FULLSCREEN_STATUS;
416 video.fullscreen_enabled = FALSE;
418 video.window_scaling_available = WINDOW_SCALING_STATUS;
420 video.frame_delay = 0;
421 video.frame_delay_value = GAME_FRAME_DELAY;
423 video.shifted_up = FALSE;
424 video.shifted_up_pos = 0;
425 video.shifted_up_pos_last = 0;
426 video.shifted_up_delay = 0;
427 video.shifted_up_delay_value = ONE_SECOND_DELAY / 4;
429 SDLInitVideoBuffer(fullscreen);
431 video.initialized = TRUE;
436 inline static void FreeBitmapPointers(Bitmap *bitmap)
441 SDLFreeBitmapPointers(bitmap);
443 checked_free(bitmap->source_filename);
444 bitmap->source_filename = NULL;
447 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
450 if (src_bitmap == NULL || dst_bitmap == NULL)
453 FreeBitmapPointers(dst_bitmap);
455 *dst_bitmap = *src_bitmap;
458 void FreeBitmap(Bitmap *bitmap)
463 FreeBitmapPointers(bitmap);
468 Bitmap *CreateBitmapStruct(void)
470 return checked_calloc(sizeof(Bitmap));
473 Bitmap *CreateBitmap(int width, int height, int depth)
475 Bitmap *new_bitmap = CreateBitmapStruct();
476 int real_width = MAX(1, width); /* prevent zero bitmap width */
477 int real_height = MAX(1, height); /* prevent zero bitmap height */
478 int real_depth = GetRealDepth(depth);
480 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
482 new_bitmap->width = real_width;
483 new_bitmap->height = real_height;
488 void ReCreateBitmap(Bitmap **bitmap, int width, int height)
490 Bitmap *new_bitmap = CreateBitmap(width, height, DEFAULT_DEPTH);
494 *bitmap = new_bitmap;
498 TransferBitmapPointers(new_bitmap, *bitmap);
503 void CloseWindow(DrawWindow *window)
507 void SetRedrawMaskFromArea(int x, int y, int width, int height)
511 int x2 = x + width - 1;
512 int y2 = y + height - 1;
514 if (width == 0 || height == 0)
517 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
518 redraw_mask |= REDRAW_FIELD;
519 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
520 redraw_mask |= REDRAW_DOOR_1;
521 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
522 redraw_mask |= REDRAW_DOOR_2;
523 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
524 redraw_mask |= REDRAW_DOOR_3;
526 redraw_mask = REDRAW_ALL;
529 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
532 if (draw_mask == REDRAW_NONE)
535 if (draw_mask & REDRAW_ALL)
538 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
541 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
544 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
547 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
553 boolean DrawingDeactivated(int x, int y, int width, int height)
555 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
558 boolean DrawingOnBackground(int x, int y)
560 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
561 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
564 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
565 int *width, int *height, boolean is_dest)
567 int clip_x, clip_y, clip_width, clip_height;
569 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
571 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
572 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
573 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
574 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
580 clip_width = bitmap->width;
581 clip_height = bitmap->height;
584 /* skip if rectangle completely outside bitmap */
586 if (*x + *width <= clip_x ||
587 *y + *height <= clip_y ||
588 *x >= clip_x + clip_width ||
589 *y >= clip_y + clip_height)
592 /* clip if rectangle overlaps bitmap */
596 *width -= clip_x - *x;
599 else if (*x + *width > clip_x + clip_width)
601 *width = clip_x + clip_width - *x;
606 *height -= clip_y - *y;
609 else if (*y + *height > clip_y + clip_height)
611 *height = clip_y + clip_height - *y;
617 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
618 int src_x, int src_y, int width, int height,
619 int dst_x, int dst_y)
621 int dst_x_unclipped = dst_x;
622 int dst_y_unclipped = dst_y;
624 if (src_bitmap == NULL || dst_bitmap == NULL)
627 if (DrawingDeactivated(dst_x, dst_y, width, height))
630 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
631 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
634 /* source x/y might need adjustment if destination x/y was clipped top/left */
635 src_x += dst_x - dst_x_unclipped;
636 src_y += dst_y - dst_y_unclipped;
638 #if defined(TARGET_SDL2)
639 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
640 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
641 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
642 but is already fixed in SVN and should therefore finally be fixed with
643 the next official SDL release, which is probably version 1.2.14.) */
644 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
646 if (src_bitmap == dst_bitmap)
648 /* needed when blitting directly to same bitmap -- should not be needed with
649 recent SDL libraries, but apparently does not work in 1.2.11 directly */
651 static Bitmap *tmp_bitmap = NULL;
652 static int tmp_bitmap_xsize = 0;
653 static int tmp_bitmap_ysize = 0;
655 /* start with largest static bitmaps for initial bitmap size ... */
656 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
658 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
659 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
662 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
663 if (src_bitmap->width > tmp_bitmap_xsize ||
664 src_bitmap->height > tmp_bitmap_ysize)
666 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
667 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
669 FreeBitmap(tmp_bitmap);
674 if (tmp_bitmap == NULL)
675 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
678 sysCopyArea(src_bitmap, tmp_bitmap,
679 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
680 sysCopyArea(tmp_bitmap, dst_bitmap,
681 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
687 sysCopyArea(src_bitmap, dst_bitmap,
688 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
691 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
692 int src_x, int src_y, int src_width, int src_height,
693 int dst_x, int dst_y, int dst_width, int dst_height)
695 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
696 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
697 int dst_xsize = dst_width;
698 int dst_ysize = dst_height;
699 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
700 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
703 for (y = 0; y < src_ysteps; y++)
705 for (x = 0; x < src_xsteps; x++)
707 int draw_x = dst_x + x * src_xsize;
708 int draw_y = dst_y + y * src_ysize;
709 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
710 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
712 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
718 void FadeRectangle(int x, int y, int width, int height,
719 int fade_mode, int fade_delay, int post_delay,
720 void (*draw_border_function)(void))
722 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
723 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
726 SDLFadeRectangle(x, y, width, height,
727 fade_mode, fade_delay, post_delay, draw_border_function);
730 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
733 if (DrawingDeactivated(x, y, width, height))
736 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
739 sysFillRectangle(bitmap, x, y, width, height, color);
742 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
744 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
747 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
748 int width, int height)
750 if (DrawingOnBackground(x, y))
751 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
753 ClearRectangle(bitmap, x, y, width, height);
756 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
757 int src_x, int src_y, int width, int height,
758 int dst_x, int dst_y)
760 if (DrawingDeactivated(dst_x, dst_y, width, height))
763 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
764 dst_x, dst_y, BLIT_MASKED);
767 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
768 int src_x, int src_y, int width, int height,
769 int dst_x, int dst_y)
771 if (DrawingOnBackground(dst_x, dst_y))
773 /* draw background */
774 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
777 /* draw foreground */
778 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
782 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
786 void BlitTexture(Bitmap *bitmap,
787 int src_x, int src_y, int width, int height,
788 int dst_x, int dst_y)
793 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
797 void BlitTextureMasked(Bitmap *bitmap,
798 int src_x, int src_y, int width, int height,
799 int dst_x, int dst_y)
804 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
808 void BlitToScreen(Bitmap *bitmap,
809 int src_x, int src_y, int width, int height,
810 int dst_x, int dst_y)
815 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
816 BlitBitmap(bitmap, gfx.final_screen_bitmap, src_x, src_y,
817 width, height, dst_x, dst_y);
819 BlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y);
822 void BlitToScreenMasked(Bitmap *bitmap,
823 int src_x, int src_y, int width, int height,
824 int dst_x, int dst_y)
829 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
830 BlitBitmapMasked(bitmap, gfx.final_screen_bitmap, src_x, src_y,
831 width, height, dst_x, dst_y);
833 BlitTextureMasked(bitmap, src_x, src_y, width, height, dst_x, dst_y);
836 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
839 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
842 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
845 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
848 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
849 int to_x, int to_y, Pixel pixel, int line_width)
853 for (x = 0; x < line_width; x++)
855 for (y = 0; y < line_width; y++)
857 int dx = x - line_width / 2;
858 int dy = y - line_width / 2;
860 if ((x == 0 && y == 0) ||
861 (x == 0 && y == line_width - 1) ||
862 (x == line_width - 1 && y == 0) ||
863 (x == line_width - 1 && y == line_width - 1))
867 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
872 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
877 for (i = 0; i < num_points - 1; i++)
878 DrawLine(bitmap, points[i].x, points[i].y,
879 points[i + 1].x, points[i + 1].y, pixel, line_width);
882 SDLDrawLines(bitmap->surface, points, num_points, pixel);
886 Pixel GetPixel(Bitmap *bitmap, int x, int y)
888 if (x < 0 || x >= bitmap->width ||
889 y < 0 || y >= bitmap->height)
892 return SDLGetPixel(bitmap, x, y);
895 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
896 unsigned int color_g, unsigned int color_b)
898 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
901 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
903 unsigned int color_r = (color >> 16) & 0xff;
904 unsigned int color_g = (color >> 8) & 0xff;
905 unsigned int color_b = (color >> 0) & 0xff;
907 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
910 void KeyboardAutoRepeatOn(void)
912 #if defined(TARGET_SDL2)
913 keyrepeat_status = TRUE;
915 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
916 SDL_DEFAULT_REPEAT_INTERVAL / 2);
917 SDL_EnableUNICODE(1);
921 void KeyboardAutoRepeatOff(void)
923 #if defined(TARGET_SDL2)
924 keyrepeat_status = FALSE;
926 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
927 SDL_EnableUNICODE(0);
931 boolean SetVideoMode(boolean fullscreen)
933 return SDLSetVideoMode(fullscreen);
936 void SetVideoFrameDelay(unsigned int frame_delay_value)
938 video.frame_delay_value = frame_delay_value;
941 unsigned int GetVideoFrameDelay()
943 return video.frame_delay_value;
946 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
948 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
949 (!fullscreen && video.fullscreen_enabled))
950 fullscreen = SetVideoMode(fullscreen);
955 Bitmap *LoadImage(char *filename)
959 new_bitmap = SDLLoadImage(filename);
962 new_bitmap->source_filename = getStringCopy(filename);
967 Bitmap *LoadCustomImage(char *basename)
969 char *filename = getCustomImageFilename(basename);
972 if (filename == NULL)
973 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
975 if ((new_bitmap = LoadImage(filename)) == NULL)
976 Error(ERR_EXIT, "LoadImage('%s') failed: %s", basename, GetError());
981 void ReloadCustomImage(Bitmap *bitmap, char *basename)
983 char *filename = getCustomImageFilename(basename);
986 if (filename == NULL) /* (should never happen) */
988 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
992 if (strEqual(filename, bitmap->source_filename))
994 /* The old and new image are the same (have the same filename and path).
995 This usually means that this image does not exist in this graphic set
996 and a fallback to the existing image is done. */
1001 if ((new_bitmap = LoadImage(filename)) == NULL)
1003 Error(ERR_WARN, "LoadImage('%s') failed: %s", basename, GetError());
1007 if (bitmap->width != new_bitmap->width ||
1008 bitmap->height != new_bitmap->height)
1010 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
1012 FreeBitmap(new_bitmap);
1016 TransferBitmapPointers(new_bitmap, bitmap);
1020 static Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
1022 return SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
1025 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
1027 if (bitmaps[IMG_BITMAP_CUSTOM])
1029 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
1031 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
1034 if (gfx.game_tile_size == gfx.standard_tile_size)
1036 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1041 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
1042 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
1043 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
1045 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
1047 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
1048 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
1051 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1052 int tile_size, boolean create_small_bitmaps)
1054 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1055 Bitmap *tmp_bitmap_final = NULL;
1056 Bitmap *tmp_bitmap_0 = NULL;
1057 Bitmap *tmp_bitmap_1 = NULL;
1058 Bitmap *tmp_bitmap_2 = NULL;
1059 Bitmap *tmp_bitmap_4 = NULL;
1060 Bitmap *tmp_bitmap_8 = NULL;
1061 Bitmap *tmp_bitmap_16 = NULL;
1062 Bitmap *tmp_bitmap_32 = NULL;
1063 int width_final, height_final;
1064 int width_0, height_0;
1065 int width_1, height_1;
1066 int width_2, height_2;
1067 int width_4, height_4;
1068 int width_8, height_8;
1069 int width_16, height_16;
1070 int width_32, height_32;
1071 int old_width, old_height;
1074 print_timestamp_init("CreateScaledBitmaps");
1076 old_width = old_bitmap->width;
1077 old_height = old_bitmap->height;
1079 /* calculate new image dimensions for final image size */
1080 width_final = old_width * zoom_factor;
1081 height_final = old_height * zoom_factor;
1083 /* get image with final size (this might require scaling up) */
1084 /* ("final" size may result in non-standard tile size image) */
1085 if (zoom_factor != 1)
1086 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1088 tmp_bitmap_final = old_bitmap;
1090 UPDATE_BUSY_STATE();
1092 width_0 = width_1 = width_final;
1093 height_0 = height_1 = height_final;
1095 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1097 if (create_small_bitmaps)
1099 /* check if we have a non-gameplay tile size image */
1100 if (tile_size != gfx.game_tile_size)
1102 /* get image with gameplay tile size */
1103 width_0 = width_final * gfx.game_tile_size / tile_size;
1104 height_0 = height_final * gfx.game_tile_size / tile_size;
1106 if (width_0 == old_width)
1107 tmp_bitmap_0 = old_bitmap;
1108 else if (width_0 == width_final)
1109 tmp_bitmap_0 = tmp_bitmap_final;
1111 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1113 UPDATE_BUSY_STATE();
1116 /* check if we have a non-standard tile size image */
1117 if (tile_size != gfx.standard_tile_size)
1119 /* get image with standard tile size */
1120 width_1 = width_final * gfx.standard_tile_size / tile_size;
1121 height_1 = height_final * gfx.standard_tile_size / tile_size;
1123 if (width_1 == old_width)
1124 tmp_bitmap_1 = old_bitmap;
1125 else if (width_1 == width_final)
1126 tmp_bitmap_1 = tmp_bitmap_final;
1127 else if (width_1 == width_0)
1128 tmp_bitmap_1 = tmp_bitmap_0;
1130 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1132 UPDATE_BUSY_STATE();
1135 /* calculate new image dimensions for small images */
1136 width_2 = width_1 / 2;
1137 height_2 = height_1 / 2;
1138 width_4 = width_1 / 4;
1139 height_4 = height_1 / 4;
1140 width_8 = width_1 / 8;
1141 height_8 = height_1 / 8;
1142 width_16 = width_1 / 16;
1143 height_16 = height_1 / 16;
1144 width_32 = width_1 / 32;
1145 height_32 = height_1 / 32;
1147 /* get image with 1/2 of normal size (for use in the level editor) */
1148 if (width_2 == old_width)
1149 tmp_bitmap_2 = old_bitmap;
1151 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1153 UPDATE_BUSY_STATE();
1155 /* get image with 1/4 of normal size (for use in the level editor) */
1156 if (width_4 == old_width)
1157 tmp_bitmap_4 = old_bitmap;
1159 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1161 UPDATE_BUSY_STATE();
1163 /* get image with 1/8 of normal size (for use on the preview screen) */
1164 if (width_8 == old_width)
1165 tmp_bitmap_8 = old_bitmap;
1167 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1169 UPDATE_BUSY_STATE();
1171 /* get image with 1/16 of normal size (for use on the preview screen) */
1172 if (width_16 == old_width)
1173 tmp_bitmap_16 = old_bitmap;
1175 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1177 UPDATE_BUSY_STATE();
1179 /* get image with 1/32 of normal size (for use on the preview screen) */
1180 if (width_32 == old_width)
1181 tmp_bitmap_32 = old_bitmap;
1183 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1185 UPDATE_BUSY_STATE();
1187 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1188 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1189 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1190 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1191 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1192 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1194 if (width_0 != width_1)
1195 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1197 if (bitmaps[IMG_BITMAP_CUSTOM])
1198 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1200 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1202 boolean free_old_bitmap = TRUE;
1204 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1205 if (bitmaps[i] == old_bitmap)
1206 free_old_bitmap = FALSE;
1208 if (free_old_bitmap)
1209 FreeBitmap(old_bitmap);
1213 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1216 UPDATE_BUSY_STATE();
1218 print_timestamp_done("CreateScaledBitmaps");
1221 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1224 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1227 void CreateBitmapTextures(Bitmap **bitmaps)
1229 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1232 void FreeBitmapTextures(Bitmap **bitmaps)
1234 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1237 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1239 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1243 /* ------------------------------------------------------------------------- */
1244 /* mouse pointer functions */
1245 /* ------------------------------------------------------------------------- */
1247 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1249 /* XPM image definitions */
1250 static const char *cursor_image_none[] =
1252 /* width height num_colors chars_per_pixel */
1282 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1283 static const char *cursor_image_dot[] =
1285 /* width height num_colors chars_per_pixel */
1314 static const char **cursor_image_playfield = cursor_image_dot;
1316 /* some people complained about a "white dot" on the screen and thought it
1317 was a graphical error... OK, let's just remove the whole pointer :-) */
1318 static const char **cursor_image_playfield = cursor_image_none;
1321 static const int cursor_bit_order = BIT_ORDER_MSB;
1323 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1325 struct MouseCursorInfo *cursor;
1326 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1327 int header_lines = 4;
1330 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1332 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1335 for (y = 0; y < cursor->width; y++)
1337 for (x = 0; x < cursor->height; x++)
1340 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1345 cursor->data[i] = cursor->mask[i] = 0;
1348 switch (image[header_lines + y][x])
1351 cursor->data[i] |= bit_mask;
1352 cursor->mask[i] |= bit_mask;
1356 cursor->mask[i] |= bit_mask;
1365 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1370 void SetMouseCursor(int mode)
1372 static struct MouseCursorInfo *cursor_none = NULL;
1373 static struct MouseCursorInfo *cursor_playfield = NULL;
1374 struct MouseCursorInfo *cursor_new;
1376 if (cursor_none == NULL)
1377 cursor_none = get_cursor_from_image(cursor_image_none);
1379 if (cursor_playfield == NULL)
1380 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1382 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1383 mode == CURSOR_NONE ? cursor_none :
1384 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1386 SDLSetMouseCursor(cursor_new);
1388 gfx.cursor_mode = mode;
1392 /* ========================================================================= */
1393 /* audio functions */
1394 /* ========================================================================= */
1396 void OpenAudio(void)
1398 /* always start with reliable default values */
1399 audio.sound_available = FALSE;
1400 audio.music_available = FALSE;
1401 audio.loops_available = FALSE;
1403 audio.sound_enabled = FALSE;
1404 audio.sound_deactivated = FALSE;
1406 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1407 audio.mixer_pid = 0;
1408 audio.device_name = NULL;
1409 audio.device_fd = -1;
1411 audio.num_channels = 0;
1412 audio.music_channel = 0;
1413 audio.first_sound_channel = 0;
1418 void CloseAudio(void)
1422 audio.sound_enabled = FALSE;
1425 void SetAudioMode(boolean enabled)
1427 if (!audio.sound_available)
1430 audio.sound_enabled = enabled;
1434 /* ========================================================================= */
1435 /* event functions */
1436 /* ========================================================================= */
1438 boolean PendingEvent(void)
1440 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1443 void NextEvent(Event *event)
1445 SDLNextEvent(event);
1448 void PeekEvent(Event *event)
1450 #if defined(TARGET_SDL2)
1451 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1453 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1457 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1459 #if defined(TARGET_SDL2)
1460 /* key up/down events in SDL2 do not return text characters anymore */
1461 return event->keysym.sym;
1464 #if ENABLE_UNUSED_CODE
1465 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1466 (int)event->keysym.unicode,
1467 (int)event->keysym.sym,
1468 (int)SDL_GetModState());
1471 if (with_modifiers &&
1472 event->keysym.unicode > 0x0000 &&
1473 event->keysym.unicode < 0x2000)
1474 return event->keysym.unicode;
1476 return event->keysym.sym;
1481 KeyMod HandleKeyModState(Key key, int key_status)
1483 static KeyMod current_modifiers = KMOD_None;
1485 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1487 KeyMod new_modifier = KMOD_None;
1492 new_modifier = KMOD_Shift_L;
1495 new_modifier = KMOD_Shift_R;
1497 case KSYM_Control_L:
1498 new_modifier = KMOD_Control_L;
1500 case KSYM_Control_R:
1501 new_modifier = KMOD_Control_R;
1504 new_modifier = KMOD_Meta_L;
1507 new_modifier = KMOD_Meta_R;
1510 new_modifier = KMOD_Alt_L;
1513 new_modifier = KMOD_Alt_R;
1519 if (key_status == KEY_PRESSED)
1520 current_modifiers |= new_modifier;
1522 current_modifiers &= ~new_modifier;
1525 return current_modifiers;
1528 KeyMod GetKeyModState()
1530 return (KeyMod)SDL_GetModState();
1533 KeyMod GetKeyModStateFromEvents()
1535 /* always use key modifier state as tracked from key events (this is needed
1536 if the modifier key event was injected into the event queue, but the key
1537 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1538 query the keys as held pressed on the keyboard) -- this case is currently
1539 only used to filter out clipboard insert events from "True X-Mouse" tool */
1541 return HandleKeyModState(KSYM_UNDEFINED, 0);
1544 void StartTextInput(int x, int y, int width, int height)
1546 #if defined(TARGET_SDL2)
1547 SDL_StartTextInput();
1549 #if defined(HAS_SCREEN_KEYBOARD)
1550 if (y + height > SCREEN_KEYBOARD_POS(video.height))
1552 video.shifted_up_pos = y + height - SCREEN_KEYBOARD_POS(video.height);
1553 video.shifted_up_delay = SDL_GetTicks();
1554 video.shifted_up = TRUE;
1560 void StopTextInput()
1562 #if defined(TARGET_SDL2)
1563 SDL_StopTextInput();
1565 #if defined(HAS_SCREEN_KEYBOARD)
1566 if (video.shifted_up)
1568 video.shifted_up_pos = 0;
1569 video.shifted_up_delay = SDL_GetTicks();
1570 video.shifted_up = FALSE;
1576 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1578 if (event->type != EVENT_CLIENTMESSAGE)
1581 return TRUE; /* the only possible message here is SDL_QUIT */
1585 /* ========================================================================= */
1586 /* joystick functions */
1587 /* ========================================================================= */
1589 void InitJoysticks()
1593 #if defined(NO_JOYSTICK)
1594 return; /* joysticks generally deactivated by compile-time directive */
1597 /* always start with reliable default values */
1598 joystick.status = JOYSTICK_NOT_AVAILABLE;
1599 for (i = 0; i < MAX_PLAYERS; i++)
1600 joystick.fd[i] = -1; /* joystick device closed */
1605 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1607 return SDLReadJoystick(nr, x, y, b1, b2);