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)(void))
239 gfx.draw_global_anim_function = draw_global_anim_function;
242 void InitGfxCustomArtworkInfo()
244 gfx.override_level_graphics = FALSE;
245 gfx.override_level_sounds = FALSE;
246 gfx.override_level_music = FALSE;
248 gfx.draw_init_text = TRUE;
251 void InitGfxOtherSettings()
253 gfx.cursor_mode = CURSOR_DEFAULT;
256 void SetDrawDeactivationMask(int draw_deactivation_mask)
258 gfx.draw_deactivation_mask = draw_deactivation_mask;
261 void SetDrawBackgroundMask(int draw_background_mask)
263 gfx.draw_background_mask = draw_background_mask;
266 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
268 if (background_bitmap_tile != NULL)
269 gfx.background_bitmap_mask |= mask;
271 gfx.background_bitmap_mask &= ~mask;
273 if (background_bitmap_tile == NULL) /* empty background requested */
276 if (mask == REDRAW_ALL)
277 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
278 0, 0, video.width, video.height);
279 else if (mask == REDRAW_FIELD)
280 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
281 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
282 else if (mask == REDRAW_DOOR_1)
283 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
284 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
287 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
289 /* remove every mask before setting mask for window */
290 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
291 SetBackgroundBitmap(NULL, 0xffff); /* !!! FIX THIS !!! */
292 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
295 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
297 /* remove window area mask before setting mask for main area */
298 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
299 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
300 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
303 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
305 /* remove window area mask before setting mask for door area */
306 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
307 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
308 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
312 /* ========================================================================= */
313 /* video functions */
314 /* ========================================================================= */
316 inline static int GetRealDepth(int depth)
318 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
321 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
322 int width, int height, Pixel color)
324 SDLFillRectangle(bitmap, x, y, width, height, color);
326 if (bitmap == backbuffer)
327 SetRedrawMaskFromArea(x, y, width, height);
330 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
331 int src_x, int src_y, int width, int height,
332 int dst_x, int dst_y, int mask_mode)
334 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
335 dst_x, dst_y, mask_mode);
337 if (dst_bitmap == backbuffer)
338 SetRedrawMaskFromArea(dst_x, dst_y, width, height);
341 void LimitScreenUpdates(boolean enable)
343 SDLLimitScreenUpdates(enable);
346 void InitVideoDisplay(void)
348 SDLInitVideoDisplay();
351 void CloseVideoDisplay(void)
353 KeyboardAutoRepeatOn();
355 SDL_QuitSubSystem(SDL_INIT_VIDEO);
358 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
361 video.height = height;
362 video.depth = GetRealDepth(depth);
364 video.fullscreen_available = FULLSCREEN_STATUS;
365 video.fullscreen_enabled = FALSE;
367 video.window_scaling_available = WINDOW_SCALING_STATUS;
369 SDLInitVideoBuffer(&backbuffer, &window, fullscreen);
371 video.initialized = TRUE;
376 inline static void FreeBitmapPointers(Bitmap *bitmap)
381 SDLFreeBitmapPointers(bitmap);
383 checked_free(bitmap->source_filename);
384 bitmap->source_filename = NULL;
387 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
390 if (src_bitmap == NULL || dst_bitmap == NULL)
393 FreeBitmapPointers(dst_bitmap);
395 *dst_bitmap = *src_bitmap;
398 void FreeBitmap(Bitmap *bitmap)
403 FreeBitmapPointers(bitmap);
408 Bitmap *CreateBitmapStruct(void)
410 return checked_calloc(sizeof(Bitmap));
413 Bitmap *CreateBitmap(int width, int height, int depth)
415 Bitmap *new_bitmap = CreateBitmapStruct();
416 int real_width = MAX(1, width); /* prevent zero bitmap width */
417 int real_height = MAX(1, height); /* prevent zero bitmap height */
418 int real_depth = GetRealDepth(depth);
420 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
422 new_bitmap->width = real_width;
423 new_bitmap->height = real_height;
428 void ReCreateBitmap(Bitmap **bitmap, int width, int height, int depth)
430 Bitmap *new_bitmap = CreateBitmap(width, height, depth);
434 *bitmap = new_bitmap;
438 TransferBitmapPointers(new_bitmap, *bitmap);
443 void CloseWindow(DrawWindow *window)
447 void SetRedrawMaskFromArea(int x, int y, int width, int height)
451 int x2 = x + width - 1;
452 int y2 = y + height - 1;
454 if (width == 0 || height == 0)
457 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
458 redraw_mask |= REDRAW_FIELD;
459 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
460 redraw_mask |= REDRAW_DOOR_1;
461 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
462 redraw_mask |= REDRAW_DOOR_2;
463 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
464 redraw_mask |= REDRAW_DOOR_3;
466 redraw_mask = REDRAW_ALL;
469 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
472 if (draw_mask == REDRAW_NONE)
475 if (draw_mask & REDRAW_ALL)
478 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
481 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
484 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
487 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
493 boolean DrawingDeactivated(int x, int y, int width, int height)
495 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
498 boolean DrawingOnBackground(int x, int y)
500 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
501 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
504 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
505 int *width, int *height, boolean is_dest)
507 int clip_x, clip_y, clip_width, clip_height;
509 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
511 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
512 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
513 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
514 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
520 clip_width = bitmap->width;
521 clip_height = bitmap->height;
524 /* skip if rectangle completely outside bitmap */
526 if (*x + *width <= clip_x ||
527 *y + *height <= clip_y ||
528 *x >= clip_x + clip_width ||
529 *y >= clip_y + clip_height)
532 /* clip if rectangle overlaps bitmap */
536 *width -= clip_x - *x;
539 else if (*x + *width > clip_x + clip_width)
541 *width = clip_x + clip_width - *x;
546 *height -= clip_y - *y;
549 else if (*y + *height > clip_y + clip_height)
551 *height = clip_y + clip_height - *y;
557 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
558 int src_x, int src_y, int width, int height,
559 int dst_x, int dst_y)
561 int dst_x_unclipped = dst_x;
562 int dst_y_unclipped = dst_y;
564 if (src_bitmap == NULL || dst_bitmap == NULL)
567 if (DrawingDeactivated(dst_x, dst_y, width, height))
570 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
571 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
574 /* source x/y might need adjustment if destination x/y was clipped top/left */
575 src_x += dst_x - dst_x_unclipped;
576 src_y += dst_y - dst_y_unclipped;
578 #if defined(TARGET_SDL2)
579 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
580 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
581 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
582 but is already fixed in SVN and should therefore finally be fixed with
583 the next official SDL release, which is probably version 1.2.14.) */
584 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
586 if (src_bitmap == dst_bitmap)
588 /* needed when blitting directly to same bitmap -- should not be needed with
589 recent SDL libraries, but apparently does not work in 1.2.11 directly */
591 static Bitmap *tmp_bitmap = NULL;
592 static int tmp_bitmap_xsize = 0;
593 static int tmp_bitmap_ysize = 0;
595 /* start with largest static bitmaps for initial bitmap size ... */
596 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
598 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
599 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
602 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
603 if (src_bitmap->width > tmp_bitmap_xsize ||
604 src_bitmap->height > tmp_bitmap_ysize)
606 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
607 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
609 FreeBitmap(tmp_bitmap);
614 if (tmp_bitmap == NULL)
615 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
618 sysCopyArea(src_bitmap, tmp_bitmap,
619 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
620 sysCopyArea(tmp_bitmap, dst_bitmap,
621 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
627 sysCopyArea(src_bitmap, dst_bitmap,
628 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
631 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
632 int src_x, int src_y, int src_width, int src_height,
633 int dst_x, int dst_y, int dst_width, int dst_height)
635 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
636 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
637 int dst_xsize = dst_width;
638 int dst_ysize = dst_height;
639 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
640 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
643 for (y = 0; y < src_ysteps; y++)
645 for (x = 0; x < src_xsteps; x++)
647 int draw_x = dst_x + x * src_xsize;
648 int draw_y = dst_y + y * src_ysize;
649 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
650 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
652 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
658 void FadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
659 int fade_mode, int fade_delay, int post_delay,
660 void (*draw_border_function)(void))
662 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
663 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
666 SDLFadeRectangle(bitmap_cross, x, y, width, height,
667 fade_mode, fade_delay, post_delay, draw_border_function);
670 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
673 if (DrawingDeactivated(x, y, width, height))
676 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
679 sysFillRectangle(bitmap, x, y, width, height, color);
682 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
684 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
687 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
688 int width, int height)
690 if (DrawingOnBackground(x, y))
691 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
693 ClearRectangle(bitmap, x, y, width, height);
696 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
697 int src_x, int src_y, int width, int height,
698 int dst_x, int dst_y)
700 if (DrawingDeactivated(dst_x, dst_y, width, height))
703 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
704 dst_x, dst_y, BLIT_MASKED);
707 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
708 int src_x, int src_y, int width, int height,
709 int dst_x, int dst_y)
711 if (DrawingOnBackground(dst_x, dst_y))
713 /* draw background */
714 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
717 /* draw foreground */
718 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
722 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
726 void BlitTexture(Bitmap *bitmap,
727 int src_x, int src_y, int width, int height,
728 int dst_x, int dst_y)
733 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
737 void BlitTextureMasked(Bitmap *bitmap,
738 int src_x, int src_y, int width, int height,
739 int dst_x, int dst_y)
744 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
748 void BlitToScreen(Bitmap *bitmap,
749 int src_x, int src_y, int width, int height,
750 int dst_x, int dst_y)
755 #if USE_FINAL_SCREEN_BITMAP
756 BlitBitmap(bitmap, gfx.final_screen_bitmap, src_x, src_y,
757 width, height, dst_x, dst_y);
759 BlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y);
763 void BlitToScreenMasked(Bitmap *bitmap,
764 int src_x, int src_y, int width, int height,
765 int dst_x, int dst_y)
770 #if USE_FINAL_SCREEN_BITMAP
771 BlitBitmapMasked(bitmap, gfx.final_screen_bitmap, src_x, src_y,
772 width, height, dst_x, dst_y);
774 BlitTextureMasked(bitmap, src_x, src_y, width, height, dst_x, dst_y);
778 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
781 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
784 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
787 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
790 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
791 int to_x, int to_y, Pixel pixel, int line_width)
795 for (x = 0; x < line_width; x++)
797 for (y = 0; y < line_width; y++)
799 int dx = x - line_width / 2;
800 int dy = y - line_width / 2;
802 if ((x == 0 && y == 0) ||
803 (x == 0 && y == line_width - 1) ||
804 (x == line_width - 1 && y == 0) ||
805 (x == line_width - 1 && y == line_width - 1))
809 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
814 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
819 for (i = 0; i < num_points - 1; i++)
820 DrawLine(bitmap, points[i].x, points[i].y,
821 points[i + 1].x, points[i + 1].y, pixel, line_width);
824 SDLDrawLines(bitmap->surface, points, num_points, pixel);
828 Pixel GetPixel(Bitmap *bitmap, int x, int y)
830 if (x < 0 || x >= bitmap->width ||
831 y < 0 || y >= bitmap->height)
834 return SDLGetPixel(bitmap, x, y);
837 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
838 unsigned int color_g, unsigned int color_b)
840 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
843 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
845 unsigned int color_r = (color >> 16) & 0xff;
846 unsigned int color_g = (color >> 8) & 0xff;
847 unsigned int color_b = (color >> 0) & 0xff;
849 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
852 void KeyboardAutoRepeatOn(void)
854 #if defined(TARGET_SDL2)
855 keyrepeat_status = TRUE;
857 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
858 SDL_DEFAULT_REPEAT_INTERVAL / 2);
859 SDL_EnableUNICODE(1);
863 void KeyboardAutoRepeatOff(void)
865 #if defined(TARGET_SDL2)
866 keyrepeat_status = FALSE;
868 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
869 SDL_EnableUNICODE(0);
873 boolean SetVideoMode(boolean fullscreen)
875 return SDLSetVideoMode(&backbuffer, fullscreen);
878 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
880 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
881 (!fullscreen && video.fullscreen_enabled))
882 fullscreen = SetVideoMode(fullscreen);
887 Bitmap *LoadImage(char *filename)
891 new_bitmap = SDLLoadImage(filename);
894 new_bitmap->source_filename = getStringCopy(filename);
899 Bitmap *LoadCustomImage(char *basename)
901 char *filename = getCustomImageFilename(basename);
904 if (filename == NULL)
905 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
907 if ((new_bitmap = LoadImage(filename)) == NULL)
908 Error(ERR_EXIT, "LoadImage('%s') failed: %s", basename, GetError());
913 void ReloadCustomImage(Bitmap *bitmap, char *basename)
915 char *filename = getCustomImageFilename(basename);
918 if (filename == NULL) /* (should never happen) */
920 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
924 if (strEqual(filename, bitmap->source_filename))
926 /* The old and new image are the same (have the same filename and path).
927 This usually means that this image does not exist in this graphic set
928 and a fallback to the existing image is done. */
933 if ((new_bitmap = LoadImage(filename)) == NULL)
935 Error(ERR_WARN, "LoadImage('%s') failed: %s", basename, GetError());
939 if (bitmap->width != new_bitmap->width ||
940 bitmap->height != new_bitmap->height)
942 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
944 FreeBitmap(new_bitmap);
948 TransferBitmapPointers(new_bitmap, bitmap);
952 Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
954 Bitmap *dst_bitmap = SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
959 static void SetMaskedBitmapSurface(Bitmap *bitmap)
964 SDL_Surface *surface = bitmap->surface;
966 if (bitmap->surface_masked)
967 SDL_FreeSurface(bitmap->surface_masked);
969 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
970 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
972 if ((bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
973 Error(ERR_EXIT, "SDL_DisplayFormat() failed");
975 SDL_SetColorKey(surface, UNSET_TRANSPARENT_PIXEL, 0);
978 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
980 if (bitmaps[IMG_BITMAP_CUSTOM])
982 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
984 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
987 if (gfx.game_tile_size == gfx.standard_tile_size)
989 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
994 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
995 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
996 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
998 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
1000 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
1001 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
1003 SetMaskedBitmapSurface(bitmap_new);
1006 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1007 int tile_size, boolean create_small_bitmaps)
1009 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1010 Bitmap *tmp_bitmap_final = NULL;
1011 Bitmap *tmp_bitmap_0 = NULL;
1012 Bitmap *tmp_bitmap_1 = NULL;
1013 Bitmap *tmp_bitmap_2 = NULL;
1014 Bitmap *tmp_bitmap_4 = NULL;
1015 Bitmap *tmp_bitmap_8 = NULL;
1016 Bitmap *tmp_bitmap_16 = NULL;
1017 Bitmap *tmp_bitmap_32 = NULL;
1018 int width_final, height_final;
1019 int width_0, height_0;
1020 int width_1, height_1;
1021 int width_2, height_2;
1022 int width_4, height_4;
1023 int width_8, height_8;
1024 int width_16, height_16;
1025 int width_32, height_32;
1026 int old_width, old_height;
1029 print_timestamp_init("CreateScaledBitmaps");
1031 old_width = old_bitmap->width;
1032 old_height = old_bitmap->height;
1034 /* calculate new image dimensions for final image size */
1035 width_final = old_width * zoom_factor;
1036 height_final = old_height * zoom_factor;
1038 /* get image with final size (this might require scaling up) */
1039 /* ("final" size may result in non-standard tile size image) */
1040 if (zoom_factor != 1)
1041 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1043 tmp_bitmap_final = old_bitmap;
1045 UPDATE_BUSY_STATE();
1047 width_0 = width_1 = width_final;
1048 height_0 = height_1 = height_final;
1050 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1052 if (create_small_bitmaps)
1054 /* check if we have a non-gameplay tile size image */
1055 if (tile_size != gfx.game_tile_size)
1057 /* get image with gameplay tile size */
1058 width_0 = width_final * gfx.game_tile_size / tile_size;
1059 height_0 = height_final * gfx.game_tile_size / tile_size;
1061 if (width_0 == old_width)
1062 tmp_bitmap_0 = old_bitmap;
1063 else if (width_0 == width_final)
1064 tmp_bitmap_0 = tmp_bitmap_final;
1066 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1068 UPDATE_BUSY_STATE();
1071 /* check if we have a non-standard tile size image */
1072 if (tile_size != gfx.standard_tile_size)
1074 /* get image with standard tile size */
1075 width_1 = width_final * gfx.standard_tile_size / tile_size;
1076 height_1 = height_final * gfx.standard_tile_size / tile_size;
1078 if (width_1 == old_width)
1079 tmp_bitmap_1 = old_bitmap;
1080 else if (width_1 == width_final)
1081 tmp_bitmap_1 = tmp_bitmap_final;
1082 else if (width_1 == width_0)
1083 tmp_bitmap_1 = tmp_bitmap_0;
1085 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1087 UPDATE_BUSY_STATE();
1090 /* calculate new image dimensions for small images */
1091 width_2 = width_1 / 2;
1092 height_2 = height_1 / 2;
1093 width_4 = width_1 / 4;
1094 height_4 = height_1 / 4;
1095 width_8 = width_1 / 8;
1096 height_8 = height_1 / 8;
1097 width_16 = width_1 / 16;
1098 height_16 = height_1 / 16;
1099 width_32 = width_1 / 32;
1100 height_32 = height_1 / 32;
1102 /* get image with 1/2 of normal size (for use in the level editor) */
1103 if (width_2 == old_width)
1104 tmp_bitmap_2 = old_bitmap;
1106 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1108 UPDATE_BUSY_STATE();
1110 /* get image with 1/4 of normal size (for use in the level editor) */
1111 if (width_4 == old_width)
1112 tmp_bitmap_4 = old_bitmap;
1114 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1116 UPDATE_BUSY_STATE();
1118 /* get image with 1/8 of normal size (for use on the preview screen) */
1119 if (width_8 == old_width)
1120 tmp_bitmap_8 = old_bitmap;
1122 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1124 UPDATE_BUSY_STATE();
1126 /* get image with 1/16 of normal size (for use on the preview screen) */
1127 if (width_16 == old_width)
1128 tmp_bitmap_16 = old_bitmap;
1130 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1132 UPDATE_BUSY_STATE();
1134 /* get image with 1/32 of normal size (for use on the preview screen) */
1135 if (width_32 == old_width)
1136 tmp_bitmap_32 = old_bitmap;
1138 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1140 UPDATE_BUSY_STATE();
1142 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1143 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1144 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1145 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1146 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1147 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1149 if (width_0 != width_1)
1150 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1152 if (bitmaps[IMG_BITMAP_CUSTOM])
1153 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1155 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1157 boolean free_old_bitmap = TRUE;
1159 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1160 if (bitmaps[i] == old_bitmap)
1161 free_old_bitmap = FALSE;
1163 if (free_old_bitmap)
1164 FreeBitmap(old_bitmap);
1168 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1171 // create corresponding bitmaps for masked blitting
1172 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1173 if (bitmaps[i] != NULL &&
1174 bitmaps[i] != old_bitmap)
1175 SetMaskedBitmapSurface(bitmaps[i]);
1177 UPDATE_BUSY_STATE();
1179 print_timestamp_done("CreateScaledBitmaps");
1182 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1185 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1188 void CreateBitmapTextures(Bitmap **bitmaps)
1190 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1193 void FreeBitmapTextures(Bitmap **bitmaps)
1195 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1198 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1200 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1204 /* ------------------------------------------------------------------------- */
1205 /* mouse pointer functions */
1206 /* ------------------------------------------------------------------------- */
1208 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1210 /* XPM image definitions */
1211 static const char *cursor_image_none[] =
1213 /* width height num_colors chars_per_pixel */
1243 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1244 static const char *cursor_image_dot[] =
1246 /* width height num_colors chars_per_pixel */
1275 static const char **cursor_image_playfield = cursor_image_dot;
1277 /* some people complained about a "white dot" on the screen and thought it
1278 was a graphical error... OK, let's just remove the whole pointer :-) */
1279 static const char **cursor_image_playfield = cursor_image_none;
1282 static const int cursor_bit_order = BIT_ORDER_MSB;
1284 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1286 struct MouseCursorInfo *cursor;
1287 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1288 int header_lines = 4;
1291 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1293 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1296 for (y = 0; y < cursor->width; y++)
1298 for (x = 0; x < cursor->height; x++)
1301 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1306 cursor->data[i] = cursor->mask[i] = 0;
1309 switch (image[header_lines + y][x])
1312 cursor->data[i] |= bit_mask;
1313 cursor->mask[i] |= bit_mask;
1317 cursor->mask[i] |= bit_mask;
1326 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1331 void SetMouseCursor(int mode)
1333 static struct MouseCursorInfo *cursor_none = NULL;
1334 static struct MouseCursorInfo *cursor_playfield = NULL;
1335 struct MouseCursorInfo *cursor_new;
1337 if (cursor_none == NULL)
1338 cursor_none = get_cursor_from_image(cursor_image_none);
1340 if (cursor_playfield == NULL)
1341 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1343 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1344 mode == CURSOR_NONE ? cursor_none :
1345 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1347 SDLSetMouseCursor(cursor_new);
1349 gfx.cursor_mode = mode;
1353 /* ========================================================================= */
1354 /* audio functions */
1355 /* ========================================================================= */
1357 void OpenAudio(void)
1359 /* always start with reliable default values */
1360 audio.sound_available = FALSE;
1361 audio.music_available = FALSE;
1362 audio.loops_available = FALSE;
1364 audio.sound_enabled = FALSE;
1365 audio.sound_deactivated = FALSE;
1367 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1368 audio.mixer_pid = 0;
1369 audio.device_name = NULL;
1370 audio.device_fd = -1;
1372 audio.num_channels = 0;
1373 audio.music_channel = 0;
1374 audio.first_sound_channel = 0;
1379 void CloseAudio(void)
1383 audio.sound_enabled = FALSE;
1386 void SetAudioMode(boolean enabled)
1388 if (!audio.sound_available)
1391 audio.sound_enabled = enabled;
1395 /* ========================================================================= */
1396 /* event functions */
1397 /* ========================================================================= */
1399 void InitEventFilter(EventFilter filter_function)
1401 /* set event filter to filter out certain events */
1402 #if defined(TARGET_SDL2)
1403 SDL_SetEventFilter(filter_function, NULL);
1405 SDL_SetEventFilter(filter_function);
1409 boolean PendingEvent(void)
1411 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1414 void NextEvent(Event *event)
1416 SDLNextEvent(event);
1419 void PeekEvent(Event *event)
1421 #if defined(TARGET_SDL2)
1422 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1424 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1428 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1430 #if defined(TARGET_SDL2)
1431 /* key up/down events in SDL2 do not return text characters anymore */
1432 return event->keysym.sym;
1435 #if ENABLE_UNUSED_CODE
1436 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1437 (int)event->keysym.unicode,
1438 (int)event->keysym.sym,
1439 (int)SDL_GetModState());
1442 if (with_modifiers &&
1443 event->keysym.unicode > 0x0000 &&
1444 event->keysym.unicode < 0x2000)
1445 return event->keysym.unicode;
1447 return event->keysym.sym;
1452 KeyMod HandleKeyModState(Key key, int key_status)
1454 static KeyMod current_modifiers = KMOD_None;
1456 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1458 KeyMod new_modifier = KMOD_None;
1463 new_modifier = KMOD_Shift_L;
1466 new_modifier = KMOD_Shift_R;
1468 case KSYM_Control_L:
1469 new_modifier = KMOD_Control_L;
1471 case KSYM_Control_R:
1472 new_modifier = KMOD_Control_R;
1475 new_modifier = KMOD_Meta_L;
1478 new_modifier = KMOD_Meta_R;
1481 new_modifier = KMOD_Alt_L;
1484 new_modifier = KMOD_Alt_R;
1490 if (key_status == KEY_PRESSED)
1491 current_modifiers |= new_modifier;
1493 current_modifiers &= ~new_modifier;
1496 return current_modifiers;
1499 KeyMod GetKeyModState()
1501 return (KeyMod)SDL_GetModState();
1504 KeyMod GetKeyModStateFromEvents()
1506 /* always use key modifier state as tracked from key events (this is needed
1507 if the modifier key event was injected into the event queue, but the key
1508 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1509 query the keys as held pressed on the keyboard) -- this case is currently
1510 only used to filter out clipboard insert events from "True X-Mouse" tool */
1512 return HandleKeyModState(KSYM_UNDEFINED, 0);
1515 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1517 if (event->type != EVENT_CLIENTMESSAGE)
1520 return TRUE; /* the only possible message here is SDL_QUIT */
1524 /* ========================================================================= */
1525 /* joystick functions */
1526 /* ========================================================================= */
1528 void InitJoysticks()
1532 #if defined(NO_JOYSTICK)
1533 return; /* joysticks generally deactivated by compile-time directive */
1536 /* always start with reliable default values */
1537 joystick.status = JOYSTICK_NOT_AVAILABLE;
1538 for (i = 0; i < MAX_PLAYERS; i++)
1539 joystick.fd[i] = -1; /* joystick device closed */
1544 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1546 return SDLReadJoystick(nr, x, y, b1, b2);