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 USE_FINAL_SCREEN_BITMAP
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(&backbuffer, &window, 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 USE_FINAL_SCREEN_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);
775 void BlitToScreenMasked(Bitmap *bitmap,
776 int src_x, int src_y, int width, int height,
777 int dst_x, int dst_y)
782 #if USE_FINAL_SCREEN_BITMAP
783 BlitBitmapMasked(bitmap, gfx.final_screen_bitmap, src_x, src_y,
784 width, height, dst_x, dst_y);
786 BlitTextureMasked(bitmap, src_x, src_y, width, height, dst_x, dst_y);
790 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
793 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
796 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
799 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
802 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
803 int to_x, int to_y, Pixel pixel, int line_width)
807 for (x = 0; x < line_width; x++)
809 for (y = 0; y < line_width; y++)
811 int dx = x - line_width / 2;
812 int dy = y - line_width / 2;
814 if ((x == 0 && y == 0) ||
815 (x == 0 && y == line_width - 1) ||
816 (x == line_width - 1 && y == 0) ||
817 (x == line_width - 1 && y == line_width - 1))
821 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
826 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
831 for (i = 0; i < num_points - 1; i++)
832 DrawLine(bitmap, points[i].x, points[i].y,
833 points[i + 1].x, points[i + 1].y, pixel, line_width);
836 SDLDrawLines(bitmap->surface, points, num_points, pixel);
840 Pixel GetPixel(Bitmap *bitmap, int x, int y)
842 if (x < 0 || x >= bitmap->width ||
843 y < 0 || y >= bitmap->height)
846 return SDLGetPixel(bitmap, x, y);
849 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
850 unsigned int color_g, unsigned int color_b)
852 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
855 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
857 unsigned int color_r = (color >> 16) & 0xff;
858 unsigned int color_g = (color >> 8) & 0xff;
859 unsigned int color_b = (color >> 0) & 0xff;
861 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
864 void KeyboardAutoRepeatOn(void)
866 #if defined(TARGET_SDL2)
867 keyrepeat_status = TRUE;
869 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
870 SDL_DEFAULT_REPEAT_INTERVAL / 2);
871 SDL_EnableUNICODE(1);
875 void KeyboardAutoRepeatOff(void)
877 #if defined(TARGET_SDL2)
878 keyrepeat_status = FALSE;
880 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
881 SDL_EnableUNICODE(0);
885 boolean SetVideoMode(boolean fullscreen)
887 return SDLSetVideoMode(&backbuffer, fullscreen);
890 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
892 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
893 (!fullscreen && video.fullscreen_enabled))
894 fullscreen = SetVideoMode(fullscreen);
899 Bitmap *LoadImage(char *filename)
903 new_bitmap = SDLLoadImage(filename);
906 new_bitmap->source_filename = getStringCopy(filename);
911 Bitmap *LoadCustomImage(char *basename)
913 char *filename = getCustomImageFilename(basename);
916 if (filename == NULL)
917 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
919 if ((new_bitmap = LoadImage(filename)) == NULL)
920 Error(ERR_EXIT, "LoadImage('%s') failed: %s", basename, GetError());
925 void ReloadCustomImage(Bitmap *bitmap, char *basename)
927 char *filename = getCustomImageFilename(basename);
930 if (filename == NULL) /* (should never happen) */
932 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
936 if (strEqual(filename, bitmap->source_filename))
938 /* The old and new image are the same (have the same filename and path).
939 This usually means that this image does not exist in this graphic set
940 and a fallback to the existing image is done. */
945 if ((new_bitmap = LoadImage(filename)) == NULL)
947 Error(ERR_WARN, "LoadImage('%s') failed: %s", basename, GetError());
951 if (bitmap->width != new_bitmap->width ||
952 bitmap->height != new_bitmap->height)
954 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
956 FreeBitmap(new_bitmap);
960 TransferBitmapPointers(new_bitmap, bitmap);
964 Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
966 Bitmap *dst_bitmap = SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
971 static void SetMaskedBitmapSurface(Bitmap *bitmap)
976 SDL_Surface *surface = bitmap->surface;
978 if (bitmap->surface_masked)
979 SDL_FreeSurface(bitmap->surface_masked);
981 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
982 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
984 if ((bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
985 Error(ERR_EXIT, "SDL_DisplayFormat() failed");
987 SDL_SetColorKey(surface, UNSET_TRANSPARENT_PIXEL, 0);
990 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
992 if (bitmaps[IMG_BITMAP_CUSTOM])
994 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
996 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
999 if (gfx.game_tile_size == gfx.standard_tile_size)
1001 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1006 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
1007 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
1008 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
1010 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
1012 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
1013 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
1015 SetMaskedBitmapSurface(bitmap_new);
1018 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1019 int tile_size, boolean create_small_bitmaps)
1021 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1022 Bitmap *tmp_bitmap_final = NULL;
1023 Bitmap *tmp_bitmap_0 = NULL;
1024 Bitmap *tmp_bitmap_1 = NULL;
1025 Bitmap *tmp_bitmap_2 = NULL;
1026 Bitmap *tmp_bitmap_4 = NULL;
1027 Bitmap *tmp_bitmap_8 = NULL;
1028 Bitmap *tmp_bitmap_16 = NULL;
1029 Bitmap *tmp_bitmap_32 = NULL;
1030 int width_final, height_final;
1031 int width_0, height_0;
1032 int width_1, height_1;
1033 int width_2, height_2;
1034 int width_4, height_4;
1035 int width_8, height_8;
1036 int width_16, height_16;
1037 int width_32, height_32;
1038 int old_width, old_height;
1041 print_timestamp_init("CreateScaledBitmaps");
1043 old_width = old_bitmap->width;
1044 old_height = old_bitmap->height;
1046 /* calculate new image dimensions for final image size */
1047 width_final = old_width * zoom_factor;
1048 height_final = old_height * zoom_factor;
1050 /* get image with final size (this might require scaling up) */
1051 /* ("final" size may result in non-standard tile size image) */
1052 if (zoom_factor != 1)
1053 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1055 tmp_bitmap_final = old_bitmap;
1057 UPDATE_BUSY_STATE();
1059 width_0 = width_1 = width_final;
1060 height_0 = height_1 = height_final;
1062 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1064 if (create_small_bitmaps)
1066 /* check if we have a non-gameplay tile size image */
1067 if (tile_size != gfx.game_tile_size)
1069 /* get image with gameplay tile size */
1070 width_0 = width_final * gfx.game_tile_size / tile_size;
1071 height_0 = height_final * gfx.game_tile_size / tile_size;
1073 if (width_0 == old_width)
1074 tmp_bitmap_0 = old_bitmap;
1075 else if (width_0 == width_final)
1076 tmp_bitmap_0 = tmp_bitmap_final;
1078 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1080 UPDATE_BUSY_STATE();
1083 /* check if we have a non-standard tile size image */
1084 if (tile_size != gfx.standard_tile_size)
1086 /* get image with standard tile size */
1087 width_1 = width_final * gfx.standard_tile_size / tile_size;
1088 height_1 = height_final * gfx.standard_tile_size / tile_size;
1090 if (width_1 == old_width)
1091 tmp_bitmap_1 = old_bitmap;
1092 else if (width_1 == width_final)
1093 tmp_bitmap_1 = tmp_bitmap_final;
1094 else if (width_1 == width_0)
1095 tmp_bitmap_1 = tmp_bitmap_0;
1097 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1099 UPDATE_BUSY_STATE();
1102 /* calculate new image dimensions for small images */
1103 width_2 = width_1 / 2;
1104 height_2 = height_1 / 2;
1105 width_4 = width_1 / 4;
1106 height_4 = height_1 / 4;
1107 width_8 = width_1 / 8;
1108 height_8 = height_1 / 8;
1109 width_16 = width_1 / 16;
1110 height_16 = height_1 / 16;
1111 width_32 = width_1 / 32;
1112 height_32 = height_1 / 32;
1114 /* get image with 1/2 of normal size (for use in the level editor) */
1115 if (width_2 == old_width)
1116 tmp_bitmap_2 = old_bitmap;
1118 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1120 UPDATE_BUSY_STATE();
1122 /* get image with 1/4 of normal size (for use in the level editor) */
1123 if (width_4 == old_width)
1124 tmp_bitmap_4 = old_bitmap;
1126 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1128 UPDATE_BUSY_STATE();
1130 /* get image with 1/8 of normal size (for use on the preview screen) */
1131 if (width_8 == old_width)
1132 tmp_bitmap_8 = old_bitmap;
1134 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1136 UPDATE_BUSY_STATE();
1138 /* get image with 1/16 of normal size (for use on the preview screen) */
1139 if (width_16 == old_width)
1140 tmp_bitmap_16 = old_bitmap;
1142 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1144 UPDATE_BUSY_STATE();
1146 /* get image with 1/32 of normal size (for use on the preview screen) */
1147 if (width_32 == old_width)
1148 tmp_bitmap_32 = old_bitmap;
1150 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1152 UPDATE_BUSY_STATE();
1154 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1155 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1156 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1157 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1158 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1159 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1161 if (width_0 != width_1)
1162 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1164 if (bitmaps[IMG_BITMAP_CUSTOM])
1165 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1167 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1169 boolean free_old_bitmap = TRUE;
1171 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1172 if (bitmaps[i] == old_bitmap)
1173 free_old_bitmap = FALSE;
1175 if (free_old_bitmap)
1176 FreeBitmap(old_bitmap);
1180 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1183 // create corresponding bitmaps for masked blitting
1184 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1185 if (bitmaps[i] != NULL &&
1186 bitmaps[i] != old_bitmap)
1187 SetMaskedBitmapSurface(bitmaps[i]);
1189 UPDATE_BUSY_STATE();
1191 print_timestamp_done("CreateScaledBitmaps");
1194 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1197 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1200 void CreateBitmapTextures(Bitmap **bitmaps)
1202 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1205 void FreeBitmapTextures(Bitmap **bitmaps)
1207 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1210 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1212 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1216 /* ------------------------------------------------------------------------- */
1217 /* mouse pointer functions */
1218 /* ------------------------------------------------------------------------- */
1220 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1222 /* XPM image definitions */
1223 static const char *cursor_image_none[] =
1225 /* width height num_colors chars_per_pixel */
1255 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1256 static const char *cursor_image_dot[] =
1258 /* width height num_colors chars_per_pixel */
1287 static const char **cursor_image_playfield = cursor_image_dot;
1289 /* some people complained about a "white dot" on the screen and thought it
1290 was a graphical error... OK, let's just remove the whole pointer :-) */
1291 static const char **cursor_image_playfield = cursor_image_none;
1294 static const int cursor_bit_order = BIT_ORDER_MSB;
1296 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1298 struct MouseCursorInfo *cursor;
1299 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1300 int header_lines = 4;
1303 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1305 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1308 for (y = 0; y < cursor->width; y++)
1310 for (x = 0; x < cursor->height; x++)
1313 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1318 cursor->data[i] = cursor->mask[i] = 0;
1321 switch (image[header_lines + y][x])
1324 cursor->data[i] |= bit_mask;
1325 cursor->mask[i] |= bit_mask;
1329 cursor->mask[i] |= bit_mask;
1338 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1343 void SetMouseCursor(int mode)
1345 static struct MouseCursorInfo *cursor_none = NULL;
1346 static struct MouseCursorInfo *cursor_playfield = NULL;
1347 struct MouseCursorInfo *cursor_new;
1349 if (cursor_none == NULL)
1350 cursor_none = get_cursor_from_image(cursor_image_none);
1352 if (cursor_playfield == NULL)
1353 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1355 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1356 mode == CURSOR_NONE ? cursor_none :
1357 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1359 SDLSetMouseCursor(cursor_new);
1361 gfx.cursor_mode = mode;
1365 /* ========================================================================= */
1366 /* audio functions */
1367 /* ========================================================================= */
1369 void OpenAudio(void)
1371 /* always start with reliable default values */
1372 audio.sound_available = FALSE;
1373 audio.music_available = FALSE;
1374 audio.loops_available = FALSE;
1376 audio.sound_enabled = FALSE;
1377 audio.sound_deactivated = FALSE;
1379 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1380 audio.mixer_pid = 0;
1381 audio.device_name = NULL;
1382 audio.device_fd = -1;
1384 audio.num_channels = 0;
1385 audio.music_channel = 0;
1386 audio.first_sound_channel = 0;
1391 void CloseAudio(void)
1395 audio.sound_enabled = FALSE;
1398 void SetAudioMode(boolean enabled)
1400 if (!audio.sound_available)
1403 audio.sound_enabled = enabled;
1407 /* ========================================================================= */
1408 /* event functions */
1409 /* ========================================================================= */
1411 void InitEventFilter(EventFilter filter_function)
1413 /* set event filter to filter out certain events */
1414 #if defined(TARGET_SDL2)
1415 SDL_SetEventFilter(filter_function, NULL);
1417 SDL_SetEventFilter(filter_function);
1421 boolean PendingEvent(void)
1423 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1426 void NextEvent(Event *event)
1428 SDLNextEvent(event);
1431 void PeekEvent(Event *event)
1433 #if defined(TARGET_SDL2)
1434 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1436 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1440 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1442 #if defined(TARGET_SDL2)
1443 /* key up/down events in SDL2 do not return text characters anymore */
1444 return event->keysym.sym;
1447 #if ENABLE_UNUSED_CODE
1448 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1449 (int)event->keysym.unicode,
1450 (int)event->keysym.sym,
1451 (int)SDL_GetModState());
1454 if (with_modifiers &&
1455 event->keysym.unicode > 0x0000 &&
1456 event->keysym.unicode < 0x2000)
1457 return event->keysym.unicode;
1459 return event->keysym.sym;
1464 KeyMod HandleKeyModState(Key key, int key_status)
1466 static KeyMod current_modifiers = KMOD_None;
1468 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1470 KeyMod new_modifier = KMOD_None;
1475 new_modifier = KMOD_Shift_L;
1478 new_modifier = KMOD_Shift_R;
1480 case KSYM_Control_L:
1481 new_modifier = KMOD_Control_L;
1483 case KSYM_Control_R:
1484 new_modifier = KMOD_Control_R;
1487 new_modifier = KMOD_Meta_L;
1490 new_modifier = KMOD_Meta_R;
1493 new_modifier = KMOD_Alt_L;
1496 new_modifier = KMOD_Alt_R;
1502 if (key_status == KEY_PRESSED)
1503 current_modifiers |= new_modifier;
1505 current_modifiers &= ~new_modifier;
1508 return current_modifiers;
1511 KeyMod GetKeyModState()
1513 return (KeyMod)SDL_GetModState();
1516 KeyMod GetKeyModStateFromEvents()
1518 /* always use key modifier state as tracked from key events (this is needed
1519 if the modifier key event was injected into the event queue, but the key
1520 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1521 query the keys as held pressed on the keyboard) -- this case is currently
1522 only used to filter out clipboard insert events from "True X-Mouse" tool */
1524 return HandleKeyModState(KSYM_UNDEFINED, 0);
1527 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1529 if (event->type != EVENT_CLIENTMESSAGE)
1532 return TRUE; /* the only possible message here is SDL_QUIT */
1536 /* ========================================================================= */
1537 /* joystick functions */
1538 /* ========================================================================= */
1540 void InitJoysticks()
1544 #if defined(NO_JOYSTICK)
1545 return; /* joysticks generally deactivated by compile-time directive */
1548 /* always start with reliable default values */
1549 joystick.status = JOYSTICK_NOT_AVAILABLE;
1550 for (i = 0; i < MAX_PLAYERS; i++)
1551 joystick.fd[i] = -1; /* joystick device closed */
1556 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1558 return SDLReadJoystick(nr, x, y, b1, b2);