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 BlitTexture(Bitmap *bitmap,
724 int src_x, int src_y, int width, int height,
725 int dst_x, int dst_y)
730 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
734 void BlitTextureMasked(Bitmap *bitmap,
735 int src_x, int src_y, int width, int height,
736 int dst_x, int dst_y)
741 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
745 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
748 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
751 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
754 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
757 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
758 int to_x, int to_y, Pixel pixel, int line_width)
762 for (x = 0; x < line_width; x++)
764 for (y = 0; y < line_width; y++)
766 int dx = x - line_width / 2;
767 int dy = y - line_width / 2;
769 if ((x == 0 && y == 0) ||
770 (x == 0 && y == line_width - 1) ||
771 (x == line_width - 1 && y == 0) ||
772 (x == line_width - 1 && y == line_width - 1))
776 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
781 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
786 for (i = 0; i < num_points - 1; i++)
787 DrawLine(bitmap, points[i].x, points[i].y,
788 points[i + 1].x, points[i + 1].y, pixel, line_width);
791 SDLDrawLines(bitmap->surface, points, num_points, pixel);
795 Pixel GetPixel(Bitmap *bitmap, int x, int y)
797 if (x < 0 || x >= bitmap->width ||
798 y < 0 || y >= bitmap->height)
801 return SDLGetPixel(bitmap, x, y);
804 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
805 unsigned int color_g, unsigned int color_b)
807 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
810 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
812 unsigned int color_r = (color >> 16) & 0xff;
813 unsigned int color_g = (color >> 8) & 0xff;
814 unsigned int color_b = (color >> 0) & 0xff;
816 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
819 void KeyboardAutoRepeatOn(void)
821 #if defined(TARGET_SDL2)
822 keyrepeat_status = TRUE;
824 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
825 SDL_DEFAULT_REPEAT_INTERVAL / 2);
826 SDL_EnableUNICODE(1);
830 void KeyboardAutoRepeatOff(void)
832 #if defined(TARGET_SDL2)
833 keyrepeat_status = FALSE;
835 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
836 SDL_EnableUNICODE(0);
840 boolean SetVideoMode(boolean fullscreen)
842 return SDLSetVideoMode(&backbuffer, fullscreen);
845 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
847 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
848 (!fullscreen && video.fullscreen_enabled))
849 fullscreen = SetVideoMode(fullscreen);
854 Bitmap *LoadImage(char *filename)
858 new_bitmap = SDLLoadImage(filename);
861 new_bitmap->source_filename = getStringCopy(filename);
866 Bitmap *LoadCustomImage(char *basename)
868 char *filename = getCustomImageFilename(basename);
871 if (filename == NULL)
872 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
874 if ((new_bitmap = LoadImage(filename)) == NULL)
875 Error(ERR_EXIT, "LoadImage('%s') failed: %s", basename, GetError());
880 void ReloadCustomImage(Bitmap *bitmap, char *basename)
882 char *filename = getCustomImageFilename(basename);
885 if (filename == NULL) /* (should never happen) */
887 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
891 if (strEqual(filename, bitmap->source_filename))
893 /* The old and new image are the same (have the same filename and path).
894 This usually means that this image does not exist in this graphic set
895 and a fallback to the existing image is done. */
900 if ((new_bitmap = LoadImage(filename)) == NULL)
902 Error(ERR_WARN, "LoadImage('%s') failed: %s", basename, GetError());
906 if (bitmap->width != new_bitmap->width ||
907 bitmap->height != new_bitmap->height)
909 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
911 FreeBitmap(new_bitmap);
915 TransferBitmapPointers(new_bitmap, bitmap);
919 Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
921 Bitmap *dst_bitmap = SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
926 static void SetMaskedBitmapSurface(Bitmap *bitmap)
931 SDL_Surface *surface = bitmap->surface;
933 if (bitmap->surface_masked)
934 SDL_FreeSurface(bitmap->surface_masked);
936 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
937 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
939 if ((bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
940 Error(ERR_EXIT, "SDL_DisplayFormat() failed");
942 SDL_SetColorKey(surface, UNSET_TRANSPARENT_PIXEL, 0);
945 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
947 if (bitmaps[IMG_BITMAP_CUSTOM])
949 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
951 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
954 if (gfx.game_tile_size == gfx.standard_tile_size)
956 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
961 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
962 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
963 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
965 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
967 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
968 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
970 SetMaskedBitmapSurface(bitmap_new);
973 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
974 int tile_size, boolean create_small_bitmaps)
976 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
977 Bitmap *tmp_bitmap_final = NULL;
978 Bitmap *tmp_bitmap_0 = NULL;
979 Bitmap *tmp_bitmap_1 = NULL;
980 Bitmap *tmp_bitmap_2 = NULL;
981 Bitmap *tmp_bitmap_4 = NULL;
982 Bitmap *tmp_bitmap_8 = NULL;
983 Bitmap *tmp_bitmap_16 = NULL;
984 Bitmap *tmp_bitmap_32 = NULL;
985 int width_final, height_final;
986 int width_0, height_0;
987 int width_1, height_1;
988 int width_2, height_2;
989 int width_4, height_4;
990 int width_8, height_8;
991 int width_16, height_16;
992 int width_32, height_32;
993 int old_width, old_height;
996 print_timestamp_init("CreateScaledBitmaps");
998 old_width = old_bitmap->width;
999 old_height = old_bitmap->height;
1001 /* calculate new image dimensions for final image size */
1002 width_final = old_width * zoom_factor;
1003 height_final = old_height * zoom_factor;
1005 /* get image with final size (this might require scaling up) */
1006 /* ("final" size may result in non-standard tile size image) */
1007 if (zoom_factor != 1)
1008 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1010 tmp_bitmap_final = old_bitmap;
1012 UPDATE_BUSY_STATE();
1014 width_0 = width_1 = width_final;
1015 height_0 = height_1 = height_final;
1017 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1019 if (create_small_bitmaps)
1021 /* check if we have a non-gameplay tile size image */
1022 if (tile_size != gfx.game_tile_size)
1024 /* get image with gameplay tile size */
1025 width_0 = width_final * gfx.game_tile_size / tile_size;
1026 height_0 = height_final * gfx.game_tile_size / tile_size;
1028 if (width_0 == old_width)
1029 tmp_bitmap_0 = old_bitmap;
1030 else if (width_0 == width_final)
1031 tmp_bitmap_0 = tmp_bitmap_final;
1033 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1035 UPDATE_BUSY_STATE();
1038 /* check if we have a non-standard tile size image */
1039 if (tile_size != gfx.standard_tile_size)
1041 /* get image with standard tile size */
1042 width_1 = width_final * gfx.standard_tile_size / tile_size;
1043 height_1 = height_final * gfx.standard_tile_size / tile_size;
1045 if (width_1 == old_width)
1046 tmp_bitmap_1 = old_bitmap;
1047 else if (width_1 == width_final)
1048 tmp_bitmap_1 = tmp_bitmap_final;
1049 else if (width_1 == width_0)
1050 tmp_bitmap_1 = tmp_bitmap_0;
1052 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1054 UPDATE_BUSY_STATE();
1057 /* calculate new image dimensions for small images */
1058 width_2 = width_1 / 2;
1059 height_2 = height_1 / 2;
1060 width_4 = width_1 / 4;
1061 height_4 = height_1 / 4;
1062 width_8 = width_1 / 8;
1063 height_8 = height_1 / 8;
1064 width_16 = width_1 / 16;
1065 height_16 = height_1 / 16;
1066 width_32 = width_1 / 32;
1067 height_32 = height_1 / 32;
1069 /* get image with 1/2 of normal size (for use in the level editor) */
1070 if (width_2 == old_width)
1071 tmp_bitmap_2 = old_bitmap;
1073 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1075 UPDATE_BUSY_STATE();
1077 /* get image with 1/4 of normal size (for use in the level editor) */
1078 if (width_4 == old_width)
1079 tmp_bitmap_4 = old_bitmap;
1081 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1083 UPDATE_BUSY_STATE();
1085 /* get image with 1/8 of normal size (for use on the preview screen) */
1086 if (width_8 == old_width)
1087 tmp_bitmap_8 = old_bitmap;
1089 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1091 UPDATE_BUSY_STATE();
1093 /* get image with 1/16 of normal size (for use on the preview screen) */
1094 if (width_16 == old_width)
1095 tmp_bitmap_16 = old_bitmap;
1097 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1099 UPDATE_BUSY_STATE();
1101 /* get image with 1/32 of normal size (for use on the preview screen) */
1102 if (width_32 == old_width)
1103 tmp_bitmap_32 = old_bitmap;
1105 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1107 UPDATE_BUSY_STATE();
1109 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1110 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1111 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1112 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1113 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1114 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1116 if (width_0 != width_1)
1117 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1119 if (bitmaps[IMG_BITMAP_CUSTOM])
1120 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1122 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1124 boolean free_old_bitmap = TRUE;
1126 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1127 if (bitmaps[i] == old_bitmap)
1128 free_old_bitmap = FALSE;
1130 if (free_old_bitmap)
1131 FreeBitmap(old_bitmap);
1135 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1138 // create corresponding bitmaps for masked blitting
1139 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1140 if (bitmaps[i] != NULL &&
1141 bitmaps[i] != old_bitmap)
1142 SetMaskedBitmapSurface(bitmaps[i]);
1144 UPDATE_BUSY_STATE();
1146 print_timestamp_done("CreateScaledBitmaps");
1149 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1152 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1155 void CreateBitmapTextures(Bitmap **bitmaps)
1157 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1160 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1162 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1166 /* ------------------------------------------------------------------------- */
1167 /* mouse pointer functions */
1168 /* ------------------------------------------------------------------------- */
1170 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1172 /* XPM image definitions */
1173 static const char *cursor_image_none[] =
1175 /* width height num_colors chars_per_pixel */
1205 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1206 static const char *cursor_image_dot[] =
1208 /* width height num_colors chars_per_pixel */
1237 static const char **cursor_image_playfield = cursor_image_dot;
1239 /* some people complained about a "white dot" on the screen and thought it
1240 was a graphical error... OK, let's just remove the whole pointer :-) */
1241 static const char **cursor_image_playfield = cursor_image_none;
1244 static const int cursor_bit_order = BIT_ORDER_MSB;
1246 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1248 struct MouseCursorInfo *cursor;
1249 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1250 int header_lines = 4;
1253 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1255 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1258 for (y = 0; y < cursor->width; y++)
1260 for (x = 0; x < cursor->height; x++)
1263 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1268 cursor->data[i] = cursor->mask[i] = 0;
1271 switch (image[header_lines + y][x])
1274 cursor->data[i] |= bit_mask;
1275 cursor->mask[i] |= bit_mask;
1279 cursor->mask[i] |= bit_mask;
1288 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1293 void SetMouseCursor(int mode)
1295 static struct MouseCursorInfo *cursor_none = NULL;
1296 static struct MouseCursorInfo *cursor_playfield = NULL;
1297 struct MouseCursorInfo *cursor_new;
1299 if (cursor_none == NULL)
1300 cursor_none = get_cursor_from_image(cursor_image_none);
1302 if (cursor_playfield == NULL)
1303 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1305 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1306 mode == CURSOR_NONE ? cursor_none :
1307 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1309 SDLSetMouseCursor(cursor_new);
1311 gfx.cursor_mode = mode;
1315 /* ========================================================================= */
1316 /* audio functions */
1317 /* ========================================================================= */
1319 void OpenAudio(void)
1321 /* always start with reliable default values */
1322 audio.sound_available = FALSE;
1323 audio.music_available = FALSE;
1324 audio.loops_available = FALSE;
1326 audio.sound_enabled = FALSE;
1327 audio.sound_deactivated = FALSE;
1329 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1330 audio.mixer_pid = 0;
1331 audio.device_name = NULL;
1332 audio.device_fd = -1;
1334 audio.num_channels = 0;
1335 audio.music_channel = 0;
1336 audio.first_sound_channel = 0;
1341 void CloseAudio(void)
1345 audio.sound_enabled = FALSE;
1348 void SetAudioMode(boolean enabled)
1350 if (!audio.sound_available)
1353 audio.sound_enabled = enabled;
1357 /* ========================================================================= */
1358 /* event functions */
1359 /* ========================================================================= */
1361 void InitEventFilter(EventFilter filter_function)
1363 /* set event filter to filter out certain events */
1364 #if defined(TARGET_SDL2)
1365 SDL_SetEventFilter(filter_function, NULL);
1367 SDL_SetEventFilter(filter_function);
1371 boolean PendingEvent(void)
1373 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1376 void NextEvent(Event *event)
1378 SDLNextEvent(event);
1381 void PeekEvent(Event *event)
1383 #if defined(TARGET_SDL2)
1384 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1386 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1390 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1392 #if defined(TARGET_SDL2)
1393 /* key up/down events in SDL2 do not return text characters anymore */
1394 return event->keysym.sym;
1397 #if ENABLE_UNUSED_CODE
1398 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1399 (int)event->keysym.unicode,
1400 (int)event->keysym.sym,
1401 (int)SDL_GetModState());
1404 if (with_modifiers &&
1405 event->keysym.unicode > 0x0000 &&
1406 event->keysym.unicode < 0x2000)
1407 return event->keysym.unicode;
1409 return event->keysym.sym;
1414 KeyMod HandleKeyModState(Key key, int key_status)
1416 static KeyMod current_modifiers = KMOD_None;
1418 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1420 KeyMod new_modifier = KMOD_None;
1425 new_modifier = KMOD_Shift_L;
1428 new_modifier = KMOD_Shift_R;
1430 case KSYM_Control_L:
1431 new_modifier = KMOD_Control_L;
1433 case KSYM_Control_R:
1434 new_modifier = KMOD_Control_R;
1437 new_modifier = KMOD_Meta_L;
1440 new_modifier = KMOD_Meta_R;
1443 new_modifier = KMOD_Alt_L;
1446 new_modifier = KMOD_Alt_R;
1452 if (key_status == KEY_PRESSED)
1453 current_modifiers |= new_modifier;
1455 current_modifiers &= ~new_modifier;
1458 return current_modifiers;
1461 KeyMod GetKeyModState()
1463 return (KeyMod)SDL_GetModState();
1466 KeyMod GetKeyModStateFromEvents()
1468 /* always use key modifier state as tracked from key events (this is needed
1469 if the modifier key event was injected into the event queue, but the key
1470 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1471 query the keys as held pressed on the keyboard) -- this case is currently
1472 only used to filter out clipboard insert events from "True X-Mouse" tool */
1474 return HandleKeyModState(KSYM_UNDEFINED, 0);
1477 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1479 if (event->type != EVENT_CLIENTMESSAGE)
1482 return TRUE; /* the only possible message here is SDL_QUIT */
1486 /* ========================================================================= */
1487 /* joystick functions */
1488 /* ========================================================================= */
1490 void InitJoysticks()
1494 #if defined(NO_JOYSTICK)
1495 return; /* joysticks generally deactivated by compile-time directive */
1498 /* always start with reliable default values */
1499 joystick.status = JOYSTICK_NOT_AVAILABLE;
1500 for (i = 0; i < MAX_PLAYERS; i++)
1501 joystick.fd[i] = -1; /* joystick device closed */
1506 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1508 return SDLReadJoystick(nr, x, y, b1, b2);