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 ArtworkInfo artwork;
37 struct JoystickInfo joystick;
38 struct SetupInfo setup;
40 LevelDirTree *leveldir_first_all = NULL;
41 LevelDirTree *leveldir_first = NULL;
42 LevelDirTree *leveldir_current = NULL;
45 struct LevelStats level_stats[MAX_LEVELS];
47 DrawWindow *window = NULL;
48 DrawBuffer *backbuffer = NULL;
49 DrawBuffer *drawto = NULL;
51 int button_status = MB_NOT_PRESSED;
52 boolean motion_status = FALSE;
53 #if defined(TARGET_SDL2)
54 boolean keyrepeat_status = TRUE;
57 int redraw_mask = REDRAW_NONE;
62 /* ========================================================================= */
63 /* init/close functions */
64 /* ========================================================================= */
66 void InitProgramInfo(char *argv0, char *config_filename, char *userdata_subdir,
67 char *program_title, char *icon_title,
68 char *icon_filename, char *cookie_prefix,
71 program.command_basepath = getBasePath(argv0);
72 program.command_basename = getBaseName(argv0);
74 program.config_filename = config_filename;
76 program.userdata_subdir = userdata_subdir;
77 program.userdata_path = getUserGameDataDir();
79 program.program_title = program_title;
80 program.window_title = "(undefined)";
81 program.icon_title = icon_title;
83 program.icon_filename = icon_filename;
85 program.cookie_prefix = cookie_prefix;
87 program.version_major = VERSION_MAJOR(program_version);
88 program.version_minor = VERSION_MINOR(program_version);
89 program.version_patch = VERSION_PATCH(program_version);
90 program.version_build = VERSION_BUILD(program_version);
91 program.version_ident = program_version;
93 program.log_filename[LOG_OUT_ID] = getLogFilename(LOG_OUT_BASENAME);
94 program.log_filename[LOG_ERR_ID] = getLogFilename(LOG_ERR_BASENAME);
95 program.log_file[LOG_OUT_ID] = program.log_file_default[LOG_OUT_ID] = stdout;
96 program.log_file[LOG_ERR_ID] = program.log_file_default[LOG_ERR_ID] = stderr;
101 program.window_title = program.window_title_function();
106 void InitWindowTitleFunction(char *(*window_title_function)(void))
108 program.window_title_function = window_title_function;
111 void InitExitMessageFunction(void (*exit_message_function)(char *, va_list))
113 program.exit_message_function = exit_message_function;
116 void InitExitFunction(void (*exit_function)(int))
118 program.exit_function = exit_function;
120 /* set signal handlers to custom exit function */
121 // signal(SIGINT, exit_function);
122 signal(SIGTERM, exit_function);
124 /* set exit function to automatically cleanup SDL stuff after exit() */
128 void InitPlatformDependentStuff(void)
130 // this is initialized in GetOptions(), but may already be used before
131 options.verbose = TRUE;
135 #if defined(TARGET_SDL2)
136 int sdl_init_flags = SDL_INIT_EVENTS | SDL_INIT_NOPARACHUTE;
138 int sdl_init_flags = SDL_INIT_EVENTTHREAD | SDL_INIT_NOPARACHUTE;
141 if (SDL_Init(sdl_init_flags) < 0)
142 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
147 void ClosePlatformDependentStuff(void)
152 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
153 int real_sx, int real_sy,
154 int full_sxsize, int full_sysize,
155 Bitmap *field_save_buffer)
161 gfx.real_sx = real_sx;
162 gfx.real_sy = real_sy;
163 gfx.full_sxsize = full_sxsize;
164 gfx.full_sysize = full_sysize;
166 gfx.field_save_buffer = field_save_buffer;
168 SetDrawDeactivationMask(REDRAW_NONE); /* do not deactivate drawing */
169 SetDrawBackgroundMask(REDRAW_NONE); /* deactivate masked drawing */
172 void InitGfxTileSizeInfo(int game_tile_size, int standard_tile_size)
174 gfx.game_tile_size = game_tile_size;
175 gfx.standard_tile_size = standard_tile_size;
178 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
186 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
194 void InitGfxDoor3Info(int ex, int ey, int exsize, int eysize)
202 void InitGfxWindowInfo(int win_xsize, int win_ysize)
204 gfx.win_xsize = win_xsize;
205 gfx.win_ysize = win_ysize;
207 gfx.background_bitmap_mask = REDRAW_NONE;
209 ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize, DEFAULT_DEPTH);
210 #if USE_FINAL_SCREEN_BITMAP
211 ReCreateBitmap(&gfx.final_screen_bitmap, win_xsize, win_ysize, DEFAULT_DEPTH);
215 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
217 /* currently only used by MSDOS code to alloc VRAM buffer, if available */
218 /* 2009-03-24: also (temporarily?) used for overlapping blit workaround */
219 gfx.scrollbuffer_width = scrollbuffer_width;
220 gfx.scrollbuffer_height = scrollbuffer_height;
223 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
225 gfx.clipping_enabled = enabled;
228 gfx.clip_width = width;
229 gfx.clip_height = height;
232 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
234 gfx.draw_busy_anim_function = draw_busy_anim_function;
237 void InitGfxDrawGlobalAnimFunction(void (*draw_global_anim_function)(int))
239 gfx.draw_global_anim_function = draw_global_anim_function;
242 void InitGfxDrawGlobalBorderFunction(void (*draw_global_border_function)(int))
244 gfx.draw_global_border_function = draw_global_border_function;
247 void InitGfxCustomArtworkInfo()
249 gfx.override_level_graphics = FALSE;
250 gfx.override_level_sounds = FALSE;
251 gfx.override_level_music = FALSE;
253 gfx.draw_init_text = TRUE;
256 void InitGfxOtherSettings()
258 gfx.cursor_mode = CURSOR_DEFAULT;
261 void SetDrawDeactivationMask(int draw_deactivation_mask)
263 gfx.draw_deactivation_mask = draw_deactivation_mask;
266 void SetDrawBackgroundMask(int draw_background_mask)
268 gfx.draw_background_mask = draw_background_mask;
271 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
273 if (background_bitmap_tile != NULL)
274 gfx.background_bitmap_mask |= mask;
276 gfx.background_bitmap_mask &= ~mask;
278 if (background_bitmap_tile == NULL) /* empty background requested */
281 if (mask == REDRAW_ALL)
282 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
283 0, 0, video.width, video.height);
284 else if (mask == REDRAW_FIELD)
285 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
286 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
287 else if (mask == REDRAW_DOOR_1)
288 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
289 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
292 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
294 /* remove every mask before setting mask for window */
295 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
296 SetBackgroundBitmap(NULL, 0xffff); /* !!! FIX THIS !!! */
297 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
300 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
302 /* remove window area mask before setting mask for main area */
303 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
304 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
305 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
308 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
310 /* remove window area mask before setting mask for door area */
311 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
312 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
313 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
317 /* ========================================================================= */
318 /* video functions */
319 /* ========================================================================= */
321 inline static int GetRealDepth(int depth)
323 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
326 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
327 int width, int height, Pixel color)
329 SDLFillRectangle(bitmap, x, y, width, height, color);
331 if (bitmap == backbuffer)
332 SetRedrawMaskFromArea(x, y, width, height);
335 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
336 int src_x, int src_y, int width, int height,
337 int dst_x, int dst_y, int mask_mode)
339 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
340 dst_x, dst_y, mask_mode);
342 if (dst_bitmap == backbuffer)
343 SetRedrawMaskFromArea(dst_x, dst_y, width, height);
346 void LimitScreenUpdates(boolean enable)
348 SDLLimitScreenUpdates(enable);
351 void InitVideoDisplay(void)
353 SDLInitVideoDisplay();
356 void CloseVideoDisplay(void)
358 KeyboardAutoRepeatOn();
360 SDL_QuitSubSystem(SDL_INIT_VIDEO);
363 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
366 video.height = height;
367 video.depth = GetRealDepth(depth);
369 video.fullscreen_available = FULLSCREEN_STATUS;
370 video.fullscreen_enabled = FALSE;
372 video.window_scaling_available = WINDOW_SCALING_STATUS;
374 SDLInitVideoBuffer(&backbuffer, &window, fullscreen);
376 video.initialized = TRUE;
381 inline static void FreeBitmapPointers(Bitmap *bitmap)
386 SDLFreeBitmapPointers(bitmap);
388 checked_free(bitmap->source_filename);
389 bitmap->source_filename = NULL;
392 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
395 if (src_bitmap == NULL || dst_bitmap == NULL)
398 FreeBitmapPointers(dst_bitmap);
400 *dst_bitmap = *src_bitmap;
403 void FreeBitmap(Bitmap *bitmap)
408 FreeBitmapPointers(bitmap);
413 Bitmap *CreateBitmapStruct(void)
415 return checked_calloc(sizeof(Bitmap));
418 Bitmap *CreateBitmap(int width, int height, int depth)
420 Bitmap *new_bitmap = CreateBitmapStruct();
421 int real_width = MAX(1, width); /* prevent zero bitmap width */
422 int real_height = MAX(1, height); /* prevent zero bitmap height */
423 int real_depth = GetRealDepth(depth);
425 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
427 new_bitmap->width = real_width;
428 new_bitmap->height = real_height;
433 void ReCreateBitmap(Bitmap **bitmap, int width, int height, int depth)
435 Bitmap *new_bitmap = CreateBitmap(width, height, depth);
439 *bitmap = new_bitmap;
443 TransferBitmapPointers(new_bitmap, *bitmap);
448 void CloseWindow(DrawWindow *window)
452 void SetRedrawMaskFromArea(int x, int y, int width, int height)
456 int x2 = x + width - 1;
457 int y2 = y + height - 1;
459 if (width == 0 || height == 0)
462 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
463 redraw_mask |= REDRAW_FIELD;
464 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
465 redraw_mask |= REDRAW_DOOR_1;
466 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
467 redraw_mask |= REDRAW_DOOR_2;
468 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
469 redraw_mask |= REDRAW_DOOR_3;
471 redraw_mask = REDRAW_ALL;
474 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
477 if (draw_mask == REDRAW_NONE)
480 if (draw_mask & REDRAW_ALL)
483 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
486 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
489 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
492 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
498 boolean DrawingDeactivated(int x, int y, int width, int height)
500 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
503 boolean DrawingOnBackground(int x, int y)
505 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
506 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
509 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
510 int *width, int *height, boolean is_dest)
512 int clip_x, clip_y, clip_width, clip_height;
514 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
516 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
517 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
518 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
519 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
525 clip_width = bitmap->width;
526 clip_height = bitmap->height;
529 /* skip if rectangle completely outside bitmap */
531 if (*x + *width <= clip_x ||
532 *y + *height <= clip_y ||
533 *x >= clip_x + clip_width ||
534 *y >= clip_y + clip_height)
537 /* clip if rectangle overlaps bitmap */
541 *width -= clip_x - *x;
544 else if (*x + *width > clip_x + clip_width)
546 *width = clip_x + clip_width - *x;
551 *height -= clip_y - *y;
554 else if (*y + *height > clip_y + clip_height)
556 *height = clip_y + clip_height - *y;
562 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
563 int src_x, int src_y, int width, int height,
564 int dst_x, int dst_y)
566 int dst_x_unclipped = dst_x;
567 int dst_y_unclipped = dst_y;
569 if (src_bitmap == NULL || dst_bitmap == NULL)
572 if (DrawingDeactivated(dst_x, dst_y, width, height))
575 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
576 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
579 /* source x/y might need adjustment if destination x/y was clipped top/left */
580 src_x += dst_x - dst_x_unclipped;
581 src_y += dst_y - dst_y_unclipped;
583 #if defined(TARGET_SDL2)
584 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
585 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
586 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
587 but is already fixed in SVN and should therefore finally be fixed with
588 the next official SDL release, which is probably version 1.2.14.) */
589 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
591 if (src_bitmap == dst_bitmap)
593 /* needed when blitting directly to same bitmap -- should not be needed with
594 recent SDL libraries, but apparently does not work in 1.2.11 directly */
596 static Bitmap *tmp_bitmap = NULL;
597 static int tmp_bitmap_xsize = 0;
598 static int tmp_bitmap_ysize = 0;
600 /* start with largest static bitmaps for initial bitmap size ... */
601 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
603 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
604 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
607 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
608 if (src_bitmap->width > tmp_bitmap_xsize ||
609 src_bitmap->height > tmp_bitmap_ysize)
611 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
612 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
614 FreeBitmap(tmp_bitmap);
619 if (tmp_bitmap == NULL)
620 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
623 sysCopyArea(src_bitmap, tmp_bitmap,
624 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
625 sysCopyArea(tmp_bitmap, dst_bitmap,
626 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
632 sysCopyArea(src_bitmap, dst_bitmap,
633 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
636 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
637 int src_x, int src_y, int src_width, int src_height,
638 int dst_x, int dst_y, int dst_width, int dst_height)
640 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
641 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
642 int dst_xsize = dst_width;
643 int dst_ysize = dst_height;
644 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
645 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
648 for (y = 0; y < src_ysteps; y++)
650 for (x = 0; x < src_xsteps; x++)
652 int draw_x = dst_x + x * src_xsize;
653 int draw_y = dst_y + y * src_ysize;
654 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
655 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
657 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
663 void FadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
664 int fade_mode, int fade_delay, int post_delay,
665 void (*draw_border_function)(void))
667 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
668 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
671 SDLFadeRectangle(bitmap_cross, x, y, width, height,
672 fade_mode, fade_delay, post_delay, draw_border_function);
675 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
678 if (DrawingDeactivated(x, y, width, height))
681 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
684 sysFillRectangle(bitmap, x, y, width, height, color);
687 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
689 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
692 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
693 int width, int height)
695 if (DrawingOnBackground(x, y))
696 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
698 ClearRectangle(bitmap, x, y, width, height);
701 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
702 int src_x, int src_y, int width, int height,
703 int dst_x, int dst_y)
705 if (DrawingDeactivated(dst_x, dst_y, width, height))
708 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
709 dst_x, dst_y, BLIT_MASKED);
712 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
713 int src_x, int src_y, int width, int height,
714 int dst_x, int dst_y)
716 if (DrawingOnBackground(dst_x, dst_y))
718 /* draw background */
719 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
722 /* draw foreground */
723 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
727 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
731 void BlitTexture(Bitmap *bitmap,
732 int src_x, int src_y, int width, int height,
733 int dst_x, int dst_y)
738 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
742 void BlitTextureMasked(Bitmap *bitmap,
743 int src_x, int src_y, int width, int height,
744 int dst_x, int dst_y)
749 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
753 void BlitToScreen(Bitmap *bitmap,
754 int src_x, int src_y, int width, int height,
755 int dst_x, int dst_y)
760 #if USE_FINAL_SCREEN_BITMAP
761 BlitBitmap(bitmap, gfx.final_screen_bitmap, src_x, src_y,
762 width, height, dst_x, dst_y);
764 BlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y);
768 void BlitToScreenMasked(Bitmap *bitmap,
769 int src_x, int src_y, int width, int height,
770 int dst_x, int dst_y)
775 #if USE_FINAL_SCREEN_BITMAP
776 BlitBitmapMasked(bitmap, gfx.final_screen_bitmap, src_x, src_y,
777 width, height, dst_x, dst_y);
779 BlitTextureMasked(bitmap, src_x, src_y, width, height, dst_x, dst_y);
783 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
786 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
789 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
792 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
795 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
796 int to_x, int to_y, Pixel pixel, int line_width)
800 for (x = 0; x < line_width; x++)
802 for (y = 0; y < line_width; y++)
804 int dx = x - line_width / 2;
805 int dy = y - line_width / 2;
807 if ((x == 0 && y == 0) ||
808 (x == 0 && y == line_width - 1) ||
809 (x == line_width - 1 && y == 0) ||
810 (x == line_width - 1 && y == line_width - 1))
814 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
819 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
824 for (i = 0; i < num_points - 1; i++)
825 DrawLine(bitmap, points[i].x, points[i].y,
826 points[i + 1].x, points[i + 1].y, pixel, line_width);
829 SDLDrawLines(bitmap->surface, points, num_points, pixel);
833 Pixel GetPixel(Bitmap *bitmap, int x, int y)
835 if (x < 0 || x >= bitmap->width ||
836 y < 0 || y >= bitmap->height)
839 return SDLGetPixel(bitmap, x, y);
842 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
843 unsigned int color_g, unsigned int color_b)
845 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
848 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
850 unsigned int color_r = (color >> 16) & 0xff;
851 unsigned int color_g = (color >> 8) & 0xff;
852 unsigned int color_b = (color >> 0) & 0xff;
854 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
857 void KeyboardAutoRepeatOn(void)
859 #if defined(TARGET_SDL2)
860 keyrepeat_status = TRUE;
862 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
863 SDL_DEFAULT_REPEAT_INTERVAL / 2);
864 SDL_EnableUNICODE(1);
868 void KeyboardAutoRepeatOff(void)
870 #if defined(TARGET_SDL2)
871 keyrepeat_status = FALSE;
873 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
874 SDL_EnableUNICODE(0);
878 boolean SetVideoMode(boolean fullscreen)
880 return SDLSetVideoMode(&backbuffer, fullscreen);
883 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
885 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
886 (!fullscreen && video.fullscreen_enabled))
887 fullscreen = SetVideoMode(fullscreen);
892 Bitmap *LoadImage(char *filename)
896 new_bitmap = SDLLoadImage(filename);
899 new_bitmap->source_filename = getStringCopy(filename);
904 Bitmap *LoadCustomImage(char *basename)
906 char *filename = getCustomImageFilename(basename);
909 if (filename == NULL)
910 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
912 if ((new_bitmap = LoadImage(filename)) == NULL)
913 Error(ERR_EXIT, "LoadImage('%s') failed: %s", basename, GetError());
918 void ReloadCustomImage(Bitmap *bitmap, char *basename)
920 char *filename = getCustomImageFilename(basename);
923 if (filename == NULL) /* (should never happen) */
925 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
929 if (strEqual(filename, bitmap->source_filename))
931 /* The old and new image are the same (have the same filename and path).
932 This usually means that this image does not exist in this graphic set
933 and a fallback to the existing image is done. */
938 if ((new_bitmap = LoadImage(filename)) == NULL)
940 Error(ERR_WARN, "LoadImage('%s') failed: %s", basename, GetError());
944 if (bitmap->width != new_bitmap->width ||
945 bitmap->height != new_bitmap->height)
947 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
949 FreeBitmap(new_bitmap);
953 TransferBitmapPointers(new_bitmap, bitmap);
957 Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
959 Bitmap *dst_bitmap = SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
964 static void SetMaskedBitmapSurface(Bitmap *bitmap)
969 SDL_Surface *surface = bitmap->surface;
971 if (bitmap->surface_masked)
972 SDL_FreeSurface(bitmap->surface_masked);
974 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
975 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
977 if ((bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
978 Error(ERR_EXIT, "SDL_DisplayFormat() failed");
980 SDL_SetColorKey(surface, UNSET_TRANSPARENT_PIXEL, 0);
983 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
985 if (bitmaps[IMG_BITMAP_CUSTOM])
987 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
989 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
992 if (gfx.game_tile_size == gfx.standard_tile_size)
994 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
999 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
1000 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
1001 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
1003 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
1005 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
1006 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
1008 SetMaskedBitmapSurface(bitmap_new);
1011 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1012 int tile_size, boolean create_small_bitmaps)
1014 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1015 Bitmap *tmp_bitmap_final = NULL;
1016 Bitmap *tmp_bitmap_0 = NULL;
1017 Bitmap *tmp_bitmap_1 = NULL;
1018 Bitmap *tmp_bitmap_2 = NULL;
1019 Bitmap *tmp_bitmap_4 = NULL;
1020 Bitmap *tmp_bitmap_8 = NULL;
1021 Bitmap *tmp_bitmap_16 = NULL;
1022 Bitmap *tmp_bitmap_32 = NULL;
1023 int width_final, height_final;
1024 int width_0, height_0;
1025 int width_1, height_1;
1026 int width_2, height_2;
1027 int width_4, height_4;
1028 int width_8, height_8;
1029 int width_16, height_16;
1030 int width_32, height_32;
1031 int old_width, old_height;
1034 print_timestamp_init("CreateScaledBitmaps");
1036 old_width = old_bitmap->width;
1037 old_height = old_bitmap->height;
1039 /* calculate new image dimensions for final image size */
1040 width_final = old_width * zoom_factor;
1041 height_final = old_height * zoom_factor;
1043 /* get image with final size (this might require scaling up) */
1044 /* ("final" size may result in non-standard tile size image) */
1045 if (zoom_factor != 1)
1046 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1048 tmp_bitmap_final = old_bitmap;
1050 UPDATE_BUSY_STATE();
1052 width_0 = width_1 = width_final;
1053 height_0 = height_1 = height_final;
1055 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1057 if (create_small_bitmaps)
1059 /* check if we have a non-gameplay tile size image */
1060 if (tile_size != gfx.game_tile_size)
1062 /* get image with gameplay tile size */
1063 width_0 = width_final * gfx.game_tile_size / tile_size;
1064 height_0 = height_final * gfx.game_tile_size / tile_size;
1066 if (width_0 == old_width)
1067 tmp_bitmap_0 = old_bitmap;
1068 else if (width_0 == width_final)
1069 tmp_bitmap_0 = tmp_bitmap_final;
1071 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1073 UPDATE_BUSY_STATE();
1076 /* check if we have a non-standard tile size image */
1077 if (tile_size != gfx.standard_tile_size)
1079 /* get image with standard tile size */
1080 width_1 = width_final * gfx.standard_tile_size / tile_size;
1081 height_1 = height_final * gfx.standard_tile_size / tile_size;
1083 if (width_1 == old_width)
1084 tmp_bitmap_1 = old_bitmap;
1085 else if (width_1 == width_final)
1086 tmp_bitmap_1 = tmp_bitmap_final;
1087 else if (width_1 == width_0)
1088 tmp_bitmap_1 = tmp_bitmap_0;
1090 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1092 UPDATE_BUSY_STATE();
1095 /* calculate new image dimensions for small images */
1096 width_2 = width_1 / 2;
1097 height_2 = height_1 / 2;
1098 width_4 = width_1 / 4;
1099 height_4 = height_1 / 4;
1100 width_8 = width_1 / 8;
1101 height_8 = height_1 / 8;
1102 width_16 = width_1 / 16;
1103 height_16 = height_1 / 16;
1104 width_32 = width_1 / 32;
1105 height_32 = height_1 / 32;
1107 /* get image with 1/2 of normal size (for use in the level editor) */
1108 if (width_2 == old_width)
1109 tmp_bitmap_2 = old_bitmap;
1111 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1113 UPDATE_BUSY_STATE();
1115 /* get image with 1/4 of normal size (for use in the level editor) */
1116 if (width_4 == old_width)
1117 tmp_bitmap_4 = old_bitmap;
1119 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1121 UPDATE_BUSY_STATE();
1123 /* get image with 1/8 of normal size (for use on the preview screen) */
1124 if (width_8 == old_width)
1125 tmp_bitmap_8 = old_bitmap;
1127 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1129 UPDATE_BUSY_STATE();
1131 /* get image with 1/16 of normal size (for use on the preview screen) */
1132 if (width_16 == old_width)
1133 tmp_bitmap_16 = old_bitmap;
1135 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1137 UPDATE_BUSY_STATE();
1139 /* get image with 1/32 of normal size (for use on the preview screen) */
1140 if (width_32 == old_width)
1141 tmp_bitmap_32 = old_bitmap;
1143 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1145 UPDATE_BUSY_STATE();
1147 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1148 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1149 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1150 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1151 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1152 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1154 if (width_0 != width_1)
1155 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1157 if (bitmaps[IMG_BITMAP_CUSTOM])
1158 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1160 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1162 boolean free_old_bitmap = TRUE;
1164 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1165 if (bitmaps[i] == old_bitmap)
1166 free_old_bitmap = FALSE;
1168 if (free_old_bitmap)
1169 FreeBitmap(old_bitmap);
1173 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1176 // create corresponding bitmaps for masked blitting
1177 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1178 if (bitmaps[i] != NULL &&
1179 bitmaps[i] != old_bitmap)
1180 SetMaskedBitmapSurface(bitmaps[i]);
1182 UPDATE_BUSY_STATE();
1184 print_timestamp_done("CreateScaledBitmaps");
1187 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1190 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1193 void CreateBitmapTextures(Bitmap **bitmaps)
1195 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1198 void FreeBitmapTextures(Bitmap **bitmaps)
1200 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1203 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1205 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1209 /* ------------------------------------------------------------------------- */
1210 /* mouse pointer functions */
1211 /* ------------------------------------------------------------------------- */
1213 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1215 /* XPM image definitions */
1216 static const char *cursor_image_none[] =
1218 /* width height num_colors chars_per_pixel */
1248 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1249 static const char *cursor_image_dot[] =
1251 /* width height num_colors chars_per_pixel */
1280 static const char **cursor_image_playfield = cursor_image_dot;
1282 /* some people complained about a "white dot" on the screen and thought it
1283 was a graphical error... OK, let's just remove the whole pointer :-) */
1284 static const char **cursor_image_playfield = cursor_image_none;
1287 static const int cursor_bit_order = BIT_ORDER_MSB;
1289 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1291 struct MouseCursorInfo *cursor;
1292 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1293 int header_lines = 4;
1296 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1298 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1301 for (y = 0; y < cursor->width; y++)
1303 for (x = 0; x < cursor->height; x++)
1306 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1311 cursor->data[i] = cursor->mask[i] = 0;
1314 switch (image[header_lines + y][x])
1317 cursor->data[i] |= bit_mask;
1318 cursor->mask[i] |= bit_mask;
1322 cursor->mask[i] |= bit_mask;
1331 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1336 void SetMouseCursor(int mode)
1338 static struct MouseCursorInfo *cursor_none = NULL;
1339 static struct MouseCursorInfo *cursor_playfield = NULL;
1340 struct MouseCursorInfo *cursor_new;
1342 if (cursor_none == NULL)
1343 cursor_none = get_cursor_from_image(cursor_image_none);
1345 if (cursor_playfield == NULL)
1346 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1348 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1349 mode == CURSOR_NONE ? cursor_none :
1350 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1352 SDLSetMouseCursor(cursor_new);
1354 gfx.cursor_mode = mode;
1358 /* ========================================================================= */
1359 /* audio functions */
1360 /* ========================================================================= */
1362 void OpenAudio(void)
1364 /* always start with reliable default values */
1365 audio.sound_available = FALSE;
1366 audio.music_available = FALSE;
1367 audio.loops_available = FALSE;
1369 audio.sound_enabled = FALSE;
1370 audio.sound_deactivated = FALSE;
1372 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1373 audio.mixer_pid = 0;
1374 audio.device_name = NULL;
1375 audio.device_fd = -1;
1377 audio.num_channels = 0;
1378 audio.music_channel = 0;
1379 audio.first_sound_channel = 0;
1384 void CloseAudio(void)
1388 audio.sound_enabled = FALSE;
1391 void SetAudioMode(boolean enabled)
1393 if (!audio.sound_available)
1396 audio.sound_enabled = enabled;
1400 /* ========================================================================= */
1401 /* event functions */
1402 /* ========================================================================= */
1404 void InitEventFilter(EventFilter filter_function)
1406 /* set event filter to filter out certain events */
1407 #if defined(TARGET_SDL2)
1408 SDL_SetEventFilter(filter_function, NULL);
1410 SDL_SetEventFilter(filter_function);
1414 boolean PendingEvent(void)
1416 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1419 void NextEvent(Event *event)
1421 SDLNextEvent(event);
1424 void PeekEvent(Event *event)
1426 #if defined(TARGET_SDL2)
1427 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1429 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1433 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1435 #if defined(TARGET_SDL2)
1436 /* key up/down events in SDL2 do not return text characters anymore */
1437 return event->keysym.sym;
1440 #if ENABLE_UNUSED_CODE
1441 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1442 (int)event->keysym.unicode,
1443 (int)event->keysym.sym,
1444 (int)SDL_GetModState());
1447 if (with_modifiers &&
1448 event->keysym.unicode > 0x0000 &&
1449 event->keysym.unicode < 0x2000)
1450 return event->keysym.unicode;
1452 return event->keysym.sym;
1457 KeyMod HandleKeyModState(Key key, int key_status)
1459 static KeyMod current_modifiers = KMOD_None;
1461 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1463 KeyMod new_modifier = KMOD_None;
1468 new_modifier = KMOD_Shift_L;
1471 new_modifier = KMOD_Shift_R;
1473 case KSYM_Control_L:
1474 new_modifier = KMOD_Control_L;
1476 case KSYM_Control_R:
1477 new_modifier = KMOD_Control_R;
1480 new_modifier = KMOD_Meta_L;
1483 new_modifier = KMOD_Meta_R;
1486 new_modifier = KMOD_Alt_L;
1489 new_modifier = KMOD_Alt_R;
1495 if (key_status == KEY_PRESSED)
1496 current_modifiers |= new_modifier;
1498 current_modifiers &= ~new_modifier;
1501 return current_modifiers;
1504 KeyMod GetKeyModState()
1506 return (KeyMod)SDL_GetModState();
1509 KeyMod GetKeyModStateFromEvents()
1511 /* always use key modifier state as tracked from key events (this is needed
1512 if the modifier key event was injected into the event queue, but the key
1513 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1514 query the keys as held pressed on the keyboard) -- this case is currently
1515 only used to filter out clipboard insert events from "True X-Mouse" tool */
1517 return HandleKeyModState(KSYM_UNDEFINED, 0);
1520 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1522 if (event->type != EVENT_CLIENTMESSAGE)
1525 return TRUE; /* the only possible message here is SDL_QUIT */
1529 /* ========================================================================= */
1530 /* joystick functions */
1531 /* ========================================================================= */
1533 void InitJoysticks()
1537 #if defined(NO_JOYSTICK)
1538 return; /* joysticks generally deactivated by compile-time directive */
1541 /* always start with reliable default values */
1542 joystick.status = JOYSTICK_NOT_AVAILABLE;
1543 for (i = 0; i < MAX_PLAYERS; i++)
1544 joystick.fd[i] = -1; /* joystick device closed */
1549 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1551 return SDLReadJoystick(nr, x, y, b1, b2);