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 SetWindowTitle()
103 program.window_title = program.window_title_function();
108 void InitWindowTitleFunction(char *(*window_title_function)(void))
110 program.window_title_function = window_title_function;
113 void InitExitMessageFunction(void (*exit_message_function)(char *, va_list))
115 program.exit_message_function = exit_message_function;
118 void InitExitFunction(void (*exit_function)(int))
120 program.exit_function = exit_function;
122 /* set signal handlers to custom exit function */
123 // signal(SIGINT, exit_function);
124 signal(SIGTERM, exit_function);
126 /* set exit function to automatically cleanup SDL stuff after exit() */
130 void InitPlatformDependentStuff(void)
132 // this is initialized in GetOptions(), but may already be used before
133 options.verbose = TRUE;
137 #if defined(TARGET_SDL2)
138 int sdl_init_flags = SDL_INIT_EVENTS | SDL_INIT_NOPARACHUTE;
140 int sdl_init_flags = SDL_INIT_EVENTTHREAD | SDL_INIT_NOPARACHUTE;
143 if (SDL_Init(sdl_init_flags) < 0)
144 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
149 void ClosePlatformDependentStuff(void)
154 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
155 int real_sx, int real_sy,
156 int full_sxsize, int full_sysize,
157 Bitmap *field_save_buffer)
163 gfx.real_sx = real_sx;
164 gfx.real_sy = real_sy;
165 gfx.full_sxsize = full_sxsize;
166 gfx.full_sysize = full_sysize;
168 gfx.field_save_buffer = field_save_buffer;
170 SetDrawDeactivationMask(REDRAW_NONE); /* do not deactivate drawing */
171 SetDrawBackgroundMask(REDRAW_NONE); /* deactivate masked drawing */
174 void InitGfxTileSizeInfo(int game_tile_size, int standard_tile_size)
176 gfx.game_tile_size = game_tile_size;
177 gfx.standard_tile_size = standard_tile_size;
180 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
188 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
196 void InitGfxDoor3Info(int ex, int ey, int exsize, int eysize)
204 void InitGfxWindowInfo(int win_xsize, int win_ysize)
206 if (win_xsize != gfx.win_xsize || win_ysize != gfx.win_ysize)
208 ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize);
210 #if defined(TARGET_SDL2)
211 ReCreateBitmap(&gfx.final_screen_bitmap, win_xsize, win_ysize);
214 ReCreateBitmap(&gfx.fade_bitmap_backup, win_xsize, win_ysize);
215 ReCreateBitmap(&gfx.fade_bitmap_source, win_xsize, win_ysize);
216 ReCreateBitmap(&gfx.fade_bitmap_target, win_xsize, win_ysize);
217 ReCreateBitmap(&gfx.fade_bitmap_black, win_xsize, win_ysize);
219 ClearRectangle(gfx.fade_bitmap_black, 0, 0, win_xsize, win_ysize);
222 gfx.win_xsize = win_xsize;
223 gfx.win_ysize = win_ysize;
225 gfx.background_bitmap_mask = REDRAW_NONE;
228 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
230 /* currently only used by MSDOS code to alloc VRAM buffer, if available */
231 /* 2009-03-24: also (temporarily?) used for overlapping blit workaround */
232 gfx.scrollbuffer_width = scrollbuffer_width;
233 gfx.scrollbuffer_height = scrollbuffer_height;
236 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
238 gfx.clipping_enabled = enabled;
241 gfx.clip_width = width;
242 gfx.clip_height = height;
245 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
247 gfx.draw_busy_anim_function = draw_busy_anim_function;
250 void InitGfxDrawGlobalAnimFunction(void (*draw_global_anim_function)(int, int))
252 gfx.draw_global_anim_function = draw_global_anim_function;
255 void InitGfxDrawGlobalBorderFunction(void (*draw_global_border_function)(int))
257 gfx.draw_global_border_function = draw_global_border_function;
260 void InitGfxCustomArtworkInfo()
262 gfx.override_level_graphics = FALSE;
263 gfx.override_level_sounds = FALSE;
264 gfx.override_level_music = FALSE;
266 gfx.draw_init_text = TRUE;
269 void InitGfxOtherSettings()
271 gfx.cursor_mode = CURSOR_DEFAULT;
274 void InitOverlayInfo()
276 overlay.active = FALSE;
279 void SetOverlayActive(boolean active)
281 overlay.active = active;
284 boolean GetOverlayActive()
286 return overlay.active;
289 void SetDrawDeactivationMask(int draw_deactivation_mask)
291 gfx.draw_deactivation_mask = draw_deactivation_mask;
294 void SetDrawBackgroundMask(int draw_background_mask)
296 gfx.draw_background_mask = draw_background_mask;
299 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
301 if (background_bitmap_tile != NULL)
302 gfx.background_bitmap_mask |= mask;
304 gfx.background_bitmap_mask &= ~mask;
306 if (background_bitmap_tile == NULL) /* empty background requested */
309 if (mask == REDRAW_ALL)
310 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
311 0, 0, video.width, video.height);
312 else if (mask == REDRAW_FIELD)
313 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
314 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
315 else if (mask == REDRAW_DOOR_1)
316 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
317 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
320 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
322 /* remove every mask before setting mask for window */
323 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
324 SetBackgroundBitmap(NULL, 0xffff); /* !!! FIX THIS !!! */
325 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
328 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
330 /* remove window area mask before setting mask for main area */
331 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
332 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
333 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
336 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
338 /* remove window area mask before setting mask for door area */
339 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
340 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
341 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
345 /* ========================================================================= */
346 /* video functions */
347 /* ========================================================================= */
349 inline static int GetRealDepth(int depth)
351 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
354 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
355 int width, int height, Pixel color)
357 SDLFillRectangle(bitmap, x, y, width, height, color);
359 if (bitmap == backbuffer)
360 SetRedrawMaskFromArea(x, y, width, height);
363 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
364 int src_x, int src_y, int width, int height,
365 int dst_x, int dst_y, int mask_mode)
367 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
368 dst_x, dst_y, mask_mode);
370 if (dst_bitmap == backbuffer)
371 SetRedrawMaskFromArea(dst_x, dst_y, width, height);
374 void LimitScreenUpdates(boolean enable)
376 SDLLimitScreenUpdates(enable);
379 void InitVideoDisplay(void)
381 SDLInitVideoDisplay();
385 void CloseVideoDisplay(void)
387 KeyboardAutoRepeatOn();
389 SDL_QuitSubSystem(SDL_INIT_VIDEO);
392 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
395 video.height = height;
396 video.depth = GetRealDepth(depth);
398 video.screen_width = width;
399 video.screen_height = height;
400 video.screen_xoffset = 0;
401 video.screen_yoffset = 0;
403 video.fullscreen_available = FULLSCREEN_STATUS;
404 video.fullscreen_enabled = FALSE;
406 video.window_scaling_available = WINDOW_SCALING_STATUS;
408 video.frame_delay = 0;
409 video.frame_delay_value = GAME_FRAME_DELAY;
411 video.shifted_up = FALSE;
412 video.shifted_up_pos = 0;
413 video.shifted_up_pos_last = 0;
414 video.shifted_up_delay = 0;
415 video.shifted_up_delay_value = ONE_SECOND_DELAY / 4;
417 SDLInitVideoBuffer(fullscreen);
419 video.initialized = TRUE;
424 inline static void FreeBitmapPointers(Bitmap *bitmap)
429 SDLFreeBitmapPointers(bitmap);
431 checked_free(bitmap->source_filename);
432 bitmap->source_filename = NULL;
435 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
438 if (src_bitmap == NULL || dst_bitmap == NULL)
441 FreeBitmapPointers(dst_bitmap);
443 *dst_bitmap = *src_bitmap;
446 void FreeBitmap(Bitmap *bitmap)
451 FreeBitmapPointers(bitmap);
456 Bitmap *CreateBitmapStruct(void)
458 return checked_calloc(sizeof(Bitmap));
461 Bitmap *CreateBitmap(int width, int height, int depth)
463 Bitmap *new_bitmap = CreateBitmapStruct();
464 int real_width = MAX(1, width); /* prevent zero bitmap width */
465 int real_height = MAX(1, height); /* prevent zero bitmap height */
466 int real_depth = GetRealDepth(depth);
468 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
470 new_bitmap->width = real_width;
471 new_bitmap->height = real_height;
476 void ReCreateBitmap(Bitmap **bitmap, int width, int height)
478 Bitmap *new_bitmap = CreateBitmap(width, height, DEFAULT_DEPTH);
482 *bitmap = new_bitmap;
486 TransferBitmapPointers(new_bitmap, *bitmap);
491 void CloseWindow(DrawWindow *window)
495 void SetRedrawMaskFromArea(int x, int y, int width, int height)
499 int x2 = x + width - 1;
500 int y2 = y + height - 1;
502 if (width == 0 || height == 0)
505 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
506 redraw_mask |= REDRAW_FIELD;
507 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
508 redraw_mask |= REDRAW_DOOR_1;
509 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
510 redraw_mask |= REDRAW_DOOR_2;
511 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
512 redraw_mask |= REDRAW_DOOR_3;
514 redraw_mask = REDRAW_ALL;
517 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
520 if (draw_mask == REDRAW_NONE)
523 if (draw_mask & REDRAW_ALL)
526 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
529 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
532 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
535 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
541 boolean DrawingDeactivated(int x, int y, int width, int height)
543 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
546 boolean DrawingOnBackground(int x, int y)
548 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
549 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
552 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
553 int *width, int *height, boolean is_dest)
555 int clip_x, clip_y, clip_width, clip_height;
557 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
559 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
560 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
561 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
562 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
568 clip_width = bitmap->width;
569 clip_height = bitmap->height;
572 /* skip if rectangle completely outside bitmap */
574 if (*x + *width <= clip_x ||
575 *y + *height <= clip_y ||
576 *x >= clip_x + clip_width ||
577 *y >= clip_y + clip_height)
580 /* clip if rectangle overlaps bitmap */
584 *width -= clip_x - *x;
587 else if (*x + *width > clip_x + clip_width)
589 *width = clip_x + clip_width - *x;
594 *height -= clip_y - *y;
597 else if (*y + *height > clip_y + clip_height)
599 *height = clip_y + clip_height - *y;
605 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
606 int src_x, int src_y, int width, int height,
607 int dst_x, int dst_y)
609 int dst_x_unclipped = dst_x;
610 int dst_y_unclipped = dst_y;
612 if (src_bitmap == NULL || dst_bitmap == NULL)
615 if (DrawingDeactivated(dst_x, dst_y, width, height))
618 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
619 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
622 /* source x/y might need adjustment if destination x/y was clipped top/left */
623 src_x += dst_x - dst_x_unclipped;
624 src_y += dst_y - dst_y_unclipped;
626 #if defined(TARGET_SDL2)
627 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
628 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
629 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
630 but is already fixed in SVN and should therefore finally be fixed with
631 the next official SDL release, which is probably version 1.2.14.) */
632 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
634 if (src_bitmap == dst_bitmap)
636 /* needed when blitting directly to same bitmap -- should not be needed with
637 recent SDL libraries, but apparently does not work in 1.2.11 directly */
639 static Bitmap *tmp_bitmap = NULL;
640 static int tmp_bitmap_xsize = 0;
641 static int tmp_bitmap_ysize = 0;
643 /* start with largest static bitmaps for initial bitmap size ... */
644 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
646 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
647 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
650 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
651 if (src_bitmap->width > tmp_bitmap_xsize ||
652 src_bitmap->height > tmp_bitmap_ysize)
654 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
655 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
657 FreeBitmap(tmp_bitmap);
662 if (tmp_bitmap == NULL)
663 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
666 sysCopyArea(src_bitmap, tmp_bitmap,
667 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
668 sysCopyArea(tmp_bitmap, dst_bitmap,
669 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
675 sysCopyArea(src_bitmap, dst_bitmap,
676 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
679 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
680 int src_x, int src_y, int src_width, int src_height,
681 int dst_x, int dst_y, int dst_width, int dst_height)
683 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
684 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
685 int dst_xsize = dst_width;
686 int dst_ysize = dst_height;
687 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
688 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
691 for (y = 0; y < src_ysteps; y++)
693 for (x = 0; x < src_xsteps; x++)
695 int draw_x = dst_x + x * src_xsize;
696 int draw_y = dst_y + y * src_ysize;
697 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
698 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
700 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
706 void FadeRectangle(int x, int y, int width, int height,
707 int fade_mode, int fade_delay, int post_delay,
708 void (*draw_border_function)(void))
710 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
711 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
714 SDLFadeRectangle(x, y, width, height,
715 fade_mode, fade_delay, post_delay, draw_border_function);
718 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
721 if (DrawingDeactivated(x, y, width, height))
724 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
727 sysFillRectangle(bitmap, x, y, width, height, color);
730 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
732 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
735 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
736 int width, int height)
738 if (DrawingOnBackground(x, y))
739 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
741 ClearRectangle(bitmap, x, y, width, height);
744 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
745 int src_x, int src_y, int width, int height,
746 int dst_x, int dst_y)
748 if (DrawingDeactivated(dst_x, dst_y, width, height))
751 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
752 dst_x, dst_y, BLIT_MASKED);
755 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
756 int src_x, int src_y, int width, int height,
757 int dst_x, int dst_y)
759 if (DrawingOnBackground(dst_x, dst_y))
761 /* draw background */
762 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
765 /* draw foreground */
766 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
770 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
774 void BlitTexture(Bitmap *bitmap,
775 int src_x, int src_y, int width, int height,
776 int dst_x, int dst_y)
781 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
785 void BlitTextureMasked(Bitmap *bitmap,
786 int src_x, int src_y, int width, int height,
787 int dst_x, int dst_y)
792 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
796 void BlitToScreen(Bitmap *bitmap,
797 int src_x, int src_y, int width, int height,
798 int dst_x, int dst_y)
803 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
804 BlitBitmap(bitmap, gfx.final_screen_bitmap, src_x, src_y,
805 width, height, dst_x, dst_y);
807 BlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y);
810 void BlitToScreenMasked(Bitmap *bitmap,
811 int src_x, int src_y, int width, int height,
812 int dst_x, int dst_y)
817 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
818 BlitBitmapMasked(bitmap, gfx.final_screen_bitmap, src_x, src_y,
819 width, height, dst_x, dst_y);
821 BlitTextureMasked(bitmap, src_x, src_y, width, height, dst_x, dst_y);
824 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
827 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
830 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
833 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
836 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
837 int to_x, int to_y, Pixel pixel, int line_width)
841 for (x = 0; x < line_width; x++)
843 for (y = 0; y < line_width; y++)
845 int dx = x - line_width / 2;
846 int dy = y - line_width / 2;
848 if ((x == 0 && y == 0) ||
849 (x == 0 && y == line_width - 1) ||
850 (x == line_width - 1 && y == 0) ||
851 (x == line_width - 1 && y == line_width - 1))
855 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
860 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
865 for (i = 0; i < num_points - 1; i++)
866 DrawLine(bitmap, points[i].x, points[i].y,
867 points[i + 1].x, points[i + 1].y, pixel, line_width);
870 SDLDrawLines(bitmap->surface, points, num_points, pixel);
874 Pixel GetPixel(Bitmap *bitmap, int x, int y)
876 if (x < 0 || x >= bitmap->width ||
877 y < 0 || y >= bitmap->height)
880 return SDLGetPixel(bitmap, x, y);
883 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
884 unsigned int color_g, unsigned int color_b)
886 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
889 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
891 unsigned int color_r = (color >> 16) & 0xff;
892 unsigned int color_g = (color >> 8) & 0xff;
893 unsigned int color_b = (color >> 0) & 0xff;
895 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
898 void KeyboardAutoRepeatOn(void)
900 #if defined(TARGET_SDL2)
901 keyrepeat_status = TRUE;
903 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
904 SDL_DEFAULT_REPEAT_INTERVAL / 2);
905 SDL_EnableUNICODE(1);
909 void KeyboardAutoRepeatOff(void)
911 #if defined(TARGET_SDL2)
912 keyrepeat_status = FALSE;
914 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
915 SDL_EnableUNICODE(0);
919 boolean SetVideoMode(boolean fullscreen)
921 return SDLSetVideoMode(fullscreen);
924 void SetVideoFrameDelay(unsigned int frame_delay_value)
926 video.frame_delay_value = frame_delay_value;
929 unsigned int GetVideoFrameDelay()
931 return video.frame_delay_value;
934 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
936 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
937 (!fullscreen && video.fullscreen_enabled))
938 fullscreen = SetVideoMode(fullscreen);
943 Bitmap *LoadImage(char *filename)
947 new_bitmap = SDLLoadImage(filename);
950 new_bitmap->source_filename = getStringCopy(filename);
955 Bitmap *LoadCustomImage(char *basename)
957 char *filename = getCustomImageFilename(basename);
960 if (filename == NULL)
961 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
963 if ((new_bitmap = LoadImage(filename)) == NULL)
964 Error(ERR_EXIT, "LoadImage('%s') failed: %s", basename, GetError());
969 void ReloadCustomImage(Bitmap *bitmap, char *basename)
971 char *filename = getCustomImageFilename(basename);
974 if (filename == NULL) /* (should never happen) */
976 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
980 if (strEqual(filename, bitmap->source_filename))
982 /* The old and new image are the same (have the same filename and path).
983 This usually means that this image does not exist in this graphic set
984 and a fallback to the existing image is done. */
989 if ((new_bitmap = LoadImage(filename)) == NULL)
991 Error(ERR_WARN, "LoadImage('%s') failed: %s", basename, GetError());
995 if (bitmap->width != new_bitmap->width ||
996 bitmap->height != new_bitmap->height)
998 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
1000 FreeBitmap(new_bitmap);
1004 TransferBitmapPointers(new_bitmap, bitmap);
1008 static Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
1010 return SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
1013 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
1015 if (bitmaps[IMG_BITMAP_CUSTOM])
1017 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
1019 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
1022 if (gfx.game_tile_size == gfx.standard_tile_size)
1024 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1029 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
1030 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
1031 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
1033 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
1035 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
1036 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
1039 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1040 int tile_size, boolean create_small_bitmaps)
1042 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1043 Bitmap *tmp_bitmap_final = NULL;
1044 Bitmap *tmp_bitmap_0 = NULL;
1045 Bitmap *tmp_bitmap_1 = NULL;
1046 Bitmap *tmp_bitmap_2 = NULL;
1047 Bitmap *tmp_bitmap_4 = NULL;
1048 Bitmap *tmp_bitmap_8 = NULL;
1049 Bitmap *tmp_bitmap_16 = NULL;
1050 Bitmap *tmp_bitmap_32 = NULL;
1051 int width_final, height_final;
1052 int width_0, height_0;
1053 int width_1, height_1;
1054 int width_2, height_2;
1055 int width_4, height_4;
1056 int width_8, height_8;
1057 int width_16, height_16;
1058 int width_32, height_32;
1059 int old_width, old_height;
1062 print_timestamp_init("CreateScaledBitmaps");
1064 old_width = old_bitmap->width;
1065 old_height = old_bitmap->height;
1067 /* calculate new image dimensions for final image size */
1068 width_final = old_width * zoom_factor;
1069 height_final = old_height * zoom_factor;
1071 /* get image with final size (this might require scaling up) */
1072 /* ("final" size may result in non-standard tile size image) */
1073 if (zoom_factor != 1)
1074 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1076 tmp_bitmap_final = old_bitmap;
1078 UPDATE_BUSY_STATE();
1080 width_0 = width_1 = width_final;
1081 height_0 = height_1 = height_final;
1083 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1085 if (create_small_bitmaps)
1087 /* check if we have a non-gameplay tile size image */
1088 if (tile_size != gfx.game_tile_size)
1090 /* get image with gameplay tile size */
1091 width_0 = width_final * gfx.game_tile_size / tile_size;
1092 height_0 = height_final * gfx.game_tile_size / tile_size;
1094 if (width_0 == old_width)
1095 tmp_bitmap_0 = old_bitmap;
1096 else if (width_0 == width_final)
1097 tmp_bitmap_0 = tmp_bitmap_final;
1099 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1101 UPDATE_BUSY_STATE();
1104 /* check if we have a non-standard tile size image */
1105 if (tile_size != gfx.standard_tile_size)
1107 /* get image with standard tile size */
1108 width_1 = width_final * gfx.standard_tile_size / tile_size;
1109 height_1 = height_final * gfx.standard_tile_size / tile_size;
1111 if (width_1 == old_width)
1112 tmp_bitmap_1 = old_bitmap;
1113 else if (width_1 == width_final)
1114 tmp_bitmap_1 = tmp_bitmap_final;
1115 else if (width_1 == width_0)
1116 tmp_bitmap_1 = tmp_bitmap_0;
1118 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1120 UPDATE_BUSY_STATE();
1123 /* calculate new image dimensions for small images */
1124 width_2 = width_1 / 2;
1125 height_2 = height_1 / 2;
1126 width_4 = width_1 / 4;
1127 height_4 = height_1 / 4;
1128 width_8 = width_1 / 8;
1129 height_8 = height_1 / 8;
1130 width_16 = width_1 / 16;
1131 height_16 = height_1 / 16;
1132 width_32 = width_1 / 32;
1133 height_32 = height_1 / 32;
1135 /* get image with 1/2 of normal size (for use in the level editor) */
1136 if (width_2 == old_width)
1137 tmp_bitmap_2 = old_bitmap;
1139 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1141 UPDATE_BUSY_STATE();
1143 /* get image with 1/4 of normal size (for use in the level editor) */
1144 if (width_4 == old_width)
1145 tmp_bitmap_4 = old_bitmap;
1147 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1149 UPDATE_BUSY_STATE();
1151 /* get image with 1/8 of normal size (for use on the preview screen) */
1152 if (width_8 == old_width)
1153 tmp_bitmap_8 = old_bitmap;
1155 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1157 UPDATE_BUSY_STATE();
1159 /* get image with 1/16 of normal size (for use on the preview screen) */
1160 if (width_16 == old_width)
1161 tmp_bitmap_16 = old_bitmap;
1163 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1165 UPDATE_BUSY_STATE();
1167 /* get image with 1/32 of normal size (for use on the preview screen) */
1168 if (width_32 == old_width)
1169 tmp_bitmap_32 = old_bitmap;
1171 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1173 UPDATE_BUSY_STATE();
1175 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1176 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1177 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1178 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1179 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1180 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1182 if (width_0 != width_1)
1183 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1185 if (bitmaps[IMG_BITMAP_CUSTOM])
1186 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1188 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1190 boolean free_old_bitmap = TRUE;
1192 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1193 if (bitmaps[i] == old_bitmap)
1194 free_old_bitmap = FALSE;
1196 if (free_old_bitmap)
1197 FreeBitmap(old_bitmap);
1201 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1204 UPDATE_BUSY_STATE();
1206 print_timestamp_done("CreateScaledBitmaps");
1209 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1212 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1215 void CreateBitmapTextures(Bitmap **bitmaps)
1217 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1220 void FreeBitmapTextures(Bitmap **bitmaps)
1222 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1225 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1227 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1231 /* ------------------------------------------------------------------------- */
1232 /* mouse pointer functions */
1233 /* ------------------------------------------------------------------------- */
1235 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1237 /* XPM image definitions */
1238 static const char *cursor_image_none[] =
1240 /* width height num_colors chars_per_pixel */
1270 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1271 static const char *cursor_image_dot[] =
1273 /* width height num_colors chars_per_pixel */
1302 static const char **cursor_image_playfield = cursor_image_dot;
1304 /* some people complained about a "white dot" on the screen and thought it
1305 was a graphical error... OK, let's just remove the whole pointer :-) */
1306 static const char **cursor_image_playfield = cursor_image_none;
1309 static const int cursor_bit_order = BIT_ORDER_MSB;
1311 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1313 struct MouseCursorInfo *cursor;
1314 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1315 int header_lines = 4;
1318 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1320 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1323 for (y = 0; y < cursor->width; y++)
1325 for (x = 0; x < cursor->height; x++)
1328 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1333 cursor->data[i] = cursor->mask[i] = 0;
1336 switch (image[header_lines + y][x])
1339 cursor->data[i] |= bit_mask;
1340 cursor->mask[i] |= bit_mask;
1344 cursor->mask[i] |= bit_mask;
1353 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1358 void SetMouseCursor(int mode)
1360 static struct MouseCursorInfo *cursor_none = NULL;
1361 static struct MouseCursorInfo *cursor_playfield = NULL;
1362 struct MouseCursorInfo *cursor_new;
1364 if (cursor_none == NULL)
1365 cursor_none = get_cursor_from_image(cursor_image_none);
1367 if (cursor_playfield == NULL)
1368 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1370 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1371 mode == CURSOR_NONE ? cursor_none :
1372 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1374 SDLSetMouseCursor(cursor_new);
1376 gfx.cursor_mode = mode;
1380 /* ========================================================================= */
1381 /* audio functions */
1382 /* ========================================================================= */
1384 void OpenAudio(void)
1386 /* always start with reliable default values */
1387 audio.sound_available = FALSE;
1388 audio.music_available = FALSE;
1389 audio.loops_available = FALSE;
1391 audio.sound_enabled = FALSE;
1392 audio.sound_deactivated = FALSE;
1394 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1395 audio.mixer_pid = 0;
1396 audio.device_name = NULL;
1397 audio.device_fd = -1;
1399 audio.num_channels = 0;
1400 audio.music_channel = 0;
1401 audio.first_sound_channel = 0;
1406 void CloseAudio(void)
1410 audio.sound_enabled = FALSE;
1413 void SetAudioMode(boolean enabled)
1415 if (!audio.sound_available)
1418 audio.sound_enabled = enabled;
1422 /* ========================================================================= */
1423 /* event functions */
1424 /* ========================================================================= */
1426 boolean PendingEvent(void)
1428 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1431 void NextEvent(Event *event)
1433 SDLNextEvent(event);
1436 void PeekEvent(Event *event)
1438 #if defined(TARGET_SDL2)
1439 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1441 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1445 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1447 #if defined(TARGET_SDL2)
1448 /* key up/down events in SDL2 do not return text characters anymore */
1449 return event->keysym.sym;
1452 #if ENABLE_UNUSED_CODE
1453 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1454 (int)event->keysym.unicode,
1455 (int)event->keysym.sym,
1456 (int)SDL_GetModState());
1459 if (with_modifiers &&
1460 event->keysym.unicode > 0x0000 &&
1461 event->keysym.unicode < 0x2000)
1462 return event->keysym.unicode;
1464 return event->keysym.sym;
1469 KeyMod HandleKeyModState(Key key, int key_status)
1471 static KeyMod current_modifiers = KMOD_None;
1473 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1475 KeyMod new_modifier = KMOD_None;
1480 new_modifier = KMOD_Shift_L;
1483 new_modifier = KMOD_Shift_R;
1485 case KSYM_Control_L:
1486 new_modifier = KMOD_Control_L;
1488 case KSYM_Control_R:
1489 new_modifier = KMOD_Control_R;
1492 new_modifier = KMOD_Meta_L;
1495 new_modifier = KMOD_Meta_R;
1498 new_modifier = KMOD_Alt_L;
1501 new_modifier = KMOD_Alt_R;
1507 if (key_status == KEY_PRESSED)
1508 current_modifiers |= new_modifier;
1510 current_modifiers &= ~new_modifier;
1513 return current_modifiers;
1516 KeyMod GetKeyModState()
1518 return (KeyMod)SDL_GetModState();
1521 KeyMod GetKeyModStateFromEvents()
1523 /* always use key modifier state as tracked from key events (this is needed
1524 if the modifier key event was injected into the event queue, but the key
1525 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1526 query the keys as held pressed on the keyboard) -- this case is currently
1527 only used to filter out clipboard insert events from "True X-Mouse" tool */
1529 return HandleKeyModState(KSYM_UNDEFINED, 0);
1532 void StartTextInput(int x, int y, int width, int height)
1534 #if defined(TARGET_SDL2)
1535 SDL_StartTextInput();
1537 #if defined(HAS_SCREEN_KEYBOARD)
1538 if (y + height > SCREEN_KEYBOARD_POS(video.height))
1540 video.shifted_up_pos = y + height - SCREEN_KEYBOARD_POS(video.height);
1541 video.shifted_up_delay = SDL_GetTicks();
1542 video.shifted_up = TRUE;
1548 void StopTextInput()
1550 #if defined(TARGET_SDL2)
1551 SDL_StopTextInput();
1553 #if defined(HAS_SCREEN_KEYBOARD)
1554 if (video.shifted_up)
1556 video.shifted_up_pos = 0;
1557 video.shifted_up_delay = SDL_GetTicks();
1558 video.shifted_up = FALSE;
1564 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1566 if (event->type != EVENT_CLIENTMESSAGE)
1569 return TRUE; /* the only possible message here is SDL_QUIT */
1573 /* ========================================================================= */
1574 /* joystick functions */
1575 /* ========================================================================= */
1577 void InitJoysticks()
1581 #if defined(NO_JOYSTICK)
1582 return; /* joysticks generally deactivated by compile-time directive */
1585 /* always start with reliable default values */
1586 joystick.status = JOYSTICK_NOT_AVAILABLE;
1587 for (i = 0; i < MAX_PLAYERS; i++)
1588 joystick.fd[i] = -1; /* joystick device closed */
1593 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1595 return SDLReadJoystick(nr, x, y, b1, b2);