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);
212 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
214 /* currently only used by MSDOS code to alloc VRAM buffer, if available */
215 /* 2009-03-24: also (temporarily?) used for overlapping blit workaround */
216 gfx.scrollbuffer_width = scrollbuffer_width;
217 gfx.scrollbuffer_height = scrollbuffer_height;
220 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
222 gfx.clipping_enabled = enabled;
225 gfx.clip_width = width;
226 gfx.clip_height = height;
229 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
231 gfx.draw_busy_anim_function = draw_busy_anim_function;
234 void InitGfxCustomArtworkInfo()
236 gfx.override_level_graphics = FALSE;
237 gfx.override_level_sounds = FALSE;
238 gfx.override_level_music = FALSE;
240 gfx.draw_init_text = TRUE;
243 void InitGfxOtherSettings()
245 gfx.cursor_mode = CURSOR_DEFAULT;
248 void SetDrawDeactivationMask(int draw_deactivation_mask)
250 gfx.draw_deactivation_mask = draw_deactivation_mask;
253 void SetDrawBackgroundMask(int draw_background_mask)
255 gfx.draw_background_mask = draw_background_mask;
258 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
260 if (background_bitmap_tile != NULL)
261 gfx.background_bitmap_mask |= mask;
263 gfx.background_bitmap_mask &= ~mask;
265 if (background_bitmap_tile == NULL) /* empty background requested */
268 if (mask == REDRAW_ALL)
269 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
270 0, 0, video.width, video.height);
271 else if (mask == REDRAW_FIELD)
272 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
273 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
274 else if (mask == REDRAW_DOOR_1)
275 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
276 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
279 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
281 /* remove every mask before setting mask for window */
282 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
283 SetBackgroundBitmap(NULL, 0xffff); /* !!! FIX THIS !!! */
284 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
287 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
289 /* remove window area mask before setting mask for main area */
290 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
291 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
292 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
295 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
297 /* remove window area mask before setting mask for door area */
298 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
299 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
300 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
304 /* ========================================================================= */
305 /* video functions */
306 /* ========================================================================= */
308 inline static int GetRealDepth(int depth)
310 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
313 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
314 int width, int height, Pixel color)
316 SDLFillRectangle(bitmap, x, y, width, height, color);
318 if (bitmap == backbuffer)
319 SetRedrawMaskFromArea(x, y, width, height);
322 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
323 int src_x, int src_y, int width, int height,
324 int dst_x, int dst_y, int mask_mode)
326 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
327 dst_x, dst_y, mask_mode);
329 if (dst_bitmap == backbuffer)
330 SetRedrawMaskFromArea(dst_x, dst_y, width, height);
333 void LimitScreenUpdates(boolean enable)
335 SDLLimitScreenUpdates(enable);
338 void InitVideoDisplay(void)
340 SDLInitVideoDisplay();
343 void CloseVideoDisplay(void)
345 KeyboardAutoRepeatOn();
347 SDL_QuitSubSystem(SDL_INIT_VIDEO);
350 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
353 video.height = height;
354 video.depth = GetRealDepth(depth);
356 video.fullscreen_available = FULLSCREEN_STATUS;
357 video.fullscreen_enabled = FALSE;
359 video.window_scaling_available = WINDOW_SCALING_STATUS;
361 SDLInitVideoBuffer(&backbuffer, &window, fullscreen);
363 video.initialized = TRUE;
368 inline static void FreeBitmapPointers(Bitmap *bitmap)
373 SDLFreeBitmapPointers(bitmap);
375 checked_free(bitmap->source_filename);
376 bitmap->source_filename = NULL;
379 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
382 if (src_bitmap == NULL || dst_bitmap == NULL)
385 FreeBitmapPointers(dst_bitmap);
387 *dst_bitmap = *src_bitmap;
390 void FreeBitmap(Bitmap *bitmap)
395 FreeBitmapPointers(bitmap);
400 Bitmap *CreateBitmapStruct(void)
402 return checked_calloc(sizeof(struct SDLSurfaceInfo));
405 Bitmap *CreateBitmap(int width, int height, int depth)
407 Bitmap *new_bitmap = CreateBitmapStruct();
408 int real_width = MAX(1, width); /* prevent zero bitmap width */
409 int real_height = MAX(1, height); /* prevent zero bitmap height */
410 int real_depth = GetRealDepth(depth);
412 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
414 new_bitmap->width = real_width;
415 new_bitmap->height = real_height;
420 void ReCreateBitmap(Bitmap **bitmap, int width, int height, int depth)
422 Bitmap *new_bitmap = CreateBitmap(width, height, depth);
426 *bitmap = new_bitmap;
430 TransferBitmapPointers(new_bitmap, *bitmap);
435 void CloseWindow(DrawWindow *window)
439 void SetRedrawMaskFromArea(int x, int y, int width, int height)
443 int x2 = x + width - 1;
444 int y2 = y + height - 1;
446 if (width == 0 || height == 0)
449 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
450 redraw_mask |= REDRAW_FIELD;
451 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
452 redraw_mask |= REDRAW_DOOR_1;
453 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
454 redraw_mask |= REDRAW_DOOR_2;
455 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
456 redraw_mask |= REDRAW_DOOR_3;
458 redraw_mask = REDRAW_ALL;
461 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
464 if (draw_mask == REDRAW_NONE)
467 if (draw_mask & REDRAW_ALL)
470 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
473 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
476 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
479 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
485 boolean DrawingDeactivated(int x, int y, int width, int height)
487 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
490 boolean DrawingOnBackground(int x, int y)
492 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
493 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
496 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
497 int *width, int *height, boolean is_dest)
499 int clip_x, clip_y, clip_width, clip_height;
501 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
503 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
504 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
505 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
506 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
512 clip_width = bitmap->width;
513 clip_height = bitmap->height;
516 /* skip if rectangle completely outside bitmap */
518 if (*x + *width <= clip_x ||
519 *y + *height <= clip_y ||
520 *x >= clip_x + clip_width ||
521 *y >= clip_y + clip_height)
524 /* clip if rectangle overlaps bitmap */
528 *width -= clip_x - *x;
531 else if (*x + *width > clip_x + clip_width)
533 *width = clip_x + clip_width - *x;
538 *height -= clip_y - *y;
541 else if (*y + *height > clip_y + clip_height)
543 *height = clip_y + clip_height - *y;
549 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
550 int src_x, int src_y, int width, int height,
551 int dst_x, int dst_y)
553 int dst_x_unclipped = dst_x;
554 int dst_y_unclipped = dst_y;
556 if (src_bitmap == NULL || dst_bitmap == NULL)
559 if (DrawingDeactivated(dst_x, dst_y, width, height))
562 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
563 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
566 /* source x/y might need adjustment if destination x/y was clipped top/left */
567 src_x += dst_x - dst_x_unclipped;
568 src_y += dst_y - dst_y_unclipped;
570 #if defined(TARGET_SDL2)
571 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
572 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
573 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
574 but is already fixed in SVN and should therefore finally be fixed with
575 the next official SDL release, which is probably version 1.2.14.) */
576 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
578 if (src_bitmap == dst_bitmap)
580 /* needed when blitting directly to same bitmap -- should not be needed with
581 recent SDL libraries, but apparently does not work in 1.2.11 directly */
583 static Bitmap *tmp_bitmap = NULL;
584 static int tmp_bitmap_xsize = 0;
585 static int tmp_bitmap_ysize = 0;
587 /* start with largest static bitmaps for initial bitmap size ... */
588 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
590 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
591 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
594 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
595 if (src_bitmap->width > tmp_bitmap_xsize ||
596 src_bitmap->height > tmp_bitmap_ysize)
598 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
599 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
601 FreeBitmap(tmp_bitmap);
606 if (tmp_bitmap == NULL)
607 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
610 sysCopyArea(src_bitmap, tmp_bitmap,
611 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
612 sysCopyArea(tmp_bitmap, dst_bitmap,
613 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
619 sysCopyArea(src_bitmap, dst_bitmap,
620 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
623 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
624 int src_x, int src_y, int src_width, int src_height,
625 int dst_x, int dst_y, int dst_width, int dst_height)
627 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
628 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
629 int dst_xsize = dst_width;
630 int dst_ysize = dst_height;
631 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
632 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
635 for (y = 0; y < src_ysteps; y++)
637 for (x = 0; x < src_xsteps; x++)
639 int draw_x = dst_x + x * src_xsize;
640 int draw_y = dst_y + y * src_ysize;
641 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
642 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
644 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
650 void FadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
651 int fade_mode, int fade_delay, int post_delay,
652 void (*draw_border_function)(void))
654 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
655 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
658 SDLFadeRectangle(bitmap_cross, x, y, width, height,
659 fade_mode, fade_delay, post_delay, draw_border_function);
662 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
665 if (DrawingDeactivated(x, y, width, height))
668 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
671 sysFillRectangle(bitmap, x, y, width, height, color);
674 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
676 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
679 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
680 int width, int height)
682 if (DrawingOnBackground(x, y))
683 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
685 ClearRectangle(bitmap, x, y, width, height);
688 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
689 int src_x, int src_y, int width, int height,
690 int dst_x, int dst_y)
692 if (DrawingDeactivated(dst_x, dst_y, width, height))
695 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
696 dst_x, dst_y, BLIT_MASKED);
699 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
700 int src_x, int src_y, int width, int height,
701 int dst_x, int dst_y)
703 if (DrawingOnBackground(dst_x, dst_y))
705 /* draw background */
706 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
709 /* draw foreground */
710 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
714 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
718 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
721 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
724 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
727 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
730 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
731 int to_x, int to_y, Pixel pixel, int line_width)
735 for (x = 0; x < line_width; x++)
737 for (y = 0; y < line_width; y++)
739 int dx = x - line_width / 2;
740 int dy = y - line_width / 2;
742 if ((x == 0 && y == 0) ||
743 (x == 0 && y == line_width - 1) ||
744 (x == line_width - 1 && y == 0) ||
745 (x == line_width - 1 && y == line_width - 1))
749 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
754 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
759 for (i = 0; i < num_points - 1; i++)
760 DrawLine(bitmap, points[i].x, points[i].y,
761 points[i + 1].x, points[i + 1].y, pixel, line_width);
764 SDLDrawLines(bitmap->surface, points, num_points, pixel);
768 Pixel GetPixel(Bitmap *bitmap, int x, int y)
770 if (x < 0 || x >= bitmap->width ||
771 y < 0 || y >= bitmap->height)
774 return SDLGetPixel(bitmap, x, y);
777 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
778 unsigned int color_g, unsigned int color_b)
780 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
783 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
785 unsigned int color_r = (color >> 16) & 0xff;
786 unsigned int color_g = (color >> 8) & 0xff;
787 unsigned int color_b = (color >> 0) & 0xff;
789 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
792 void KeyboardAutoRepeatOn(void)
794 #if defined(TARGET_SDL2)
795 keyrepeat_status = TRUE;
797 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
798 SDL_DEFAULT_REPEAT_INTERVAL / 2);
799 SDL_EnableUNICODE(1);
803 void KeyboardAutoRepeatOff(void)
805 #if defined(TARGET_SDL2)
806 keyrepeat_status = FALSE;
808 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
809 SDL_EnableUNICODE(0);
813 boolean SetVideoMode(boolean fullscreen)
815 return SDLSetVideoMode(&backbuffer, fullscreen);
818 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
820 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
821 (!fullscreen && video.fullscreen_enabled))
822 fullscreen = SetVideoMode(fullscreen);
827 Bitmap *LoadImage(char *filename)
831 new_bitmap = SDLLoadImage(filename);
834 new_bitmap->source_filename = getStringCopy(filename);
839 Bitmap *LoadCustomImage(char *basename)
841 char *filename = getCustomImageFilename(basename);
844 if (filename == NULL)
845 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
847 if ((new_bitmap = LoadImage(filename)) == NULL)
848 Error(ERR_EXIT, "LoadImage('%s') failed: %s", basename, GetError());
853 void ReloadCustomImage(Bitmap *bitmap, char *basename)
855 char *filename = getCustomImageFilename(basename);
858 if (filename == NULL) /* (should never happen) */
860 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
864 if (strEqual(filename, bitmap->source_filename))
866 /* The old and new image are the same (have the same filename and path).
867 This usually means that this image does not exist in this graphic set
868 and a fallback to the existing image is done. */
873 if ((new_bitmap = LoadImage(filename)) == NULL)
875 Error(ERR_WARN, "LoadImage('%s') failed: %s", basename, GetError());
879 if (bitmap->width != new_bitmap->width ||
880 bitmap->height != new_bitmap->height)
882 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
884 FreeBitmap(new_bitmap);
888 TransferBitmapPointers(new_bitmap, bitmap);
892 Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
894 Bitmap *dst_bitmap = SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
899 static void SetMaskedBitmapSurface(Bitmap *bitmap)
904 SDL_Surface *surface = bitmap->surface;
906 if (bitmap->surface_masked)
907 SDL_FreeSurface(bitmap->surface_masked);
909 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
910 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
912 if ((bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
913 Error(ERR_EXIT, "SDL_DisplayFormat() failed");
915 SDL_SetColorKey(surface, UNSET_TRANSPARENT_PIXEL, 0);
918 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
920 if (bitmaps[IMG_BITMAP_CUSTOM])
922 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
924 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
927 if (gfx.game_tile_size == gfx.standard_tile_size)
929 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
934 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
935 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
936 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
938 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
940 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
941 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
943 SetMaskedBitmapSurface(bitmap_new);
946 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
947 int tile_size, boolean create_small_bitmaps)
949 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
950 Bitmap *tmp_bitmap_final = NULL;
951 Bitmap *tmp_bitmap_0 = NULL;
952 Bitmap *tmp_bitmap_1 = NULL;
953 Bitmap *tmp_bitmap_2 = NULL;
954 Bitmap *tmp_bitmap_4 = NULL;
955 Bitmap *tmp_bitmap_8 = NULL;
956 Bitmap *tmp_bitmap_16 = NULL;
957 Bitmap *tmp_bitmap_32 = NULL;
958 int width_final, height_final;
959 int width_0, height_0;
960 int width_1, height_1;
961 int width_2, height_2;
962 int width_4, height_4;
963 int width_8, height_8;
964 int width_16, height_16;
965 int width_32, height_32;
966 int old_width, old_height;
969 print_timestamp_init("CreateScaledBitmaps");
971 old_width = old_bitmap->width;
972 old_height = old_bitmap->height;
974 /* calculate new image dimensions for final image size */
975 width_final = old_width * zoom_factor;
976 height_final = old_height * zoom_factor;
978 /* get image with final size (this might require scaling up) */
979 /* ("final" size may result in non-standard tile size image) */
980 if (zoom_factor != 1)
981 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
983 tmp_bitmap_final = old_bitmap;
987 width_0 = width_1 = width_final;
988 height_0 = height_1 = height_final;
990 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
992 if (create_small_bitmaps)
994 /* check if we have a non-gameplay tile size image */
995 if (tile_size != gfx.game_tile_size)
997 /* get image with gameplay tile size */
998 width_0 = width_final * gfx.game_tile_size / tile_size;
999 height_0 = height_final * gfx.game_tile_size / tile_size;
1001 if (width_0 == old_width)
1002 tmp_bitmap_0 = old_bitmap;
1003 else if (width_0 == width_final)
1004 tmp_bitmap_0 = tmp_bitmap_final;
1006 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1008 UPDATE_BUSY_STATE();
1011 /* check if we have a non-standard tile size image */
1012 if (tile_size != gfx.standard_tile_size)
1014 /* get image with standard tile size */
1015 width_1 = width_final * gfx.standard_tile_size / tile_size;
1016 height_1 = height_final * gfx.standard_tile_size / tile_size;
1018 if (width_1 == old_width)
1019 tmp_bitmap_1 = old_bitmap;
1020 else if (width_1 == width_final)
1021 tmp_bitmap_1 = tmp_bitmap_final;
1022 else if (width_1 == width_0)
1023 tmp_bitmap_1 = tmp_bitmap_0;
1025 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1027 UPDATE_BUSY_STATE();
1030 /* calculate new image dimensions for small images */
1031 width_2 = width_1 / 2;
1032 height_2 = height_1 / 2;
1033 width_4 = width_1 / 4;
1034 height_4 = height_1 / 4;
1035 width_8 = width_1 / 8;
1036 height_8 = height_1 / 8;
1037 width_16 = width_1 / 16;
1038 height_16 = height_1 / 16;
1039 width_32 = width_1 / 32;
1040 height_32 = height_1 / 32;
1042 /* get image with 1/2 of normal size (for use in the level editor) */
1043 if (width_2 == old_width)
1044 tmp_bitmap_2 = old_bitmap;
1046 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1048 UPDATE_BUSY_STATE();
1050 /* get image with 1/4 of normal size (for use in the level editor) */
1051 if (width_4 == old_width)
1052 tmp_bitmap_4 = old_bitmap;
1054 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1056 UPDATE_BUSY_STATE();
1058 /* get image with 1/8 of normal size (for use on the preview screen) */
1059 if (width_8 == old_width)
1060 tmp_bitmap_8 = old_bitmap;
1062 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1064 UPDATE_BUSY_STATE();
1066 /* get image with 1/16 of normal size (for use on the preview screen) */
1067 if (width_16 == old_width)
1068 tmp_bitmap_16 = old_bitmap;
1070 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1072 UPDATE_BUSY_STATE();
1074 /* get image with 1/32 of normal size (for use on the preview screen) */
1075 if (width_32 == old_width)
1076 tmp_bitmap_32 = old_bitmap;
1078 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1080 UPDATE_BUSY_STATE();
1082 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1083 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1084 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1085 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1086 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1087 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1089 if (width_0 != width_1)
1090 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1092 if (bitmaps[IMG_BITMAP_CUSTOM])
1093 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1095 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1097 boolean free_old_bitmap = TRUE;
1099 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1100 if (bitmaps[i] == old_bitmap)
1101 free_old_bitmap = FALSE;
1103 if (free_old_bitmap)
1104 FreeBitmap(old_bitmap);
1108 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1111 // create corresponding bitmaps for masked blitting
1112 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1113 if (bitmaps[i] != NULL &&
1114 bitmaps[i] != old_bitmap)
1115 SetMaskedBitmapSurface(bitmaps[i]);
1117 UPDATE_BUSY_STATE();
1119 print_timestamp_done("CreateScaledBitmaps");
1122 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1125 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1128 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1130 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1134 /* ------------------------------------------------------------------------- */
1135 /* mouse pointer functions */
1136 /* ------------------------------------------------------------------------- */
1138 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1140 /* XPM image definitions */
1141 static const char *cursor_image_none[] =
1143 /* width height num_colors chars_per_pixel */
1173 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1174 static const char *cursor_image_dot[] =
1176 /* width height num_colors chars_per_pixel */
1205 static const char **cursor_image_playfield = cursor_image_dot;
1207 /* some people complained about a "white dot" on the screen and thought it
1208 was a graphical error... OK, let's just remove the whole pointer :-) */
1209 static const char **cursor_image_playfield = cursor_image_none;
1212 static const int cursor_bit_order = BIT_ORDER_MSB;
1214 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1216 struct MouseCursorInfo *cursor;
1217 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1218 int header_lines = 4;
1221 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1223 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1226 for (y = 0; y < cursor->width; y++)
1228 for (x = 0; x < cursor->height; x++)
1231 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1236 cursor->data[i] = cursor->mask[i] = 0;
1239 switch (image[header_lines + y][x])
1242 cursor->data[i] |= bit_mask;
1243 cursor->mask[i] |= bit_mask;
1247 cursor->mask[i] |= bit_mask;
1256 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1261 void SetMouseCursor(int mode)
1263 static struct MouseCursorInfo *cursor_none = NULL;
1264 static struct MouseCursorInfo *cursor_playfield = NULL;
1265 struct MouseCursorInfo *cursor_new;
1267 if (cursor_none == NULL)
1268 cursor_none = get_cursor_from_image(cursor_image_none);
1270 if (cursor_playfield == NULL)
1271 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1273 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1274 mode == CURSOR_NONE ? cursor_none :
1275 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1277 SDLSetMouseCursor(cursor_new);
1279 gfx.cursor_mode = mode;
1283 /* ========================================================================= */
1284 /* audio functions */
1285 /* ========================================================================= */
1287 void OpenAudio(void)
1289 /* always start with reliable default values */
1290 audio.sound_available = FALSE;
1291 audio.music_available = FALSE;
1292 audio.loops_available = FALSE;
1294 audio.sound_enabled = FALSE;
1295 audio.sound_deactivated = FALSE;
1297 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1298 audio.mixer_pid = 0;
1299 audio.device_name = NULL;
1300 audio.device_fd = -1;
1302 audio.num_channels = 0;
1303 audio.music_channel = 0;
1304 audio.first_sound_channel = 0;
1309 void CloseAudio(void)
1313 audio.sound_enabled = FALSE;
1316 void SetAudioMode(boolean enabled)
1318 if (!audio.sound_available)
1321 audio.sound_enabled = enabled;
1325 /* ========================================================================= */
1326 /* event functions */
1327 /* ========================================================================= */
1329 void InitEventFilter(EventFilter filter_function)
1331 /* set event filter to filter out certain events */
1332 #if defined(TARGET_SDL2)
1333 SDL_SetEventFilter(filter_function, NULL);
1335 SDL_SetEventFilter(filter_function);
1339 boolean PendingEvent(void)
1341 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1344 void NextEvent(Event *event)
1346 SDLNextEvent(event);
1349 void PeekEvent(Event *event)
1351 #if defined(TARGET_SDL2)
1352 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1354 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1358 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1360 #if defined(TARGET_SDL2)
1361 /* key up/down events in SDL2 do not return text characters anymore */
1362 return event->keysym.sym;
1365 #if ENABLE_UNUSED_CODE
1366 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1367 (int)event->keysym.unicode,
1368 (int)event->keysym.sym,
1369 (int)SDL_GetModState());
1372 if (with_modifiers &&
1373 event->keysym.unicode > 0x0000 &&
1374 event->keysym.unicode < 0x2000)
1375 return event->keysym.unicode;
1377 return event->keysym.sym;
1382 KeyMod HandleKeyModState(Key key, int key_status)
1384 static KeyMod current_modifiers = KMOD_None;
1386 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1388 KeyMod new_modifier = KMOD_None;
1393 new_modifier = KMOD_Shift_L;
1396 new_modifier = KMOD_Shift_R;
1398 case KSYM_Control_L:
1399 new_modifier = KMOD_Control_L;
1401 case KSYM_Control_R:
1402 new_modifier = KMOD_Control_R;
1405 new_modifier = KMOD_Meta_L;
1408 new_modifier = KMOD_Meta_R;
1411 new_modifier = KMOD_Alt_L;
1414 new_modifier = KMOD_Alt_R;
1420 if (key_status == KEY_PRESSED)
1421 current_modifiers |= new_modifier;
1423 current_modifiers &= ~new_modifier;
1426 return current_modifiers;
1429 KeyMod GetKeyModState()
1431 return (KeyMod)SDL_GetModState();
1434 KeyMod GetKeyModStateFromEvents()
1436 /* always use key modifier state as tracked from key events (this is needed
1437 if the modifier key event was injected into the event queue, but the key
1438 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1439 query the keys as held pressed on the keyboard) -- this case is currently
1440 only used to filter out clipboard insert events from "True X-Mouse" tool */
1442 return HandleKeyModState(KSYM_UNDEFINED, 0);
1445 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1447 if (event->type != EVENT_CLIENTMESSAGE)
1450 return TRUE; /* the only possible message here is SDL_QUIT */
1454 /* ========================================================================= */
1455 /* joystick functions */
1456 /* ========================================================================= */
1458 void InitJoysticks()
1462 #if defined(NO_JOYSTICK)
1463 return; /* joysticks generally deactivated by compile-time directive */
1466 /* always start with reliable default values */
1467 joystick.status = JOYSTICK_NOT_AVAILABLE;
1468 for (i = 0; i < MAX_PLAYERS; i++)
1469 joystick.fd[i] = -1; /* joystick device closed */
1474 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1476 return SDLReadJoystick(nr, x, y, b1, b2);