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 gfx.drawing_area_changed = FALSE;
176 SetDrawDeactivationMask(REDRAW_NONE); /* do not deactivate drawing */
177 SetDrawBackgroundMask(REDRAW_NONE); /* deactivate masked drawing */
180 void InitGfxTileSizeInfo(int game_tile_size, int standard_tile_size)
182 gfx.game_tile_size = game_tile_size;
183 gfx.standard_tile_size = standard_tile_size;
186 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
194 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
202 void InitGfxDoor3Info(int ex, int ey, int exsize, int eysize)
210 void InitGfxWindowInfo(int win_xsize, int win_ysize)
212 gfx.win_xsize = win_xsize;
213 gfx.win_ysize = win_ysize;
215 gfx.background_bitmap_mask = REDRAW_NONE;
217 ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize, DEFAULT_DEPTH);
220 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
222 /* currently only used by MSDOS code to alloc VRAM buffer, if available */
223 /* 2009-03-24: also (temporarily?) used for overlapping blit workaround */
224 gfx.scrollbuffer_width = scrollbuffer_width;
225 gfx.scrollbuffer_height = scrollbuffer_height;
228 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
230 gfx.clipping_enabled = enabled;
233 gfx.clip_width = width;
234 gfx.clip_height = height;
237 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
239 gfx.draw_busy_anim_function = draw_busy_anim_function;
242 void InitGfxCustomArtworkInfo()
244 gfx.override_level_graphics = FALSE;
245 gfx.override_level_sounds = FALSE;
246 gfx.override_level_music = FALSE;
248 gfx.draw_init_text = TRUE;
251 void InitGfxOtherSettings()
253 gfx.cursor_mode = CURSOR_DEFAULT;
256 void SetDrawDeactivationMask(int draw_deactivation_mask)
258 gfx.draw_deactivation_mask = draw_deactivation_mask;
261 void SetDrawBackgroundMask(int draw_background_mask)
263 gfx.draw_background_mask = draw_background_mask;
266 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
268 if (background_bitmap_tile != NULL)
269 gfx.background_bitmap_mask |= mask;
271 gfx.background_bitmap_mask &= ~mask;
273 if (background_bitmap_tile == NULL) /* empty background requested */
276 if (mask == REDRAW_ALL)
277 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
278 0, 0, video.width, video.height);
279 else if (mask == REDRAW_FIELD)
280 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
281 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
282 else if (mask == REDRAW_DOOR_1)
283 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
284 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
287 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
289 /* remove every mask before setting mask for window */
290 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
291 SetBackgroundBitmap(NULL, 0xffff); /* !!! FIX THIS !!! */
292 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
295 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
297 /* remove window area mask before setting mask for main area */
298 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
299 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
300 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
303 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
305 /* remove window area mask before setting mask for door area */
306 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
307 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
308 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
312 /* ========================================================================= */
313 /* video functions */
314 /* ========================================================================= */
316 inline static int GetRealDepth(int depth)
318 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
321 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
322 int width, int height, Pixel color)
324 SDLFillRectangle(bitmap, x, y, width, height, color);
326 if (bitmap == backbuffer)
327 SetRedrawMaskFromArea(x, y, width, height);
330 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
331 int src_x, int src_y, int width, int height,
332 int dst_x, int dst_y, int mask_mode)
334 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
335 dst_x, dst_y, mask_mode);
337 if (dst_bitmap == backbuffer)
338 SetRedrawMaskFromArea(dst_x, dst_y, width, height);
341 void LimitScreenUpdates(boolean enable)
343 SDLLimitScreenUpdates(enable);
346 void InitVideoDisplay(void)
348 SDLInitVideoDisplay();
351 void CloseVideoDisplay(void)
353 KeyboardAutoRepeatOn();
355 SDL_QuitSubSystem(SDL_INIT_VIDEO);
358 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
361 video.height = height;
362 video.depth = GetRealDepth(depth);
364 video.fullscreen_available = FULLSCREEN_STATUS;
365 video.fullscreen_enabled = FALSE;
367 video.window_scaling_available = WINDOW_SCALING_STATUS;
369 SDLInitVideoBuffer(&backbuffer, &window, fullscreen);
371 video.initialized = TRUE;
376 inline static void FreeBitmapPointers(Bitmap *bitmap)
381 SDLFreeBitmapPointers(bitmap);
383 checked_free(bitmap->source_filename);
384 bitmap->source_filename = NULL;
387 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
390 if (src_bitmap == NULL || dst_bitmap == NULL)
393 FreeBitmapPointers(dst_bitmap);
395 *dst_bitmap = *src_bitmap;
398 void FreeBitmap(Bitmap *bitmap)
403 FreeBitmapPointers(bitmap);
408 Bitmap *CreateBitmapStruct(void)
410 return checked_calloc(sizeof(struct SDLSurfaceInfo));
413 Bitmap *CreateBitmap(int width, int height, int depth)
415 Bitmap *new_bitmap = CreateBitmapStruct();
416 int real_width = MAX(1, width); /* prevent zero bitmap width */
417 int real_height = MAX(1, height); /* prevent zero bitmap height */
418 int real_depth = GetRealDepth(depth);
420 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
422 new_bitmap->width = real_width;
423 new_bitmap->height = real_height;
428 void ReCreateBitmap(Bitmap **bitmap, int width, int height, int depth)
430 Bitmap *new_bitmap = CreateBitmap(width, height, depth);
434 *bitmap = new_bitmap;
438 TransferBitmapPointers(new_bitmap, *bitmap);
443 void CloseWindow(DrawWindow *window)
447 void SetRedrawMaskFromArea(int x, int y, int width, int height)
451 int x2 = x + width - 1;
452 int y2 = y + height - 1;
454 if (width == 0 || height == 0)
457 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
458 redraw_mask |= REDRAW_FIELD;
459 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
460 redraw_mask |= REDRAW_DOOR_1;
461 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
462 redraw_mask |= REDRAW_DOOR_2;
463 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
464 redraw_mask |= REDRAW_DOOR_3;
466 redraw_mask = REDRAW_ALL;
469 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
472 if (draw_mask == REDRAW_NONE)
475 if (draw_mask & REDRAW_ALL)
478 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
481 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
484 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
487 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
493 boolean DrawingDeactivated(int x, int y, int width, int height)
495 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
498 boolean DrawingOnBackground(int x, int y)
500 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
501 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
504 boolean DrawingAreaChanged()
506 int drawing_area_changed = gfx.drawing_area_changed;
508 // reset flag for change of drawing area after querying it
509 gfx.drawing_area_changed = FALSE;
511 return drawing_area_changed;
514 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
515 int *width, int *height, boolean is_dest)
517 int clip_x, clip_y, clip_width, clip_height;
519 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
521 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
522 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
523 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
524 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
530 clip_width = bitmap->width;
531 clip_height = bitmap->height;
534 /* skip if rectangle completely outside bitmap */
536 if (*x + *width <= clip_x ||
537 *y + *height <= clip_y ||
538 *x >= clip_x + clip_width ||
539 *y >= clip_y + clip_height)
542 /* clip if rectangle overlaps bitmap */
546 *width -= clip_x - *x;
549 else if (*x + *width > clip_x + clip_width)
551 *width = clip_x + clip_width - *x;
556 *height -= clip_y - *y;
559 else if (*y + *height > clip_y + clip_height)
561 *height = clip_y + clip_height - *y;
567 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
568 int src_x, int src_y, int width, int height,
569 int dst_x, int dst_y)
571 int dst_x_unclipped = dst_x;
572 int dst_y_unclipped = dst_y;
574 if (src_bitmap == NULL || dst_bitmap == NULL)
577 if (DrawingDeactivated(dst_x, dst_y, width, height))
580 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
581 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
584 /* source x/y might need adjustment if destination x/y was clipped top/left */
585 src_x += dst_x - dst_x_unclipped;
586 src_y += dst_y - dst_y_unclipped;
588 #if defined(TARGET_SDL2)
589 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
590 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
591 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
592 but is already fixed in SVN and should therefore finally be fixed with
593 the next official SDL release, which is probably version 1.2.14.) */
594 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
596 if (src_bitmap == dst_bitmap)
598 /* needed when blitting directly to same bitmap -- should not be needed with
599 recent SDL libraries, but apparently does not work in 1.2.11 directly */
601 static Bitmap *tmp_bitmap = NULL;
602 static int tmp_bitmap_xsize = 0;
603 static int tmp_bitmap_ysize = 0;
605 /* start with largest static bitmaps for initial bitmap size ... */
606 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
608 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
609 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
612 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
613 if (src_bitmap->width > tmp_bitmap_xsize ||
614 src_bitmap->height > tmp_bitmap_ysize)
616 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
617 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
619 FreeBitmap(tmp_bitmap);
624 if (tmp_bitmap == NULL)
625 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
628 sysCopyArea(src_bitmap, tmp_bitmap,
629 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
630 sysCopyArea(tmp_bitmap, dst_bitmap,
631 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
637 sysCopyArea(src_bitmap, dst_bitmap,
638 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
641 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
642 int src_x, int src_y, int src_width, int src_height,
643 int dst_x, int dst_y, int dst_width, int dst_height)
645 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
646 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
647 int dst_xsize = dst_width;
648 int dst_ysize = dst_height;
649 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
650 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
653 for (y = 0; y < src_ysteps; y++)
655 for (x = 0; x < src_xsteps; x++)
657 int draw_x = dst_x + x * src_xsize;
658 int draw_y = dst_y + y * src_ysize;
659 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
660 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
662 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
668 void FadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
669 int fade_mode, int fade_delay, int post_delay,
670 void (*draw_border_function)(void))
672 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
673 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
676 SDLFadeRectangle(bitmap_cross, x, y, width, height,
677 fade_mode, fade_delay, post_delay, draw_border_function);
680 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
683 if (DrawingDeactivated(x, y, width, height))
686 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
689 sysFillRectangle(bitmap, x, y, width, height, color);
692 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
694 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
697 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
698 int width, int height)
700 if (DrawingOnBackground(x, y))
701 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
703 ClearRectangle(bitmap, x, y, width, height);
706 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
707 int src_x, int src_y, int width, int height,
708 int dst_x, int dst_y)
710 if (DrawingDeactivated(dst_x, dst_y, width, height))
713 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
714 dst_x, dst_y, BLIT_MASKED);
717 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
718 int src_x, int src_y, int width, int height,
719 int dst_x, int dst_y)
721 if (DrawingOnBackground(dst_x, dst_y))
723 /* draw background */
724 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
727 /* draw foreground */
728 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
732 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
736 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
739 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
742 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
745 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
748 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
749 int to_x, int to_y, Pixel pixel, int line_width)
753 for (x = 0; x < line_width; x++)
755 for (y = 0; y < line_width; y++)
757 int dx = x - line_width / 2;
758 int dy = y - line_width / 2;
760 if ((x == 0 && y == 0) ||
761 (x == 0 && y == line_width - 1) ||
762 (x == line_width - 1 && y == 0) ||
763 (x == line_width - 1 && y == line_width - 1))
767 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
772 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
777 for (i = 0; i < num_points - 1; i++)
778 DrawLine(bitmap, points[i].x, points[i].y,
779 points[i + 1].x, points[i + 1].y, pixel, line_width);
782 SDLDrawLines(bitmap->surface, points, num_points, pixel);
786 Pixel GetPixel(Bitmap *bitmap, int x, int y)
788 if (x < 0 || x >= bitmap->width ||
789 y < 0 || y >= bitmap->height)
792 return SDLGetPixel(bitmap, x, y);
795 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
796 unsigned int color_g, unsigned int color_b)
798 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
801 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
803 unsigned int color_r = (color >> 16) & 0xff;
804 unsigned int color_g = (color >> 8) & 0xff;
805 unsigned int color_b = (color >> 0) & 0xff;
807 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
810 void KeyboardAutoRepeatOn(void)
812 #if defined(TARGET_SDL2)
813 keyrepeat_status = TRUE;
815 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
816 SDL_DEFAULT_REPEAT_INTERVAL / 2);
817 SDL_EnableUNICODE(1);
821 void KeyboardAutoRepeatOff(void)
823 #if defined(TARGET_SDL2)
824 keyrepeat_status = FALSE;
826 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
827 SDL_EnableUNICODE(0);
831 boolean SetVideoMode(boolean fullscreen)
833 return SDLSetVideoMode(&backbuffer, fullscreen);
836 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
838 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
839 (!fullscreen && video.fullscreen_enabled))
840 fullscreen = SetVideoMode(fullscreen);
845 Bitmap *LoadImage(char *filename)
849 new_bitmap = SDLLoadImage(filename);
852 new_bitmap->source_filename = getStringCopy(filename);
857 Bitmap *LoadCustomImage(char *basename)
859 char *filename = getCustomImageFilename(basename);
862 if (filename == NULL)
863 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
865 if ((new_bitmap = LoadImage(filename)) == NULL)
866 Error(ERR_EXIT, "LoadImage() failed: %s", GetError());
871 void ReloadCustomImage(Bitmap *bitmap, char *basename)
873 char *filename = getCustomImageFilename(basename);
876 if (filename == NULL) /* (should never happen) */
878 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
882 if (strEqual(filename, bitmap->source_filename))
884 /* The old and new image are the same (have the same filename and path).
885 This usually means that this image does not exist in this graphic set
886 and a fallback to the existing image is done. */
891 if ((new_bitmap = LoadImage(filename)) == NULL)
893 Error(ERR_WARN, "LoadImage() failed: %s", GetError());
897 if (bitmap->width != new_bitmap->width ||
898 bitmap->height != new_bitmap->height)
900 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
902 FreeBitmap(new_bitmap);
906 TransferBitmapPointers(new_bitmap, bitmap);
910 Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
912 Bitmap *dst_bitmap = SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
917 static void SetMaskedBitmapSurface(Bitmap *bitmap)
922 SDL_Surface *surface = bitmap->surface;
924 if (bitmap->surface_masked)
925 SDL_FreeSurface(bitmap->surface_masked);
927 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
928 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
930 if ((bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
931 Error(ERR_EXIT, "SDL_DisplayFormat() failed");
933 SDL_SetColorKey(surface, UNSET_TRANSPARENT_PIXEL, 0);
936 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
938 if (bitmaps[IMG_BITMAP_CUSTOM])
940 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
942 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
945 if (gfx.game_tile_size == gfx.standard_tile_size)
947 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
952 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
953 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
954 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
956 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
958 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
959 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
961 SetMaskedBitmapSurface(bitmap_new);
964 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
965 int tile_size, boolean create_small_bitmaps)
967 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
968 Bitmap *tmp_bitmap_final = NULL;
969 Bitmap *tmp_bitmap_0 = NULL;
970 Bitmap *tmp_bitmap_1 = NULL;
971 Bitmap *tmp_bitmap_2 = NULL;
972 Bitmap *tmp_bitmap_4 = NULL;
973 Bitmap *tmp_bitmap_8 = NULL;
974 Bitmap *tmp_bitmap_16 = NULL;
975 Bitmap *tmp_bitmap_32 = NULL;
976 int width_final, height_final;
977 int width_0, height_0;
978 int width_1, height_1;
979 int width_2, height_2;
980 int width_4, height_4;
981 int width_8, height_8;
982 int width_16, height_16;
983 int width_32, height_32;
984 int old_width, old_height;
987 print_timestamp_init("CreateScaledBitmaps");
989 old_width = old_bitmap->width;
990 old_height = old_bitmap->height;
992 /* calculate new image dimensions for final image size */
993 width_final = old_width * zoom_factor;
994 height_final = old_height * zoom_factor;
996 /* get image with final size (this might require scaling up) */
997 /* ("final" size may result in non-standard tile size image) */
998 if (zoom_factor != 1)
999 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1001 tmp_bitmap_final = old_bitmap;
1003 UPDATE_BUSY_STATE();
1005 width_0 = width_1 = width_final;
1006 height_0 = height_1 = height_final;
1008 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1010 if (create_small_bitmaps)
1012 /* check if we have a non-gameplay tile size image */
1013 if (tile_size != gfx.game_tile_size)
1015 /* get image with gameplay tile size */
1016 width_0 = width_final * gfx.game_tile_size / tile_size;
1017 height_0 = height_final * gfx.game_tile_size / tile_size;
1019 if (width_0 == old_width)
1020 tmp_bitmap_0 = old_bitmap;
1021 else if (width_0 == width_final)
1022 tmp_bitmap_0 = tmp_bitmap_final;
1024 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1026 UPDATE_BUSY_STATE();
1029 /* check if we have a non-standard tile size image */
1030 if (tile_size != gfx.standard_tile_size)
1032 /* get image with standard tile size */
1033 width_1 = width_final * gfx.standard_tile_size / tile_size;
1034 height_1 = height_final * gfx.standard_tile_size / tile_size;
1036 if (width_1 == old_width)
1037 tmp_bitmap_1 = old_bitmap;
1038 else if (width_1 == width_final)
1039 tmp_bitmap_1 = tmp_bitmap_final;
1040 else if (width_1 == width_0)
1041 tmp_bitmap_1 = tmp_bitmap_0;
1043 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1045 UPDATE_BUSY_STATE();
1048 /* calculate new image dimensions for small images */
1049 width_2 = width_1 / 2;
1050 height_2 = height_1 / 2;
1051 width_4 = width_1 / 4;
1052 height_4 = height_1 / 4;
1053 width_8 = width_1 / 8;
1054 height_8 = height_1 / 8;
1055 width_16 = width_1 / 16;
1056 height_16 = height_1 / 16;
1057 width_32 = width_1 / 32;
1058 height_32 = height_1 / 32;
1060 /* get image with 1/2 of normal size (for use in the level editor) */
1061 if (width_2 == old_width)
1062 tmp_bitmap_2 = old_bitmap;
1064 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1066 UPDATE_BUSY_STATE();
1068 /* get image with 1/4 of normal size (for use in the level editor) */
1069 if (width_4 == old_width)
1070 tmp_bitmap_4 = old_bitmap;
1072 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1074 UPDATE_BUSY_STATE();
1076 /* get image with 1/8 of normal size (for use on the preview screen) */
1077 if (width_8 == old_width)
1078 tmp_bitmap_8 = old_bitmap;
1080 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1082 UPDATE_BUSY_STATE();
1084 /* get image with 1/16 of normal size (for use on the preview screen) */
1085 if (width_16 == old_width)
1086 tmp_bitmap_16 = old_bitmap;
1088 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1090 UPDATE_BUSY_STATE();
1092 /* get image with 1/32 of normal size (for use on the preview screen) */
1093 if (width_32 == old_width)
1094 tmp_bitmap_32 = old_bitmap;
1096 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1098 UPDATE_BUSY_STATE();
1100 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1101 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1102 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1103 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1104 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1105 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1107 if (width_0 != width_1)
1108 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1110 if (bitmaps[IMG_BITMAP_CUSTOM])
1111 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1113 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1115 boolean free_old_bitmap = TRUE;
1117 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1118 if (bitmaps[i] == old_bitmap)
1119 free_old_bitmap = FALSE;
1121 if (free_old_bitmap)
1122 FreeBitmap(old_bitmap);
1126 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1129 // create corresponding bitmaps for masked blitting
1130 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1131 if (bitmaps[i] != NULL &&
1132 bitmaps[i] != old_bitmap)
1133 SetMaskedBitmapSurface(bitmaps[i]);
1135 UPDATE_BUSY_STATE();
1137 print_timestamp_done("CreateScaledBitmaps");
1140 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1143 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1146 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1148 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1152 /* ------------------------------------------------------------------------- */
1153 /* mouse pointer functions */
1154 /* ------------------------------------------------------------------------- */
1156 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1158 /* XPM image definitions */
1159 static const char *cursor_image_none[] =
1161 /* width height num_colors chars_per_pixel */
1191 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1192 static const char *cursor_image_dot[] =
1194 /* width height num_colors chars_per_pixel */
1223 static const char **cursor_image_playfield = cursor_image_dot;
1225 /* some people complained about a "white dot" on the screen and thought it
1226 was a graphical error... OK, let's just remove the whole pointer :-) */
1227 static const char **cursor_image_playfield = cursor_image_none;
1230 static const int cursor_bit_order = BIT_ORDER_MSB;
1232 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1234 struct MouseCursorInfo *cursor;
1235 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1236 int header_lines = 4;
1239 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1241 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1244 for (y = 0; y < cursor->width; y++)
1246 for (x = 0; x < cursor->height; x++)
1249 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1254 cursor->data[i] = cursor->mask[i] = 0;
1257 switch (image[header_lines + y][x])
1260 cursor->data[i] |= bit_mask;
1261 cursor->mask[i] |= bit_mask;
1265 cursor->mask[i] |= bit_mask;
1274 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1279 void SetMouseCursor(int mode)
1281 static struct MouseCursorInfo *cursor_none = NULL;
1282 static struct MouseCursorInfo *cursor_playfield = NULL;
1283 struct MouseCursorInfo *cursor_new;
1285 if (cursor_none == NULL)
1286 cursor_none = get_cursor_from_image(cursor_image_none);
1288 if (cursor_playfield == NULL)
1289 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1291 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1292 mode == CURSOR_NONE ? cursor_none :
1293 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1295 SDLSetMouseCursor(cursor_new);
1297 gfx.cursor_mode = mode;
1301 /* ========================================================================= */
1302 /* audio functions */
1303 /* ========================================================================= */
1305 void OpenAudio(void)
1307 /* always start with reliable default values */
1308 audio.sound_available = FALSE;
1309 audio.music_available = FALSE;
1310 audio.loops_available = FALSE;
1312 audio.sound_enabled = FALSE;
1313 audio.sound_deactivated = FALSE;
1315 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1316 audio.mixer_pid = 0;
1317 audio.device_name = NULL;
1318 audio.device_fd = -1;
1320 audio.num_channels = 0;
1321 audio.music_channel = 0;
1322 audio.first_sound_channel = 0;
1327 void CloseAudio(void)
1331 audio.sound_enabled = FALSE;
1334 void SetAudioMode(boolean enabled)
1336 if (!audio.sound_available)
1339 audio.sound_enabled = enabled;
1343 /* ========================================================================= */
1344 /* event functions */
1345 /* ========================================================================= */
1347 void InitEventFilter(EventFilter filter_function)
1349 /* set event filter to filter out certain events */
1350 #if defined(TARGET_SDL2)
1351 SDL_SetEventFilter(filter_function, NULL);
1353 SDL_SetEventFilter(filter_function);
1357 boolean PendingEvent(void)
1359 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1362 void NextEvent(Event *event)
1364 SDLNextEvent(event);
1367 void PeekEvent(Event *event)
1369 #if defined(TARGET_SDL2)
1370 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1372 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1376 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1378 #if defined(TARGET_SDL2)
1379 /* key up/down events in SDL2 do not return text characters anymore */
1380 return event->keysym.sym;
1383 #if ENABLE_UNUSED_CODE
1384 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1385 (int)event->keysym.unicode,
1386 (int)event->keysym.sym,
1387 (int)SDL_GetModState());
1390 if (with_modifiers &&
1391 event->keysym.unicode > 0x0000 &&
1392 event->keysym.unicode < 0x2000)
1393 return event->keysym.unicode;
1395 return event->keysym.sym;
1400 KeyMod HandleKeyModState(Key key, int key_status)
1402 static KeyMod current_modifiers = KMOD_None;
1404 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1406 KeyMod new_modifier = KMOD_None;
1411 new_modifier = KMOD_Shift_L;
1414 new_modifier = KMOD_Shift_R;
1416 case KSYM_Control_L:
1417 new_modifier = KMOD_Control_L;
1419 case KSYM_Control_R:
1420 new_modifier = KMOD_Control_R;
1423 new_modifier = KMOD_Meta_L;
1426 new_modifier = KMOD_Meta_R;
1429 new_modifier = KMOD_Alt_L;
1432 new_modifier = KMOD_Alt_R;
1438 if (key_status == KEY_PRESSED)
1439 current_modifiers |= new_modifier;
1441 current_modifiers &= ~new_modifier;
1444 return current_modifiers;
1447 KeyMod GetKeyModState()
1449 return (KeyMod)SDL_GetModState();
1452 KeyMod GetKeyModStateFromEvents()
1454 /* always use key modifier state as tracked from key events (this is needed
1455 if the modifier key event was injected into the event queue, but the key
1456 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1457 query the keys as held pressed on the keyboard) -- this case is currently
1458 only used to filter out clipboard insert events from "True X-Mouse" tool */
1460 return HandleKeyModState(KSYM_UNDEFINED, 0);
1463 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1465 if (event->type != EVENT_CLIENTMESSAGE)
1468 return TRUE; /* the only possible message here is SDL_QUIT */
1472 /* ========================================================================= */
1473 /* joystick functions */
1474 /* ========================================================================= */
1476 void InitJoysticks()
1480 #if defined(NO_JOYSTICK)
1481 return; /* joysticks generally deactivated by compile-time directive */
1484 /* always start with reliable default values */
1485 joystick.status = JOYSTICK_NOT_AVAILABLE;
1486 for (i = 0; i < MAX_PLAYERS; i++)
1487 joystick.fd[i] = -1; /* joystick device closed */
1492 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1494 return SDLReadJoystick(nr, x, y, b1, b2);