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,
67 char *userdata_subdir, char *userdata_subdir_unix,
68 char *program_title, char *icon_title,
69 char *icon_filename, char *cookie_prefix,
72 program.command_basepath = getBasePath(argv0);
73 program.command_basename = getBaseName(argv0);
75 program.config_filename = config_filename;
77 program.userdata_subdir = userdata_subdir;
78 program.userdata_subdir_unix = userdata_subdir_unix;
79 program.userdata_path = getUserGameDataDir();
81 program.program_title = program_title;
82 program.window_title = "(undefined)";
83 program.icon_title = icon_title;
85 program.icon_filename = icon_filename;
87 program.cookie_prefix = cookie_prefix;
89 program.version_major = VERSION_MAJOR(program_version);
90 program.version_minor = VERSION_MINOR(program_version);
91 program.version_patch = VERSION_PATCH(program_version);
92 program.version_build = VERSION_BUILD(program_version);
93 program.version_ident = program_version;
95 program.log_filename[LOG_OUT_ID] = getLogFilename(LOG_OUT_BASENAME);
96 program.log_filename[LOG_ERR_ID] = getLogFilename(LOG_ERR_BASENAME);
97 program.log_file[LOG_OUT_ID] = program.log_file_default[LOG_OUT_ID] = stdout;
98 program.log_file[LOG_ERR_ID] = program.log_file_default[LOG_ERR_ID] = stderr;
101 void SetWindowTitle()
103 program.window_title = program.window_title_function();
108 void InitWindowTitleFunction(char *(*window_title_function)(void))
110 program.window_title_function = window_title_function;
113 void InitExitMessageFunction(void (*exit_message_function)(char *, va_list))
115 program.exit_message_function = exit_message_function;
118 void InitExitFunction(void (*exit_function)(int))
120 program.exit_function = exit_function;
122 /* set signal handlers to custom exit function */
123 signal(SIGINT, exit_function);
124 signal(SIGTERM, exit_function);
126 /* set exit function to automatically cleanup SDL stuff after exit() */
130 void InitPlatformDependentStuff(void)
132 // this is initialized in GetOptions(), but may already be used before
133 options.verbose = TRUE;
135 #if defined(PLATFORM_MACOSX)
136 updateUserGameDataDir();
141 #if defined(TARGET_SDL2)
142 int sdl_init_flags = SDL_INIT_EVENTS | SDL_INIT_NOPARACHUTE;
144 int sdl_init_flags = SDL_INIT_EVENTTHREAD | SDL_INIT_NOPARACHUTE;
147 if (SDL_Init(sdl_init_flags) < 0)
148 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
153 void ClosePlatformDependentStuff(void)
158 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
159 int real_sx, int real_sy,
160 int full_sxsize, int full_sysize,
161 Bitmap *field_save_buffer)
167 gfx.real_sx = real_sx;
168 gfx.real_sy = real_sy;
169 gfx.full_sxsize = full_sxsize;
170 gfx.full_sysize = full_sysize;
172 gfx.field_save_buffer = field_save_buffer;
174 SetDrawDeactivationMask(REDRAW_NONE); /* do not deactivate drawing */
175 SetDrawBackgroundMask(REDRAW_NONE); /* deactivate masked drawing */
178 void InitGfxTileSizeInfo(int game_tile_size, int standard_tile_size)
180 gfx.game_tile_size = game_tile_size;
181 gfx.standard_tile_size = standard_tile_size;
184 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
192 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
200 void InitGfxDoor3Info(int ex, int ey, int exsize, int eysize)
208 void InitGfxWindowInfo(int win_xsize, int win_ysize)
210 gfx.win_xsize = win_xsize;
211 gfx.win_ysize = win_ysize;
213 gfx.background_bitmap_mask = REDRAW_NONE;
215 ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize, DEFAULT_DEPTH);
218 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
220 /* currently only used by MSDOS code to alloc VRAM buffer, if available */
221 /* 2009-03-24: also (temporarily?) used for overlapping blit workaround */
222 gfx.scrollbuffer_width = scrollbuffer_width;
223 gfx.scrollbuffer_height = scrollbuffer_height;
226 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
228 gfx.clipping_enabled = enabled;
231 gfx.clip_width = width;
232 gfx.clip_height = height;
235 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
237 gfx.draw_busy_anim_function = draw_busy_anim_function;
240 void InitGfxCustomArtworkInfo()
242 gfx.override_level_graphics = FALSE;
243 gfx.override_level_sounds = FALSE;
244 gfx.override_level_music = FALSE;
246 gfx.draw_init_text = TRUE;
249 void InitGfxOtherSettings()
251 gfx.cursor_mode = CURSOR_DEFAULT;
254 void SetDrawDeactivationMask(int draw_deactivation_mask)
256 gfx.draw_deactivation_mask = draw_deactivation_mask;
259 void SetDrawBackgroundMask(int draw_background_mask)
261 gfx.draw_background_mask = draw_background_mask;
264 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
266 if (background_bitmap_tile != NULL)
267 gfx.background_bitmap_mask |= mask;
269 gfx.background_bitmap_mask &= ~mask;
271 if (background_bitmap_tile == NULL) /* empty background requested */
274 if (mask == REDRAW_ALL)
275 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
276 0, 0, video.width, video.height);
277 else if (mask == REDRAW_FIELD)
278 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
279 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
280 else if (mask == REDRAW_DOOR_1)
281 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
282 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
285 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
287 /* remove every mask before setting mask for window */
288 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
289 SetBackgroundBitmap(NULL, 0xffff); /* !!! FIX THIS !!! */
290 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
293 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
295 /* remove window area mask before setting mask for main area */
296 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
297 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
298 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
301 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
303 /* remove window area mask before setting mask for door area */
304 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
305 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
306 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
310 /* ========================================================================= */
311 /* video functions */
312 /* ========================================================================= */
314 inline static int GetRealDepth(int depth)
316 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
319 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
320 int width, int height, Pixel color)
322 SDLFillRectangle(bitmap, x, y, width, height, color);
324 if (bitmap == backbuffer)
325 SetRedrawMaskFromArea(x, y, width, height);
328 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
329 int src_x, int src_y, int width, int height,
330 int dst_x, int dst_y, int mask_mode)
332 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
333 dst_x, dst_y, mask_mode);
335 if (dst_bitmap == backbuffer)
336 SetRedrawMaskFromArea(dst_x, dst_y, width, height);
339 void LimitScreenUpdates(boolean enable)
341 SDLLimitScreenUpdates(enable);
344 void InitVideoDisplay(void)
346 SDLInitVideoDisplay();
349 void CloseVideoDisplay(void)
351 KeyboardAutoRepeatOn();
353 SDL_QuitSubSystem(SDL_INIT_VIDEO);
356 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
359 video.height = height;
360 video.depth = GetRealDepth(depth);
362 video.fullscreen_available = FULLSCREEN_STATUS;
363 video.fullscreen_enabled = FALSE;
365 video.window_scaling_available = WINDOW_SCALING_STATUS;
367 SDLInitVideoBuffer(&backbuffer, &window, fullscreen);
369 video.initialized = TRUE;
374 inline static void FreeBitmapPointers(Bitmap *bitmap)
379 SDLFreeBitmapPointers(bitmap);
381 checked_free(bitmap->source_filename);
382 bitmap->source_filename = NULL;
385 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
388 if (src_bitmap == NULL || dst_bitmap == NULL)
391 FreeBitmapPointers(dst_bitmap);
393 *dst_bitmap = *src_bitmap;
396 void FreeBitmap(Bitmap *bitmap)
401 FreeBitmapPointers(bitmap);
406 Bitmap *CreateBitmapStruct(void)
408 return checked_calloc(sizeof(struct SDLSurfaceInfo));
411 Bitmap *CreateBitmap(int width, int height, int depth)
413 Bitmap *new_bitmap = CreateBitmapStruct();
414 int real_width = MAX(1, width); /* prevent zero bitmap width */
415 int real_height = MAX(1, height); /* prevent zero bitmap height */
416 int real_depth = GetRealDepth(depth);
418 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
420 new_bitmap->width = real_width;
421 new_bitmap->height = real_height;
426 void ReCreateBitmap(Bitmap **bitmap, int width, int height, int depth)
428 Bitmap *new_bitmap = CreateBitmap(width, height, depth);
432 *bitmap = new_bitmap;
436 TransferBitmapPointers(new_bitmap, *bitmap);
441 void CloseWindow(DrawWindow *window)
445 void SetRedrawMaskFromArea(int x, int y, int width, int height)
449 int x2 = x + width - 1;
450 int y2 = y + height - 1;
452 if (width == 0 || height == 0)
455 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
456 redraw_mask |= REDRAW_FIELD;
457 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
458 redraw_mask |= REDRAW_DOOR_1;
459 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
460 redraw_mask |= REDRAW_DOOR_2;
461 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
462 redraw_mask |= REDRAW_DOOR_3;
464 redraw_mask = REDRAW_ALL;
467 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
470 if (draw_mask == REDRAW_NONE)
473 if (draw_mask & REDRAW_ALL)
476 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
479 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
482 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
485 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
491 boolean DrawingDeactivated(int x, int y, int width, int height)
493 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
496 boolean DrawingOnBackground(int x, int y)
498 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
499 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
502 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
503 int *width, int *height, boolean is_dest)
505 int clip_x, clip_y, clip_width, clip_height;
507 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
509 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
510 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
511 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
512 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
518 clip_width = bitmap->width;
519 clip_height = bitmap->height;
522 /* skip if rectangle completely outside bitmap */
524 if (*x + *width <= clip_x ||
525 *y + *height <= clip_y ||
526 *x >= clip_x + clip_width ||
527 *y >= clip_y + clip_height)
530 /* clip if rectangle overlaps bitmap */
534 *width -= clip_x - *x;
537 else if (*x + *width > clip_x + clip_width)
539 *width = clip_x + clip_width - *x;
544 *height -= clip_y - *y;
547 else if (*y + *height > clip_y + clip_height)
549 *height = clip_y + clip_height - *y;
555 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
556 int src_x, int src_y, int width, int height,
557 int dst_x, int dst_y)
559 int dst_x_unclipped = dst_x;
560 int dst_y_unclipped = dst_y;
562 if (src_bitmap == NULL || dst_bitmap == NULL)
565 if (DrawingDeactivated(dst_x, dst_y, width, height))
568 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
569 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
572 /* source x/y might need adjustment if destination x/y was clipped top/left */
573 src_x += dst_x - dst_x_unclipped;
574 src_y += dst_y - dst_y_unclipped;
576 #if defined(TARGET_SDL2)
577 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
578 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
579 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
580 but is already fixed in SVN and should therefore finally be fixed with
581 the next official SDL release, which is probably version 1.2.14.) */
582 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
584 if (src_bitmap == dst_bitmap)
586 /* needed when blitting directly to same bitmap -- should not be needed with
587 recent SDL libraries, but apparently does not work in 1.2.11 directly */
589 static Bitmap *tmp_bitmap = NULL;
590 static int tmp_bitmap_xsize = 0;
591 static int tmp_bitmap_ysize = 0;
593 /* start with largest static bitmaps for initial bitmap size ... */
594 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
596 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
597 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
600 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
601 if (src_bitmap->width > tmp_bitmap_xsize ||
602 src_bitmap->height > tmp_bitmap_ysize)
604 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
605 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
607 FreeBitmap(tmp_bitmap);
612 if (tmp_bitmap == NULL)
613 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
616 sysCopyArea(src_bitmap, tmp_bitmap,
617 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
618 sysCopyArea(tmp_bitmap, dst_bitmap,
619 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
625 sysCopyArea(src_bitmap, dst_bitmap,
626 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
629 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
630 int src_x, int src_y, int src_width, int src_height,
631 int dst_x, int dst_y, int dst_width, int dst_height)
633 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
634 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
635 int dst_xsize = dst_width;
636 int dst_ysize = dst_height;
637 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
638 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
641 for (y = 0; y < src_ysteps; y++)
643 for (x = 0; x < src_xsteps; x++)
645 int draw_x = dst_x + x * src_xsize;
646 int draw_y = dst_y + y * src_ysize;
647 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
648 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
650 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
656 void FadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
657 int fade_mode, int fade_delay, int post_delay,
658 void (*draw_border_function)(void))
660 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
661 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
664 SDLFadeRectangle(bitmap_cross, x, y, width, height,
665 fade_mode, fade_delay, post_delay, draw_border_function);
668 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
671 if (DrawingDeactivated(x, y, width, height))
674 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
677 sysFillRectangle(bitmap, x, y, width, height, color);
680 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
682 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
685 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
686 int width, int height)
688 if (DrawingOnBackground(x, y))
689 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
691 ClearRectangle(bitmap, x, y, width, height);
694 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
695 int src_x, int src_y, int width, int height,
696 int dst_x, int dst_y)
698 if (DrawingDeactivated(dst_x, dst_y, width, height))
701 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
702 dst_x, dst_y, BLIT_MASKED);
705 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
706 int src_x, int src_y, int width, int height,
707 int dst_x, int dst_y)
709 if (DrawingOnBackground(dst_x, dst_y))
711 /* draw background */
712 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
715 /* draw foreground */
716 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
720 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
724 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
727 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
730 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
733 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
736 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
737 int to_x, int to_y, Pixel pixel, int line_width)
741 for (x = 0; x < line_width; x++)
743 for (y = 0; y < line_width; y++)
745 int dx = x - line_width / 2;
746 int dy = y - line_width / 2;
748 if ((x == 0 && y == 0) ||
749 (x == 0 && y == line_width - 1) ||
750 (x == line_width - 1 && y == 0) ||
751 (x == line_width - 1 && y == line_width - 1))
755 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
760 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
765 for (i = 0; i < num_points - 1; i++)
766 DrawLine(bitmap, points[i].x, points[i].y,
767 points[i + 1].x, points[i + 1].y, pixel, line_width);
770 SDLDrawLines(bitmap->surface, points, num_points, pixel);
774 Pixel GetPixel(Bitmap *bitmap, int x, int y)
776 if (x < 0 || x >= bitmap->width ||
777 y < 0 || y >= bitmap->height)
780 return SDLGetPixel(bitmap, x, y);
783 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
784 unsigned int color_g, unsigned int color_b)
786 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
789 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
791 unsigned int color_r = (color >> 16) & 0xff;
792 unsigned int color_g = (color >> 8) & 0xff;
793 unsigned int color_b = (color >> 0) & 0xff;
795 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
798 void KeyboardAutoRepeatOn(void)
800 #if defined(TARGET_SDL2)
801 keyrepeat_status = TRUE;
803 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
804 SDL_DEFAULT_REPEAT_INTERVAL / 2);
805 SDL_EnableUNICODE(1);
809 void KeyboardAutoRepeatOff(void)
811 #if defined(TARGET_SDL2)
812 keyrepeat_status = FALSE;
814 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
815 SDL_EnableUNICODE(0);
819 boolean SetVideoMode(boolean fullscreen)
821 return SDLSetVideoMode(&backbuffer, fullscreen);
824 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
826 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
827 (!fullscreen && video.fullscreen_enabled))
828 fullscreen = SetVideoMode(fullscreen);
833 Bitmap *LoadImage(char *filename)
837 new_bitmap = SDLLoadImage(filename);
840 new_bitmap->source_filename = getStringCopy(filename);
845 Bitmap *LoadCustomImage(char *basename)
847 char *filename = getCustomImageFilename(basename);
850 if (filename == NULL)
851 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
853 if ((new_bitmap = LoadImage(filename)) == NULL)
854 Error(ERR_EXIT, "LoadImage() failed: %s", GetError());
859 void ReloadCustomImage(Bitmap *bitmap, char *basename)
861 char *filename = getCustomImageFilename(basename);
864 if (filename == NULL) /* (should never happen) */
866 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
870 if (strEqual(filename, bitmap->source_filename))
872 /* The old and new image are the same (have the same filename and path).
873 This usually means that this image does not exist in this graphic set
874 and a fallback to the existing image is done. */
879 if ((new_bitmap = LoadImage(filename)) == NULL)
881 Error(ERR_WARN, "LoadImage() failed: %s", GetError());
885 if (bitmap->width != new_bitmap->width ||
886 bitmap->height != new_bitmap->height)
888 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
890 FreeBitmap(new_bitmap);
894 TransferBitmapPointers(new_bitmap, bitmap);
898 Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
900 Bitmap *dst_bitmap = SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
905 static void SetMaskedBitmapSurface(Bitmap *bitmap)
910 SDL_Surface *surface = bitmap->surface;
912 if (bitmap->surface_masked)
913 SDL_FreeSurface(bitmap->surface_masked);
915 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
916 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
918 if ((bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
919 Error(ERR_EXIT, "SDL_DisplayFormat() failed");
921 SDL_SetColorKey(surface, UNSET_TRANSPARENT_PIXEL, 0);
924 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
926 if (bitmaps[IMG_BITMAP_CUSTOM])
928 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
930 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
933 if (gfx.game_tile_size == gfx.standard_tile_size)
935 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
940 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
941 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
942 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
944 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
946 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
947 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
949 SetMaskedBitmapSurface(bitmap_new);
952 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
953 int tile_size, boolean create_small_bitmaps)
955 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
956 Bitmap *tmp_bitmap_final = NULL;
957 Bitmap *tmp_bitmap_0 = NULL;
958 Bitmap *tmp_bitmap_1 = NULL;
959 Bitmap *tmp_bitmap_2 = NULL;
960 Bitmap *tmp_bitmap_4 = NULL;
961 Bitmap *tmp_bitmap_8 = NULL;
962 Bitmap *tmp_bitmap_16 = NULL;
963 Bitmap *tmp_bitmap_32 = NULL;
964 int width_final, height_final;
965 int width_0, height_0;
966 int width_1, height_1;
967 int width_2, height_2;
968 int width_4, height_4;
969 int width_8, height_8;
970 int width_16, height_16;
971 int width_32, height_32;
972 int old_width, old_height;
975 print_timestamp_init("CreateScaledBitmaps");
977 old_width = old_bitmap->width;
978 old_height = old_bitmap->height;
980 /* calculate new image dimensions for final image size */
981 width_final = old_width * zoom_factor;
982 height_final = old_height * zoom_factor;
984 /* get image with final size (this might require scaling up) */
985 /* ("final" size may result in non-standard tile size image) */
986 if (zoom_factor != 1)
987 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
989 tmp_bitmap_final = old_bitmap;
993 width_0 = width_1 = width_final;
994 height_0 = height_1 = height_final;
996 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
998 if (create_small_bitmaps)
1000 /* check if we have a non-gameplay tile size image */
1001 if (tile_size != gfx.game_tile_size)
1003 /* get image with gameplay tile size */
1004 width_0 = width_final * gfx.game_tile_size / tile_size;
1005 height_0 = height_final * gfx.game_tile_size / tile_size;
1007 if (width_0 == old_width)
1008 tmp_bitmap_0 = old_bitmap;
1009 else if (width_0 == width_final)
1010 tmp_bitmap_0 = tmp_bitmap_final;
1012 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1014 UPDATE_BUSY_STATE();
1017 /* check if we have a non-standard tile size image */
1018 if (tile_size != gfx.standard_tile_size)
1020 /* get image with standard tile size */
1021 width_1 = width_final * gfx.standard_tile_size / tile_size;
1022 height_1 = height_final * gfx.standard_tile_size / tile_size;
1024 if (width_1 == old_width)
1025 tmp_bitmap_1 = old_bitmap;
1026 else if (width_1 == width_final)
1027 tmp_bitmap_1 = tmp_bitmap_final;
1028 else if (width_1 == width_0)
1029 tmp_bitmap_1 = tmp_bitmap_0;
1031 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1033 UPDATE_BUSY_STATE();
1036 /* calculate new image dimensions for small images */
1037 width_2 = width_1 / 2;
1038 height_2 = height_1 / 2;
1039 width_4 = width_1 / 4;
1040 height_4 = height_1 / 4;
1041 width_8 = width_1 / 8;
1042 height_8 = height_1 / 8;
1043 width_16 = width_1 / 16;
1044 height_16 = height_1 / 16;
1045 width_32 = width_1 / 32;
1046 height_32 = height_1 / 32;
1048 /* get image with 1/2 of normal size (for use in the level editor) */
1049 if (width_2 == old_width)
1050 tmp_bitmap_2 = old_bitmap;
1052 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1054 UPDATE_BUSY_STATE();
1056 /* get image with 1/4 of normal size (for use in the level editor) */
1057 if (width_4 == old_width)
1058 tmp_bitmap_4 = old_bitmap;
1060 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1062 UPDATE_BUSY_STATE();
1064 /* get image with 1/8 of normal size (for use on the preview screen) */
1065 if (width_8 == old_width)
1066 tmp_bitmap_8 = old_bitmap;
1068 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1070 UPDATE_BUSY_STATE();
1072 /* get image with 1/16 of normal size (for use on the preview screen) */
1073 if (width_16 == old_width)
1074 tmp_bitmap_16 = old_bitmap;
1076 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1078 UPDATE_BUSY_STATE();
1080 /* get image with 1/32 of normal size (for use on the preview screen) */
1081 if (width_32 == old_width)
1082 tmp_bitmap_32 = old_bitmap;
1084 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1086 UPDATE_BUSY_STATE();
1088 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1089 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1090 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1091 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1092 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1093 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1095 if (width_0 != width_1)
1096 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1098 if (bitmaps[IMG_BITMAP_CUSTOM])
1099 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1101 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1103 boolean free_old_bitmap = TRUE;
1105 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1106 if (bitmaps[i] == old_bitmap)
1107 free_old_bitmap = FALSE;
1109 if (free_old_bitmap)
1110 FreeBitmap(old_bitmap);
1114 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1117 // create corresponding bitmaps for masked blitting
1118 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1119 if (bitmaps[i] != NULL &&
1120 bitmaps[i] != old_bitmap)
1121 SetMaskedBitmapSurface(bitmaps[i]);
1123 UPDATE_BUSY_STATE();
1125 print_timestamp_done("CreateScaledBitmaps");
1128 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1131 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1134 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1136 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1140 /* ------------------------------------------------------------------------- */
1141 /* mouse pointer functions */
1142 /* ------------------------------------------------------------------------- */
1144 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1146 /* XPM image definitions */
1147 static const char *cursor_image_none[] =
1149 /* width height num_colors chars_per_pixel */
1179 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1180 static const char *cursor_image_dot[] =
1182 /* width height num_colors chars_per_pixel */
1211 static const char **cursor_image_playfield = cursor_image_dot;
1213 /* some people complained about a "white dot" on the screen and thought it
1214 was a graphical error... OK, let's just remove the whole pointer :-) */
1215 static const char **cursor_image_playfield = cursor_image_none;
1218 static const int cursor_bit_order = BIT_ORDER_MSB;
1220 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1222 struct MouseCursorInfo *cursor;
1223 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1224 int header_lines = 4;
1227 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1229 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1232 for (y = 0; y < cursor->width; y++)
1234 for (x = 0; x < cursor->height; x++)
1237 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1242 cursor->data[i] = cursor->mask[i] = 0;
1245 switch (image[header_lines + y][x])
1248 cursor->data[i] |= bit_mask;
1249 cursor->mask[i] |= bit_mask;
1253 cursor->mask[i] |= bit_mask;
1262 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1267 void SetMouseCursor(int mode)
1269 static struct MouseCursorInfo *cursor_none = NULL;
1270 static struct MouseCursorInfo *cursor_playfield = NULL;
1271 struct MouseCursorInfo *cursor_new;
1273 if (cursor_none == NULL)
1274 cursor_none = get_cursor_from_image(cursor_image_none);
1276 if (cursor_playfield == NULL)
1277 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1279 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1280 mode == CURSOR_NONE ? cursor_none :
1281 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1283 SDLSetMouseCursor(cursor_new);
1285 gfx.cursor_mode = mode;
1289 /* ========================================================================= */
1290 /* audio functions */
1291 /* ========================================================================= */
1293 void OpenAudio(void)
1295 /* always start with reliable default values */
1296 audio.sound_available = FALSE;
1297 audio.music_available = FALSE;
1298 audio.loops_available = FALSE;
1300 audio.sound_enabled = FALSE;
1301 audio.sound_deactivated = FALSE;
1303 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1304 audio.mixer_pid = 0;
1305 audio.device_name = NULL;
1306 audio.device_fd = -1;
1308 audio.num_channels = 0;
1309 audio.music_channel = 0;
1310 audio.first_sound_channel = 0;
1315 void CloseAudio(void)
1319 audio.sound_enabled = FALSE;
1322 void SetAudioMode(boolean enabled)
1324 if (!audio.sound_available)
1327 audio.sound_enabled = enabled;
1331 /* ========================================================================= */
1332 /* event functions */
1333 /* ========================================================================= */
1335 void InitEventFilter(EventFilter filter_function)
1337 /* set event filter to filter out certain events */
1338 #if defined(TARGET_SDL2)
1339 SDL_SetEventFilter(filter_function, NULL);
1341 SDL_SetEventFilter(filter_function);
1345 boolean PendingEvent(void)
1347 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1350 void NextEvent(Event *event)
1352 SDLNextEvent(event);
1355 void PeekEvent(Event *event)
1357 #if defined(TARGET_SDL2)
1358 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1360 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1364 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1366 #if defined(TARGET_SDL2)
1367 /* key up/down events in SDL2 do not return text characters anymore */
1368 return event->keysym.sym;
1371 #if ENABLE_UNUSED_CODE
1372 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1373 (int)event->keysym.unicode,
1374 (int)event->keysym.sym,
1375 (int)SDL_GetModState());
1378 if (with_modifiers &&
1379 event->keysym.unicode > 0x0000 &&
1380 event->keysym.unicode < 0x2000)
1381 return event->keysym.unicode;
1383 return event->keysym.sym;
1388 KeyMod HandleKeyModState(Key key, int key_status)
1390 static KeyMod current_modifiers = KMOD_None;
1392 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1394 KeyMod new_modifier = KMOD_None;
1399 new_modifier = KMOD_Shift_L;
1402 new_modifier = KMOD_Shift_R;
1404 case KSYM_Control_L:
1405 new_modifier = KMOD_Control_L;
1407 case KSYM_Control_R:
1408 new_modifier = KMOD_Control_R;
1411 new_modifier = KMOD_Meta_L;
1414 new_modifier = KMOD_Meta_R;
1417 new_modifier = KMOD_Alt_L;
1420 new_modifier = KMOD_Alt_R;
1426 if (key_status == KEY_PRESSED)
1427 current_modifiers |= new_modifier;
1429 current_modifiers &= ~new_modifier;
1432 return current_modifiers;
1435 KeyMod GetKeyModState()
1437 return (KeyMod)SDL_GetModState();
1440 KeyMod GetKeyModStateFromEvents()
1442 /* always use key modifier state as tracked from key events (this is needed
1443 if the modifier key event was injected into the event queue, but the key
1444 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1445 query the keys as held pressed on the keyboard) -- this case is currently
1446 only used to filter out clipboard insert events from "True X-Mouse" tool */
1448 return HandleKeyModState(KSYM_UNDEFINED, 0);
1451 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1453 if (event->type != EVENT_CLIENTMESSAGE)
1456 return TRUE; /* the only possible message here is SDL_QUIT */
1460 /* ========================================================================= */
1461 /* joystick functions */
1462 /* ========================================================================= */
1464 void InitJoysticks()
1468 #if defined(NO_JOYSTICK)
1469 return; /* joysticks generally deactivated by compile-time directive */
1472 /* always start with reliable default values */
1473 joystick.status = JOYSTICK_NOT_AVAILABLE;
1474 for (i = 0; i < MAX_PLAYERS; i++)
1475 joystick.fd[i] = -1; /* joystick device closed */
1480 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1482 return SDLReadJoystick(nr, x, y, b1, b2);