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);
211 #if defined(TARGET_SDL2)
212 ReCreateBitmap(&gfx.final_screen_bitmap, win_xsize, win_ysize, DEFAULT_DEPTH);
215 ReCreateBitmap(&gfx.fade_bitmap_source, win_xsize, win_ysize, DEFAULT_DEPTH);
216 ReCreateBitmap(&gfx.fade_bitmap_target, win_xsize, win_ysize, DEFAULT_DEPTH);
217 ReCreateBitmap(&gfx.fade_bitmap_black, win_xsize, win_ysize, DEFAULT_DEPTH);
219 ClearRectangle(gfx.fade_bitmap_black, 0, 0, win_xsize, win_ysize);
222 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
224 /* currently only used by MSDOS code to alloc VRAM buffer, if available */
225 /* 2009-03-24: also (temporarily?) used for overlapping blit workaround */
226 gfx.scrollbuffer_width = scrollbuffer_width;
227 gfx.scrollbuffer_height = scrollbuffer_height;
230 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
232 gfx.clipping_enabled = enabled;
235 gfx.clip_width = width;
236 gfx.clip_height = height;
239 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
241 gfx.draw_busy_anim_function = draw_busy_anim_function;
244 void InitGfxDrawGlobalAnimFunction(void (*draw_global_anim_function)(int))
246 gfx.draw_global_anim_function = draw_global_anim_function;
249 void InitGfxDrawGlobalBorderFunction(void (*draw_global_border_function)(int))
251 gfx.draw_global_border_function = draw_global_border_function;
254 void InitGfxCustomArtworkInfo()
256 gfx.override_level_graphics = FALSE;
257 gfx.override_level_sounds = FALSE;
258 gfx.override_level_music = FALSE;
260 gfx.draw_init_text = TRUE;
263 void InitGfxOtherSettings()
265 gfx.cursor_mode = CURSOR_DEFAULT;
268 void SetDrawDeactivationMask(int draw_deactivation_mask)
270 gfx.draw_deactivation_mask = draw_deactivation_mask;
273 void SetDrawBackgroundMask(int draw_background_mask)
275 gfx.draw_background_mask = draw_background_mask;
278 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
280 if (background_bitmap_tile != NULL)
281 gfx.background_bitmap_mask |= mask;
283 gfx.background_bitmap_mask &= ~mask;
285 if (background_bitmap_tile == NULL) /* empty background requested */
288 if (mask == REDRAW_ALL)
289 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
290 0, 0, video.width, video.height);
291 else if (mask == REDRAW_FIELD)
292 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
293 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
294 else if (mask == REDRAW_DOOR_1)
295 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
296 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
299 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
301 /* remove every mask before setting mask for window */
302 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
303 SetBackgroundBitmap(NULL, 0xffff); /* !!! FIX THIS !!! */
304 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
307 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
309 /* remove window area mask before setting mask for main area */
310 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
311 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
312 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
315 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
317 /* remove window area mask before setting mask for door area */
318 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
319 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
320 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
324 /* ========================================================================= */
325 /* video functions */
326 /* ========================================================================= */
328 inline static int GetRealDepth(int depth)
330 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
333 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
334 int width, int height, Pixel color)
336 SDLFillRectangle(bitmap, x, y, width, height, color);
338 if (bitmap == backbuffer)
339 SetRedrawMaskFromArea(x, y, width, height);
342 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
343 int src_x, int src_y, int width, int height,
344 int dst_x, int dst_y, int mask_mode)
346 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
347 dst_x, dst_y, mask_mode);
349 if (dst_bitmap == backbuffer)
350 SetRedrawMaskFromArea(dst_x, dst_y, width, height);
353 void LimitScreenUpdates(boolean enable)
355 SDLLimitScreenUpdates(enable);
358 void InitVideoDisplay(void)
360 SDLInitVideoDisplay();
363 void CloseVideoDisplay(void)
365 KeyboardAutoRepeatOn();
367 SDL_QuitSubSystem(SDL_INIT_VIDEO);
370 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
373 video.height = height;
374 video.depth = GetRealDepth(depth);
376 video.fullscreen_available = FULLSCREEN_STATUS;
377 video.fullscreen_enabled = FALSE;
379 video.window_scaling_available = WINDOW_SCALING_STATUS;
381 SDLInitVideoBuffer(fullscreen);
383 video.initialized = TRUE;
388 inline static void FreeBitmapPointers(Bitmap *bitmap)
393 SDLFreeBitmapPointers(bitmap);
395 checked_free(bitmap->source_filename);
396 bitmap->source_filename = NULL;
399 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
402 if (src_bitmap == NULL || dst_bitmap == NULL)
405 FreeBitmapPointers(dst_bitmap);
407 *dst_bitmap = *src_bitmap;
410 void FreeBitmap(Bitmap *bitmap)
415 FreeBitmapPointers(bitmap);
420 Bitmap *CreateBitmapStruct(void)
422 return checked_calloc(sizeof(Bitmap));
425 Bitmap *CreateBitmap(int width, int height, int depth)
427 Bitmap *new_bitmap = CreateBitmapStruct();
428 int real_width = MAX(1, width); /* prevent zero bitmap width */
429 int real_height = MAX(1, height); /* prevent zero bitmap height */
430 int real_depth = GetRealDepth(depth);
432 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
434 new_bitmap->width = real_width;
435 new_bitmap->height = real_height;
440 void ReCreateBitmap(Bitmap **bitmap, int width, int height, int depth)
442 Bitmap *new_bitmap = CreateBitmap(width, height, depth);
446 *bitmap = new_bitmap;
450 TransferBitmapPointers(new_bitmap, *bitmap);
455 void CloseWindow(DrawWindow *window)
459 void SetRedrawMaskFromArea(int x, int y, int width, int height)
463 int x2 = x + width - 1;
464 int y2 = y + height - 1;
466 if (width == 0 || height == 0)
469 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
470 redraw_mask |= REDRAW_FIELD;
471 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
472 redraw_mask |= REDRAW_DOOR_1;
473 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
474 redraw_mask |= REDRAW_DOOR_2;
475 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
476 redraw_mask |= REDRAW_DOOR_3;
478 redraw_mask = REDRAW_ALL;
481 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
484 if (draw_mask == REDRAW_NONE)
487 if (draw_mask & REDRAW_ALL)
490 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
493 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
496 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
499 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
505 boolean DrawingDeactivated(int x, int y, int width, int height)
507 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
510 boolean DrawingOnBackground(int x, int y)
512 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
513 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
516 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
517 int *width, int *height, boolean is_dest)
519 int clip_x, clip_y, clip_width, clip_height;
521 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
523 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
524 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
525 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
526 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
532 clip_width = bitmap->width;
533 clip_height = bitmap->height;
536 /* skip if rectangle completely outside bitmap */
538 if (*x + *width <= clip_x ||
539 *y + *height <= clip_y ||
540 *x >= clip_x + clip_width ||
541 *y >= clip_y + clip_height)
544 /* clip if rectangle overlaps bitmap */
548 *width -= clip_x - *x;
551 else if (*x + *width > clip_x + clip_width)
553 *width = clip_x + clip_width - *x;
558 *height -= clip_y - *y;
561 else if (*y + *height > clip_y + clip_height)
563 *height = clip_y + clip_height - *y;
569 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
570 int src_x, int src_y, int width, int height,
571 int dst_x, int dst_y)
573 int dst_x_unclipped = dst_x;
574 int dst_y_unclipped = dst_y;
576 if (src_bitmap == NULL || dst_bitmap == NULL)
579 if (DrawingDeactivated(dst_x, dst_y, width, height))
582 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
583 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
586 /* source x/y might need adjustment if destination x/y was clipped top/left */
587 src_x += dst_x - dst_x_unclipped;
588 src_y += dst_y - dst_y_unclipped;
590 #if defined(TARGET_SDL2)
591 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
592 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
593 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
594 but is already fixed in SVN and should therefore finally be fixed with
595 the next official SDL release, which is probably version 1.2.14.) */
596 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
598 if (src_bitmap == dst_bitmap)
600 /* needed when blitting directly to same bitmap -- should not be needed with
601 recent SDL libraries, but apparently does not work in 1.2.11 directly */
603 static Bitmap *tmp_bitmap = NULL;
604 static int tmp_bitmap_xsize = 0;
605 static int tmp_bitmap_ysize = 0;
607 /* start with largest static bitmaps for initial bitmap size ... */
608 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
610 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
611 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
614 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
615 if (src_bitmap->width > tmp_bitmap_xsize ||
616 src_bitmap->height > tmp_bitmap_ysize)
618 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
619 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
621 FreeBitmap(tmp_bitmap);
626 if (tmp_bitmap == NULL)
627 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
630 sysCopyArea(src_bitmap, tmp_bitmap,
631 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
632 sysCopyArea(tmp_bitmap, dst_bitmap,
633 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
639 sysCopyArea(src_bitmap, dst_bitmap,
640 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
643 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
644 int src_x, int src_y, int src_width, int src_height,
645 int dst_x, int dst_y, int dst_width, int dst_height)
647 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
648 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
649 int dst_xsize = dst_width;
650 int dst_ysize = dst_height;
651 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
652 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
655 for (y = 0; y < src_ysteps; y++)
657 for (x = 0; x < src_xsteps; x++)
659 int draw_x = dst_x + x * src_xsize;
660 int draw_y = dst_y + y * src_ysize;
661 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
662 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
664 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
670 void FadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
671 int fade_mode, int fade_delay, int post_delay,
672 void (*draw_border_function)(void))
674 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
675 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
678 SDLFadeRectangle(bitmap_cross, x, y, width, height,
679 fade_mode, fade_delay, post_delay, draw_border_function);
682 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
685 if (DrawingDeactivated(x, y, width, height))
688 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
691 sysFillRectangle(bitmap, x, y, width, height, color);
694 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
696 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
699 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
700 int width, int height)
702 if (DrawingOnBackground(x, y))
703 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
705 ClearRectangle(bitmap, x, y, width, height);
708 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
709 int src_x, int src_y, int width, int height,
710 int dst_x, int dst_y)
712 if (DrawingDeactivated(dst_x, dst_y, width, height))
715 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
716 dst_x, dst_y, BLIT_MASKED);
719 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
720 int src_x, int src_y, int width, int height,
721 int dst_x, int dst_y)
723 if (DrawingOnBackground(dst_x, dst_y))
725 /* draw background */
726 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
729 /* draw foreground */
730 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
734 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
738 void BlitTexture(Bitmap *bitmap,
739 int src_x, int src_y, int width, int height,
740 int dst_x, int dst_y)
745 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
749 void BlitTextureMasked(Bitmap *bitmap,
750 int src_x, int src_y, int width, int height,
751 int dst_x, int dst_y)
756 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
760 void BlitToScreen(Bitmap *bitmap,
761 int src_x, int src_y, int width, int height,
762 int dst_x, int dst_y)
767 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
768 BlitBitmap(bitmap, gfx.final_screen_bitmap, src_x, src_y,
769 width, height, dst_x, dst_y);
771 BlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y);
774 void BlitToScreenMasked(Bitmap *bitmap,
775 int src_x, int src_y, int width, int height,
776 int dst_x, int dst_y)
781 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
782 BlitBitmapMasked(bitmap, gfx.final_screen_bitmap, src_x, src_y,
783 width, height, dst_x, dst_y);
785 BlitTextureMasked(bitmap, src_x, src_y, width, height, dst_x, dst_y);
788 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
791 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
794 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
797 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
800 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
801 int to_x, int to_y, Pixel pixel, int line_width)
805 for (x = 0; x < line_width; x++)
807 for (y = 0; y < line_width; y++)
809 int dx = x - line_width / 2;
810 int dy = y - line_width / 2;
812 if ((x == 0 && y == 0) ||
813 (x == 0 && y == line_width - 1) ||
814 (x == line_width - 1 && y == 0) ||
815 (x == line_width - 1 && y == line_width - 1))
819 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
824 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
829 for (i = 0; i < num_points - 1; i++)
830 DrawLine(bitmap, points[i].x, points[i].y,
831 points[i + 1].x, points[i + 1].y, pixel, line_width);
834 SDLDrawLines(bitmap->surface, points, num_points, pixel);
838 Pixel GetPixel(Bitmap *bitmap, int x, int y)
840 if (x < 0 || x >= bitmap->width ||
841 y < 0 || y >= bitmap->height)
844 return SDLGetPixel(bitmap, x, y);
847 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
848 unsigned int color_g, unsigned int color_b)
850 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
853 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
855 unsigned int color_r = (color >> 16) & 0xff;
856 unsigned int color_g = (color >> 8) & 0xff;
857 unsigned int color_b = (color >> 0) & 0xff;
859 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
862 void KeyboardAutoRepeatOn(void)
864 #if defined(TARGET_SDL2)
865 keyrepeat_status = TRUE;
867 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
868 SDL_DEFAULT_REPEAT_INTERVAL / 2);
869 SDL_EnableUNICODE(1);
873 void KeyboardAutoRepeatOff(void)
875 #if defined(TARGET_SDL2)
876 keyrepeat_status = FALSE;
878 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
879 SDL_EnableUNICODE(0);
883 boolean SetVideoMode(boolean fullscreen)
885 return SDLSetVideoMode(fullscreen);
888 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
890 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
891 (!fullscreen && video.fullscreen_enabled))
892 fullscreen = SetVideoMode(fullscreen);
897 Bitmap *LoadImage(char *filename)
901 new_bitmap = SDLLoadImage(filename);
904 new_bitmap->source_filename = getStringCopy(filename);
909 Bitmap *LoadCustomImage(char *basename)
911 char *filename = getCustomImageFilename(basename);
914 if (filename == NULL)
915 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
917 if ((new_bitmap = LoadImage(filename)) == NULL)
918 Error(ERR_EXIT, "LoadImage('%s') failed: %s", basename, GetError());
923 void ReloadCustomImage(Bitmap *bitmap, char *basename)
925 char *filename = getCustomImageFilename(basename);
928 if (filename == NULL) /* (should never happen) */
930 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
934 if (strEqual(filename, bitmap->source_filename))
936 /* The old and new image are the same (have the same filename and path).
937 This usually means that this image does not exist in this graphic set
938 and a fallback to the existing image is done. */
943 if ((new_bitmap = LoadImage(filename)) == NULL)
945 Error(ERR_WARN, "LoadImage('%s') failed: %s", basename, GetError());
949 if (bitmap->width != new_bitmap->width ||
950 bitmap->height != new_bitmap->height)
952 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
954 FreeBitmap(new_bitmap);
958 TransferBitmapPointers(new_bitmap, bitmap);
962 Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
964 Bitmap *dst_bitmap = SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
969 static void SetMaskedBitmapSurface(Bitmap *bitmap)
974 SDL_Surface *surface = bitmap->surface;
976 if (bitmap->surface_masked)
977 SDL_FreeSurface(bitmap->surface_masked);
979 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
980 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
982 if ((bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
983 Error(ERR_EXIT, "SDL_DisplayFormat() failed");
985 SDL_SetColorKey(surface, UNSET_TRANSPARENT_PIXEL, 0);
988 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
990 if (bitmaps[IMG_BITMAP_CUSTOM])
992 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
994 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
997 if (gfx.game_tile_size == gfx.standard_tile_size)
999 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1004 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
1005 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
1006 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
1008 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
1010 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
1011 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
1013 SetMaskedBitmapSurface(bitmap_new);
1016 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1017 int tile_size, boolean create_small_bitmaps)
1019 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1020 Bitmap *tmp_bitmap_final = NULL;
1021 Bitmap *tmp_bitmap_0 = NULL;
1022 Bitmap *tmp_bitmap_1 = NULL;
1023 Bitmap *tmp_bitmap_2 = NULL;
1024 Bitmap *tmp_bitmap_4 = NULL;
1025 Bitmap *tmp_bitmap_8 = NULL;
1026 Bitmap *tmp_bitmap_16 = NULL;
1027 Bitmap *tmp_bitmap_32 = NULL;
1028 int width_final, height_final;
1029 int width_0, height_0;
1030 int width_1, height_1;
1031 int width_2, height_2;
1032 int width_4, height_4;
1033 int width_8, height_8;
1034 int width_16, height_16;
1035 int width_32, height_32;
1036 int old_width, old_height;
1039 print_timestamp_init("CreateScaledBitmaps");
1041 old_width = old_bitmap->width;
1042 old_height = old_bitmap->height;
1044 /* calculate new image dimensions for final image size */
1045 width_final = old_width * zoom_factor;
1046 height_final = old_height * zoom_factor;
1048 /* get image with final size (this might require scaling up) */
1049 /* ("final" size may result in non-standard tile size image) */
1050 if (zoom_factor != 1)
1051 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1053 tmp_bitmap_final = old_bitmap;
1055 UPDATE_BUSY_STATE();
1057 width_0 = width_1 = width_final;
1058 height_0 = height_1 = height_final;
1060 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1062 if (create_small_bitmaps)
1064 /* check if we have a non-gameplay tile size image */
1065 if (tile_size != gfx.game_tile_size)
1067 /* get image with gameplay tile size */
1068 width_0 = width_final * gfx.game_tile_size / tile_size;
1069 height_0 = height_final * gfx.game_tile_size / tile_size;
1071 if (width_0 == old_width)
1072 tmp_bitmap_0 = old_bitmap;
1073 else if (width_0 == width_final)
1074 tmp_bitmap_0 = tmp_bitmap_final;
1076 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1078 UPDATE_BUSY_STATE();
1081 /* check if we have a non-standard tile size image */
1082 if (tile_size != gfx.standard_tile_size)
1084 /* get image with standard tile size */
1085 width_1 = width_final * gfx.standard_tile_size / tile_size;
1086 height_1 = height_final * gfx.standard_tile_size / tile_size;
1088 if (width_1 == old_width)
1089 tmp_bitmap_1 = old_bitmap;
1090 else if (width_1 == width_final)
1091 tmp_bitmap_1 = tmp_bitmap_final;
1092 else if (width_1 == width_0)
1093 tmp_bitmap_1 = tmp_bitmap_0;
1095 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1097 UPDATE_BUSY_STATE();
1100 /* calculate new image dimensions for small images */
1101 width_2 = width_1 / 2;
1102 height_2 = height_1 / 2;
1103 width_4 = width_1 / 4;
1104 height_4 = height_1 / 4;
1105 width_8 = width_1 / 8;
1106 height_8 = height_1 / 8;
1107 width_16 = width_1 / 16;
1108 height_16 = height_1 / 16;
1109 width_32 = width_1 / 32;
1110 height_32 = height_1 / 32;
1112 /* get image with 1/2 of normal size (for use in the level editor) */
1113 if (width_2 == old_width)
1114 tmp_bitmap_2 = old_bitmap;
1116 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1118 UPDATE_BUSY_STATE();
1120 /* get image with 1/4 of normal size (for use in the level editor) */
1121 if (width_4 == old_width)
1122 tmp_bitmap_4 = old_bitmap;
1124 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1126 UPDATE_BUSY_STATE();
1128 /* get image with 1/8 of normal size (for use on the preview screen) */
1129 if (width_8 == old_width)
1130 tmp_bitmap_8 = old_bitmap;
1132 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1134 UPDATE_BUSY_STATE();
1136 /* get image with 1/16 of normal size (for use on the preview screen) */
1137 if (width_16 == old_width)
1138 tmp_bitmap_16 = old_bitmap;
1140 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1142 UPDATE_BUSY_STATE();
1144 /* get image with 1/32 of normal size (for use on the preview screen) */
1145 if (width_32 == old_width)
1146 tmp_bitmap_32 = old_bitmap;
1148 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1150 UPDATE_BUSY_STATE();
1152 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1153 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1154 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1155 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1156 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1157 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1159 if (width_0 != width_1)
1160 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1162 if (bitmaps[IMG_BITMAP_CUSTOM])
1163 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1165 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1167 boolean free_old_bitmap = TRUE;
1169 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1170 if (bitmaps[i] == old_bitmap)
1171 free_old_bitmap = FALSE;
1173 if (free_old_bitmap)
1174 FreeBitmap(old_bitmap);
1178 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1181 // create corresponding bitmaps for masked blitting
1182 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1183 if (bitmaps[i] != NULL &&
1184 bitmaps[i] != old_bitmap)
1185 SetMaskedBitmapSurface(bitmaps[i]);
1187 UPDATE_BUSY_STATE();
1189 print_timestamp_done("CreateScaledBitmaps");
1192 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1195 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1198 void CreateBitmapTextures(Bitmap **bitmaps)
1200 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1203 void FreeBitmapTextures(Bitmap **bitmaps)
1205 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1208 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1210 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1214 /* ------------------------------------------------------------------------- */
1215 /* mouse pointer functions */
1216 /* ------------------------------------------------------------------------- */
1218 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1220 /* XPM image definitions */
1221 static const char *cursor_image_none[] =
1223 /* width height num_colors chars_per_pixel */
1253 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1254 static const char *cursor_image_dot[] =
1256 /* width height num_colors chars_per_pixel */
1285 static const char **cursor_image_playfield = cursor_image_dot;
1287 /* some people complained about a "white dot" on the screen and thought it
1288 was a graphical error... OK, let's just remove the whole pointer :-) */
1289 static const char **cursor_image_playfield = cursor_image_none;
1292 static const int cursor_bit_order = BIT_ORDER_MSB;
1294 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1296 struct MouseCursorInfo *cursor;
1297 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1298 int header_lines = 4;
1301 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1303 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1306 for (y = 0; y < cursor->width; y++)
1308 for (x = 0; x < cursor->height; x++)
1311 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1316 cursor->data[i] = cursor->mask[i] = 0;
1319 switch (image[header_lines + y][x])
1322 cursor->data[i] |= bit_mask;
1323 cursor->mask[i] |= bit_mask;
1327 cursor->mask[i] |= bit_mask;
1336 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1341 void SetMouseCursor(int mode)
1343 static struct MouseCursorInfo *cursor_none = NULL;
1344 static struct MouseCursorInfo *cursor_playfield = NULL;
1345 struct MouseCursorInfo *cursor_new;
1347 if (cursor_none == NULL)
1348 cursor_none = get_cursor_from_image(cursor_image_none);
1350 if (cursor_playfield == NULL)
1351 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1353 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1354 mode == CURSOR_NONE ? cursor_none :
1355 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1357 SDLSetMouseCursor(cursor_new);
1359 gfx.cursor_mode = mode;
1363 /* ========================================================================= */
1364 /* audio functions */
1365 /* ========================================================================= */
1367 void OpenAudio(void)
1369 /* always start with reliable default values */
1370 audio.sound_available = FALSE;
1371 audio.music_available = FALSE;
1372 audio.loops_available = FALSE;
1374 audio.sound_enabled = FALSE;
1375 audio.sound_deactivated = FALSE;
1377 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1378 audio.mixer_pid = 0;
1379 audio.device_name = NULL;
1380 audio.device_fd = -1;
1382 audio.num_channels = 0;
1383 audio.music_channel = 0;
1384 audio.first_sound_channel = 0;
1389 void CloseAudio(void)
1393 audio.sound_enabled = FALSE;
1396 void SetAudioMode(boolean enabled)
1398 if (!audio.sound_available)
1401 audio.sound_enabled = enabled;
1405 /* ========================================================================= */
1406 /* event functions */
1407 /* ========================================================================= */
1409 void InitEventFilter(EventFilter filter_function)
1411 /* set event filter to filter out certain events */
1412 #if defined(TARGET_SDL2)
1413 SDL_SetEventFilter(filter_function, NULL);
1415 SDL_SetEventFilter(filter_function);
1419 boolean PendingEvent(void)
1421 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1424 void NextEvent(Event *event)
1426 SDLNextEvent(event);
1429 void PeekEvent(Event *event)
1431 #if defined(TARGET_SDL2)
1432 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1434 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1438 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1440 #if defined(TARGET_SDL2)
1441 /* key up/down events in SDL2 do not return text characters anymore */
1442 return event->keysym.sym;
1445 #if ENABLE_UNUSED_CODE
1446 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1447 (int)event->keysym.unicode,
1448 (int)event->keysym.sym,
1449 (int)SDL_GetModState());
1452 if (with_modifiers &&
1453 event->keysym.unicode > 0x0000 &&
1454 event->keysym.unicode < 0x2000)
1455 return event->keysym.unicode;
1457 return event->keysym.sym;
1462 KeyMod HandleKeyModState(Key key, int key_status)
1464 static KeyMod current_modifiers = KMOD_None;
1466 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1468 KeyMod new_modifier = KMOD_None;
1473 new_modifier = KMOD_Shift_L;
1476 new_modifier = KMOD_Shift_R;
1478 case KSYM_Control_L:
1479 new_modifier = KMOD_Control_L;
1481 case KSYM_Control_R:
1482 new_modifier = KMOD_Control_R;
1485 new_modifier = KMOD_Meta_L;
1488 new_modifier = KMOD_Meta_R;
1491 new_modifier = KMOD_Alt_L;
1494 new_modifier = KMOD_Alt_R;
1500 if (key_status == KEY_PRESSED)
1501 current_modifiers |= new_modifier;
1503 current_modifiers &= ~new_modifier;
1506 return current_modifiers;
1509 KeyMod GetKeyModState()
1511 return (KeyMod)SDL_GetModState();
1514 KeyMod GetKeyModStateFromEvents()
1516 /* always use key modifier state as tracked from key events (this is needed
1517 if the modifier key event was injected into the event queue, but the key
1518 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1519 query the keys as held pressed on the keyboard) -- this case is currently
1520 only used to filter out clipboard insert events from "True X-Mouse" tool */
1522 return HandleKeyModState(KSYM_UNDEFINED, 0);
1525 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1527 if (event->type != EVENT_CLIENTMESSAGE)
1530 return TRUE; /* the only possible message here is SDL_QUIT */
1534 /* ========================================================================= */
1535 /* joystick functions */
1536 /* ========================================================================= */
1538 void InitJoysticks()
1542 #if defined(NO_JOYSTICK)
1543 return; /* joysticks generally deactivated by compile-time directive */
1546 /* always start with reliable default values */
1547 joystick.status = JOYSTICK_NOT_AVAILABLE;
1548 for (i = 0; i < MAX_PLAYERS; i++)
1549 joystick.fd[i] = -1; /* joystick device closed */
1554 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1556 return SDLReadJoystick(nr, x, y, b1, b2);