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 InitGfxDrawGlobalAnimFunction(void (*draw_global_anim_function)(void))
236 gfx.draw_global_anim_function = draw_global_anim_function;
239 void InitGfxCustomArtworkInfo()
241 gfx.override_level_graphics = FALSE;
242 gfx.override_level_sounds = FALSE;
243 gfx.override_level_music = FALSE;
245 gfx.draw_init_text = TRUE;
248 void InitGfxOtherSettings()
250 gfx.cursor_mode = CURSOR_DEFAULT;
253 void SetDrawDeactivationMask(int draw_deactivation_mask)
255 gfx.draw_deactivation_mask = draw_deactivation_mask;
258 void SetDrawBackgroundMask(int draw_background_mask)
260 gfx.draw_background_mask = draw_background_mask;
263 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
265 if (background_bitmap_tile != NULL)
266 gfx.background_bitmap_mask |= mask;
268 gfx.background_bitmap_mask &= ~mask;
270 if (background_bitmap_tile == NULL) /* empty background requested */
273 if (mask == REDRAW_ALL)
274 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
275 0, 0, video.width, video.height);
276 else if (mask == REDRAW_FIELD)
277 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
278 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
279 else if (mask == REDRAW_DOOR_1)
280 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
281 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
284 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
286 /* remove every mask before setting mask for window */
287 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
288 SetBackgroundBitmap(NULL, 0xffff); /* !!! FIX THIS !!! */
289 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
292 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
294 /* remove window area mask before setting mask for main area */
295 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
296 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
297 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
300 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
302 /* remove window area mask before setting mask for door area */
303 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
304 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
305 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
309 /* ========================================================================= */
310 /* video functions */
311 /* ========================================================================= */
313 inline static int GetRealDepth(int depth)
315 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
318 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
319 int width, int height, Pixel color)
321 SDLFillRectangle(bitmap, x, y, width, height, color);
323 if (bitmap == backbuffer)
324 SetRedrawMaskFromArea(x, y, width, height);
327 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
328 int src_x, int src_y, int width, int height,
329 int dst_x, int dst_y, int mask_mode)
331 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
332 dst_x, dst_y, mask_mode);
334 if (dst_bitmap == backbuffer)
335 SetRedrawMaskFromArea(dst_x, dst_y, width, height);
338 void LimitScreenUpdates(boolean enable)
340 SDLLimitScreenUpdates(enable);
343 void InitVideoDisplay(void)
345 SDLInitVideoDisplay();
348 void CloseVideoDisplay(void)
350 KeyboardAutoRepeatOn();
352 SDL_QuitSubSystem(SDL_INIT_VIDEO);
355 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
358 video.height = height;
359 video.depth = GetRealDepth(depth);
361 video.fullscreen_available = FULLSCREEN_STATUS;
362 video.fullscreen_enabled = FALSE;
364 video.window_scaling_available = WINDOW_SCALING_STATUS;
366 SDLInitVideoBuffer(&backbuffer, &window, fullscreen);
368 video.initialized = TRUE;
373 inline static void FreeBitmapPointers(Bitmap *bitmap)
378 SDLFreeBitmapPointers(bitmap);
380 checked_free(bitmap->source_filename);
381 bitmap->source_filename = NULL;
384 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
387 if (src_bitmap == NULL || dst_bitmap == NULL)
390 FreeBitmapPointers(dst_bitmap);
392 *dst_bitmap = *src_bitmap;
395 void FreeBitmap(Bitmap *bitmap)
400 FreeBitmapPointers(bitmap);
405 Bitmap *CreateBitmapStruct(void)
407 return checked_calloc(sizeof(struct SDLSurfaceInfo));
410 Bitmap *CreateBitmap(int width, int height, int depth)
412 Bitmap *new_bitmap = CreateBitmapStruct();
413 int real_width = MAX(1, width); /* prevent zero bitmap width */
414 int real_height = MAX(1, height); /* prevent zero bitmap height */
415 int real_depth = GetRealDepth(depth);
417 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
419 new_bitmap->width = real_width;
420 new_bitmap->height = real_height;
425 void ReCreateBitmap(Bitmap **bitmap, int width, int height, int depth)
427 Bitmap *new_bitmap = CreateBitmap(width, height, depth);
431 *bitmap = new_bitmap;
435 TransferBitmapPointers(new_bitmap, *bitmap);
440 void CloseWindow(DrawWindow *window)
444 void SetRedrawMaskFromArea(int x, int y, int width, int height)
448 int x2 = x + width - 1;
449 int y2 = y + height - 1;
451 if (width == 0 || height == 0)
454 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
455 redraw_mask |= REDRAW_FIELD;
456 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
457 redraw_mask |= REDRAW_DOOR_1;
458 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
459 redraw_mask |= REDRAW_DOOR_2;
460 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
461 redraw_mask |= REDRAW_DOOR_3;
463 redraw_mask = REDRAW_ALL;
466 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
469 if (draw_mask == REDRAW_NONE)
472 if (draw_mask & REDRAW_ALL)
475 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
478 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
481 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
484 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
490 boolean DrawingDeactivated(int x, int y, int width, int height)
492 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
495 boolean DrawingOnBackground(int x, int y)
497 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
498 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
501 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
502 int *width, int *height, boolean is_dest)
504 int clip_x, clip_y, clip_width, clip_height;
506 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
508 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
509 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
510 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
511 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
517 clip_width = bitmap->width;
518 clip_height = bitmap->height;
521 /* skip if rectangle completely outside bitmap */
523 if (*x + *width <= clip_x ||
524 *y + *height <= clip_y ||
525 *x >= clip_x + clip_width ||
526 *y >= clip_y + clip_height)
529 /* clip if rectangle overlaps bitmap */
533 *width -= clip_x - *x;
536 else if (*x + *width > clip_x + clip_width)
538 *width = clip_x + clip_width - *x;
543 *height -= clip_y - *y;
546 else if (*y + *height > clip_y + clip_height)
548 *height = clip_y + clip_height - *y;
554 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
555 int src_x, int src_y, int width, int height,
556 int dst_x, int dst_y)
558 int dst_x_unclipped = dst_x;
559 int dst_y_unclipped = dst_y;
561 if (src_bitmap == NULL || dst_bitmap == NULL)
564 if (DrawingDeactivated(dst_x, dst_y, width, height))
567 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
568 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
571 /* source x/y might need adjustment if destination x/y was clipped top/left */
572 src_x += dst_x - dst_x_unclipped;
573 src_y += dst_y - dst_y_unclipped;
575 #if defined(TARGET_SDL2)
576 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
577 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
578 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
579 but is already fixed in SVN and should therefore finally be fixed with
580 the next official SDL release, which is probably version 1.2.14.) */
581 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
583 if (src_bitmap == dst_bitmap)
585 /* needed when blitting directly to same bitmap -- should not be needed with
586 recent SDL libraries, but apparently does not work in 1.2.11 directly */
588 static Bitmap *tmp_bitmap = NULL;
589 static int tmp_bitmap_xsize = 0;
590 static int tmp_bitmap_ysize = 0;
592 /* start with largest static bitmaps for initial bitmap size ... */
593 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
595 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
596 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
599 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
600 if (src_bitmap->width > tmp_bitmap_xsize ||
601 src_bitmap->height > tmp_bitmap_ysize)
603 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
604 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
606 FreeBitmap(tmp_bitmap);
611 if (tmp_bitmap == NULL)
612 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
615 sysCopyArea(src_bitmap, tmp_bitmap,
616 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
617 sysCopyArea(tmp_bitmap, dst_bitmap,
618 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
624 sysCopyArea(src_bitmap, dst_bitmap,
625 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
628 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
629 int src_x, int src_y, int src_width, int src_height,
630 int dst_x, int dst_y, int dst_width, int dst_height)
632 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
633 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
634 int dst_xsize = dst_width;
635 int dst_ysize = dst_height;
636 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
637 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
640 for (y = 0; y < src_ysteps; y++)
642 for (x = 0; x < src_xsteps; x++)
644 int draw_x = dst_x + x * src_xsize;
645 int draw_y = dst_y + y * src_ysize;
646 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
647 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
649 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
655 void FadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
656 int fade_mode, int fade_delay, int post_delay,
657 void (*draw_border_function)(void))
659 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
660 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
663 SDLFadeRectangle(bitmap_cross, x, y, width, height,
664 fade_mode, fade_delay, post_delay, draw_border_function);
667 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
670 if (DrawingDeactivated(x, y, width, height))
673 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
676 sysFillRectangle(bitmap, x, y, width, height, color);
679 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
681 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
684 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
685 int width, int height)
687 if (DrawingOnBackground(x, y))
688 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
690 ClearRectangle(bitmap, x, y, width, height);
693 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
694 int src_x, int src_y, int width, int height,
695 int dst_x, int dst_y)
697 if (DrawingDeactivated(dst_x, dst_y, width, height))
700 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
701 dst_x, dst_y, BLIT_MASKED);
704 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
705 int src_x, int src_y, int width, int height,
706 int dst_x, int dst_y)
708 if (DrawingOnBackground(dst_x, dst_y))
710 /* draw background */
711 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
714 /* draw foreground */
715 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
719 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
723 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
726 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
729 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
732 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
735 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
736 int to_x, int to_y, Pixel pixel, int line_width)
740 for (x = 0; x < line_width; x++)
742 for (y = 0; y < line_width; y++)
744 int dx = x - line_width / 2;
745 int dy = y - line_width / 2;
747 if ((x == 0 && y == 0) ||
748 (x == 0 && y == line_width - 1) ||
749 (x == line_width - 1 && y == 0) ||
750 (x == line_width - 1 && y == line_width - 1))
754 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
759 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
764 for (i = 0; i < num_points - 1; i++)
765 DrawLine(bitmap, points[i].x, points[i].y,
766 points[i + 1].x, points[i + 1].y, pixel, line_width);
769 SDLDrawLines(bitmap->surface, points, num_points, pixel);
773 Pixel GetPixel(Bitmap *bitmap, int x, int y)
775 if (x < 0 || x >= bitmap->width ||
776 y < 0 || y >= bitmap->height)
779 return SDLGetPixel(bitmap, x, y);
782 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
783 unsigned int color_g, unsigned int color_b)
785 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
788 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
790 unsigned int color_r = (color >> 16) & 0xff;
791 unsigned int color_g = (color >> 8) & 0xff;
792 unsigned int color_b = (color >> 0) & 0xff;
794 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
797 void KeyboardAutoRepeatOn(void)
799 #if defined(TARGET_SDL2)
800 keyrepeat_status = TRUE;
802 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
803 SDL_DEFAULT_REPEAT_INTERVAL / 2);
804 SDL_EnableUNICODE(1);
808 void KeyboardAutoRepeatOff(void)
810 #if defined(TARGET_SDL2)
811 keyrepeat_status = FALSE;
813 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
814 SDL_EnableUNICODE(0);
818 boolean SetVideoMode(boolean fullscreen)
820 return SDLSetVideoMode(&backbuffer, fullscreen);
823 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
825 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
826 (!fullscreen && video.fullscreen_enabled))
827 fullscreen = SetVideoMode(fullscreen);
832 Bitmap *LoadImage(char *filename)
836 new_bitmap = SDLLoadImage(filename);
839 new_bitmap->source_filename = getStringCopy(filename);
844 Bitmap *LoadCustomImage(char *basename)
846 char *filename = getCustomImageFilename(basename);
849 if (filename == NULL)
850 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
852 if ((new_bitmap = LoadImage(filename)) == NULL)
853 Error(ERR_EXIT, "LoadImage('%s') failed: %s", basename, GetError());
858 void ReloadCustomImage(Bitmap *bitmap, char *basename)
860 char *filename = getCustomImageFilename(basename);
863 if (filename == NULL) /* (should never happen) */
865 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
869 if (strEqual(filename, bitmap->source_filename))
871 /* The old and new image are the same (have the same filename and path).
872 This usually means that this image does not exist in this graphic set
873 and a fallback to the existing image is done. */
878 if ((new_bitmap = LoadImage(filename)) == NULL)
880 Error(ERR_WARN, "LoadImage('%s') failed: %s", basename, GetError());
884 if (bitmap->width != new_bitmap->width ||
885 bitmap->height != new_bitmap->height)
887 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
889 FreeBitmap(new_bitmap);
893 TransferBitmapPointers(new_bitmap, bitmap);
897 Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
899 Bitmap *dst_bitmap = SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
904 static void SetMaskedBitmapSurface(Bitmap *bitmap)
909 SDL_Surface *surface = bitmap->surface;
911 if (bitmap->surface_masked)
912 SDL_FreeSurface(bitmap->surface_masked);
914 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
915 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
917 if ((bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
918 Error(ERR_EXIT, "SDL_DisplayFormat() failed");
920 SDL_SetColorKey(surface, UNSET_TRANSPARENT_PIXEL, 0);
923 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
925 if (bitmaps[IMG_BITMAP_CUSTOM])
927 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
929 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
932 if (gfx.game_tile_size == gfx.standard_tile_size)
934 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
939 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
940 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
941 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
943 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
945 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
946 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
948 SetMaskedBitmapSurface(bitmap_new);
951 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
952 int tile_size, boolean create_small_bitmaps)
954 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
955 Bitmap *tmp_bitmap_final = NULL;
956 Bitmap *tmp_bitmap_0 = NULL;
957 Bitmap *tmp_bitmap_1 = NULL;
958 Bitmap *tmp_bitmap_2 = NULL;
959 Bitmap *tmp_bitmap_4 = NULL;
960 Bitmap *tmp_bitmap_8 = NULL;
961 Bitmap *tmp_bitmap_16 = NULL;
962 Bitmap *tmp_bitmap_32 = NULL;
963 int width_final, height_final;
964 int width_0, height_0;
965 int width_1, height_1;
966 int width_2, height_2;
967 int width_4, height_4;
968 int width_8, height_8;
969 int width_16, height_16;
970 int width_32, height_32;
971 int old_width, old_height;
974 print_timestamp_init("CreateScaledBitmaps");
976 old_width = old_bitmap->width;
977 old_height = old_bitmap->height;
979 /* calculate new image dimensions for final image size */
980 width_final = old_width * zoom_factor;
981 height_final = old_height * zoom_factor;
983 /* get image with final size (this might require scaling up) */
984 /* ("final" size may result in non-standard tile size image) */
985 if (zoom_factor != 1)
986 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
988 tmp_bitmap_final = old_bitmap;
992 width_0 = width_1 = width_final;
993 height_0 = height_1 = height_final;
995 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
997 if (create_small_bitmaps)
999 /* check if we have a non-gameplay tile size image */
1000 if (tile_size != gfx.game_tile_size)
1002 /* get image with gameplay tile size */
1003 width_0 = width_final * gfx.game_tile_size / tile_size;
1004 height_0 = height_final * gfx.game_tile_size / tile_size;
1006 if (width_0 == old_width)
1007 tmp_bitmap_0 = old_bitmap;
1008 else if (width_0 == width_final)
1009 tmp_bitmap_0 = tmp_bitmap_final;
1011 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1013 UPDATE_BUSY_STATE();
1016 /* check if we have a non-standard tile size image */
1017 if (tile_size != gfx.standard_tile_size)
1019 /* get image with standard tile size */
1020 width_1 = width_final * gfx.standard_tile_size / tile_size;
1021 height_1 = height_final * gfx.standard_tile_size / tile_size;
1023 if (width_1 == old_width)
1024 tmp_bitmap_1 = old_bitmap;
1025 else if (width_1 == width_final)
1026 tmp_bitmap_1 = tmp_bitmap_final;
1027 else if (width_1 == width_0)
1028 tmp_bitmap_1 = tmp_bitmap_0;
1030 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1032 UPDATE_BUSY_STATE();
1035 /* calculate new image dimensions for small images */
1036 width_2 = width_1 / 2;
1037 height_2 = height_1 / 2;
1038 width_4 = width_1 / 4;
1039 height_4 = height_1 / 4;
1040 width_8 = width_1 / 8;
1041 height_8 = height_1 / 8;
1042 width_16 = width_1 / 16;
1043 height_16 = height_1 / 16;
1044 width_32 = width_1 / 32;
1045 height_32 = height_1 / 32;
1047 /* get image with 1/2 of normal size (for use in the level editor) */
1048 if (width_2 == old_width)
1049 tmp_bitmap_2 = old_bitmap;
1051 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1053 UPDATE_BUSY_STATE();
1055 /* get image with 1/4 of normal size (for use in the level editor) */
1056 if (width_4 == old_width)
1057 tmp_bitmap_4 = old_bitmap;
1059 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1061 UPDATE_BUSY_STATE();
1063 /* get image with 1/8 of normal size (for use on the preview screen) */
1064 if (width_8 == old_width)
1065 tmp_bitmap_8 = old_bitmap;
1067 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1069 UPDATE_BUSY_STATE();
1071 /* get image with 1/16 of normal size (for use on the preview screen) */
1072 if (width_16 == old_width)
1073 tmp_bitmap_16 = old_bitmap;
1075 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1077 UPDATE_BUSY_STATE();
1079 /* get image with 1/32 of normal size (for use on the preview screen) */
1080 if (width_32 == old_width)
1081 tmp_bitmap_32 = old_bitmap;
1083 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1085 UPDATE_BUSY_STATE();
1087 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1088 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1089 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1090 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1091 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1092 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1094 if (width_0 != width_1)
1095 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1097 if (bitmaps[IMG_BITMAP_CUSTOM])
1098 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1100 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1102 boolean free_old_bitmap = TRUE;
1104 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1105 if (bitmaps[i] == old_bitmap)
1106 free_old_bitmap = FALSE;
1108 if (free_old_bitmap)
1109 FreeBitmap(old_bitmap);
1113 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1116 // create corresponding bitmaps for masked blitting
1117 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1118 if (bitmaps[i] != NULL &&
1119 bitmaps[i] != old_bitmap)
1120 SetMaskedBitmapSurface(bitmaps[i]);
1122 UPDATE_BUSY_STATE();
1124 print_timestamp_done("CreateScaledBitmaps");
1127 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1130 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1133 void CreateBitmapTextures(Bitmap **bitmaps)
1135 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1138 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1140 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1144 /* ------------------------------------------------------------------------- */
1145 /* mouse pointer functions */
1146 /* ------------------------------------------------------------------------- */
1148 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1150 /* XPM image definitions */
1151 static const char *cursor_image_none[] =
1153 /* width height num_colors chars_per_pixel */
1183 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1184 static const char *cursor_image_dot[] =
1186 /* width height num_colors chars_per_pixel */
1215 static const char **cursor_image_playfield = cursor_image_dot;
1217 /* some people complained about a "white dot" on the screen and thought it
1218 was a graphical error... OK, let's just remove the whole pointer :-) */
1219 static const char **cursor_image_playfield = cursor_image_none;
1222 static const int cursor_bit_order = BIT_ORDER_MSB;
1224 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1226 struct MouseCursorInfo *cursor;
1227 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1228 int header_lines = 4;
1231 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1233 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1236 for (y = 0; y < cursor->width; y++)
1238 for (x = 0; x < cursor->height; x++)
1241 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1246 cursor->data[i] = cursor->mask[i] = 0;
1249 switch (image[header_lines + y][x])
1252 cursor->data[i] |= bit_mask;
1253 cursor->mask[i] |= bit_mask;
1257 cursor->mask[i] |= bit_mask;
1266 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1271 void SetMouseCursor(int mode)
1273 static struct MouseCursorInfo *cursor_none = NULL;
1274 static struct MouseCursorInfo *cursor_playfield = NULL;
1275 struct MouseCursorInfo *cursor_new;
1277 if (cursor_none == NULL)
1278 cursor_none = get_cursor_from_image(cursor_image_none);
1280 if (cursor_playfield == NULL)
1281 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1283 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1284 mode == CURSOR_NONE ? cursor_none :
1285 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1287 SDLSetMouseCursor(cursor_new);
1289 gfx.cursor_mode = mode;
1293 /* ========================================================================= */
1294 /* audio functions */
1295 /* ========================================================================= */
1297 void OpenAudio(void)
1299 /* always start with reliable default values */
1300 audio.sound_available = FALSE;
1301 audio.music_available = FALSE;
1302 audio.loops_available = FALSE;
1304 audio.sound_enabled = FALSE;
1305 audio.sound_deactivated = FALSE;
1307 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1308 audio.mixer_pid = 0;
1309 audio.device_name = NULL;
1310 audio.device_fd = -1;
1312 audio.num_channels = 0;
1313 audio.music_channel = 0;
1314 audio.first_sound_channel = 0;
1319 void CloseAudio(void)
1323 audio.sound_enabled = FALSE;
1326 void SetAudioMode(boolean enabled)
1328 if (!audio.sound_available)
1331 audio.sound_enabled = enabled;
1335 /* ========================================================================= */
1336 /* event functions */
1337 /* ========================================================================= */
1339 void InitEventFilter(EventFilter filter_function)
1341 /* set event filter to filter out certain events */
1342 #if defined(TARGET_SDL2)
1343 SDL_SetEventFilter(filter_function, NULL);
1345 SDL_SetEventFilter(filter_function);
1349 boolean PendingEvent(void)
1351 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1354 void NextEvent(Event *event)
1356 SDLNextEvent(event);
1359 void PeekEvent(Event *event)
1361 #if defined(TARGET_SDL2)
1362 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1364 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1368 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1370 #if defined(TARGET_SDL2)
1371 /* key up/down events in SDL2 do not return text characters anymore */
1372 return event->keysym.sym;
1375 #if ENABLE_UNUSED_CODE
1376 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1377 (int)event->keysym.unicode,
1378 (int)event->keysym.sym,
1379 (int)SDL_GetModState());
1382 if (with_modifiers &&
1383 event->keysym.unicode > 0x0000 &&
1384 event->keysym.unicode < 0x2000)
1385 return event->keysym.unicode;
1387 return event->keysym.sym;
1392 KeyMod HandleKeyModState(Key key, int key_status)
1394 static KeyMod current_modifiers = KMOD_None;
1396 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1398 KeyMod new_modifier = KMOD_None;
1403 new_modifier = KMOD_Shift_L;
1406 new_modifier = KMOD_Shift_R;
1408 case KSYM_Control_L:
1409 new_modifier = KMOD_Control_L;
1411 case KSYM_Control_R:
1412 new_modifier = KMOD_Control_R;
1415 new_modifier = KMOD_Meta_L;
1418 new_modifier = KMOD_Meta_R;
1421 new_modifier = KMOD_Alt_L;
1424 new_modifier = KMOD_Alt_R;
1430 if (key_status == KEY_PRESSED)
1431 current_modifiers |= new_modifier;
1433 current_modifiers &= ~new_modifier;
1436 return current_modifiers;
1439 KeyMod GetKeyModState()
1441 return (KeyMod)SDL_GetModState();
1444 KeyMod GetKeyModStateFromEvents()
1446 /* always use key modifier state as tracked from key events (this is needed
1447 if the modifier key event was injected into the event queue, but the key
1448 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1449 query the keys as held pressed on the keyboard) -- this case is currently
1450 only used to filter out clipboard insert events from "True X-Mouse" tool */
1452 return HandleKeyModState(KSYM_UNDEFINED, 0);
1455 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1457 if (event->type != EVENT_CLIENTMESSAGE)
1460 return TRUE; /* the only possible message here is SDL_QUIT */
1464 /* ========================================================================= */
1465 /* joystick functions */
1466 /* ========================================================================= */
1468 void InitJoysticks()
1472 #if defined(NO_JOYSTICK)
1473 return; /* joysticks generally deactivated by compile-time directive */
1476 /* always start with reliable default values */
1477 joystick.status = JOYSTICK_NOT_AVAILABLE;
1478 for (i = 0; i < MAX_PLAYERS; i++)
1479 joystick.fd[i] = -1; /* joystick device closed */
1484 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1486 return SDLReadJoystick(nr, x, y, b1, b2);