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 #if USE_FINAL_SCREEN_BITMAP
213 ReCreateBitmap(&gfx.final_screen_bitmap, win_xsize, win_ysize, DEFAULT_DEPTH);
217 ReCreateBitmap(&gfx.fade_bitmap_source, win_xsize, win_ysize, DEFAULT_DEPTH);
218 ReCreateBitmap(&gfx.fade_bitmap_target, win_xsize, win_ysize, DEFAULT_DEPTH);
219 ReCreateBitmap(&gfx.fade_bitmap_black, win_xsize, win_ysize, DEFAULT_DEPTH);
221 ClearRectangle(gfx.fade_bitmap_black, 0, 0, win_xsize, win_ysize);
224 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
226 /* currently only used by MSDOS code to alloc VRAM buffer, if available */
227 /* 2009-03-24: also (temporarily?) used for overlapping blit workaround */
228 gfx.scrollbuffer_width = scrollbuffer_width;
229 gfx.scrollbuffer_height = scrollbuffer_height;
232 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
234 gfx.clipping_enabled = enabled;
237 gfx.clip_width = width;
238 gfx.clip_height = height;
241 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
243 gfx.draw_busy_anim_function = draw_busy_anim_function;
246 void InitGfxDrawGlobalAnimFunction(void (*draw_global_anim_function)(int))
248 gfx.draw_global_anim_function = draw_global_anim_function;
251 void InitGfxDrawGlobalBorderFunction(void (*draw_global_border_function)(int))
253 gfx.draw_global_border_function = draw_global_border_function;
256 void InitGfxCustomArtworkInfo()
258 gfx.override_level_graphics = FALSE;
259 gfx.override_level_sounds = FALSE;
260 gfx.override_level_music = FALSE;
262 gfx.draw_init_text = TRUE;
265 void InitGfxOtherSettings()
267 gfx.cursor_mode = CURSOR_DEFAULT;
270 void SetDrawDeactivationMask(int draw_deactivation_mask)
272 gfx.draw_deactivation_mask = draw_deactivation_mask;
275 void SetDrawBackgroundMask(int draw_background_mask)
277 gfx.draw_background_mask = draw_background_mask;
280 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
282 if (background_bitmap_tile != NULL)
283 gfx.background_bitmap_mask |= mask;
285 gfx.background_bitmap_mask &= ~mask;
287 if (background_bitmap_tile == NULL) /* empty background requested */
290 if (mask == REDRAW_ALL)
291 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
292 0, 0, video.width, video.height);
293 else if (mask == REDRAW_FIELD)
294 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
295 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
296 else if (mask == REDRAW_DOOR_1)
297 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
298 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
301 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
303 /* remove every mask before setting mask for window */
304 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
305 SetBackgroundBitmap(NULL, 0xffff); /* !!! FIX THIS !!! */
306 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
309 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
311 /* remove window area mask before setting mask for main area */
312 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
313 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
314 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
317 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
319 /* remove window area mask before setting mask for door area */
320 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
321 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
322 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
326 /* ========================================================================= */
327 /* video functions */
328 /* ========================================================================= */
330 inline static int GetRealDepth(int depth)
332 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
335 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
336 int width, int height, Pixel color)
338 SDLFillRectangle(bitmap, x, y, width, height, color);
340 if (bitmap == backbuffer)
341 SetRedrawMaskFromArea(x, y, width, height);
344 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
345 int src_x, int src_y, int width, int height,
346 int dst_x, int dst_y, int mask_mode)
348 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
349 dst_x, dst_y, mask_mode);
351 if (dst_bitmap == backbuffer)
352 SetRedrawMaskFromArea(dst_x, dst_y, width, height);
355 void LimitScreenUpdates(boolean enable)
357 SDLLimitScreenUpdates(enable);
360 void InitVideoDisplay(void)
362 SDLInitVideoDisplay();
365 void CloseVideoDisplay(void)
367 KeyboardAutoRepeatOn();
369 SDL_QuitSubSystem(SDL_INIT_VIDEO);
372 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
375 video.height = height;
376 video.depth = GetRealDepth(depth);
378 video.fullscreen_available = FULLSCREEN_STATUS;
379 video.fullscreen_enabled = FALSE;
381 video.window_scaling_available = WINDOW_SCALING_STATUS;
383 SDLInitVideoBuffer(fullscreen);
385 video.initialized = TRUE;
390 inline static void FreeBitmapPointers(Bitmap *bitmap)
395 SDLFreeBitmapPointers(bitmap);
397 checked_free(bitmap->source_filename);
398 bitmap->source_filename = NULL;
401 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
404 if (src_bitmap == NULL || dst_bitmap == NULL)
407 FreeBitmapPointers(dst_bitmap);
409 *dst_bitmap = *src_bitmap;
412 void FreeBitmap(Bitmap *bitmap)
417 FreeBitmapPointers(bitmap);
422 Bitmap *CreateBitmapStruct(void)
424 return checked_calloc(sizeof(Bitmap));
427 Bitmap *CreateBitmap(int width, int height, int depth)
429 Bitmap *new_bitmap = CreateBitmapStruct();
430 int real_width = MAX(1, width); /* prevent zero bitmap width */
431 int real_height = MAX(1, height); /* prevent zero bitmap height */
432 int real_depth = GetRealDepth(depth);
434 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
436 new_bitmap->width = real_width;
437 new_bitmap->height = real_height;
442 void ReCreateBitmap(Bitmap **bitmap, int width, int height, int depth)
444 Bitmap *new_bitmap = CreateBitmap(width, height, depth);
448 *bitmap = new_bitmap;
452 TransferBitmapPointers(new_bitmap, *bitmap);
457 void CloseWindow(DrawWindow *window)
461 void SetRedrawMaskFromArea(int x, int y, int width, int height)
465 int x2 = x + width - 1;
466 int y2 = y + height - 1;
468 if (width == 0 || height == 0)
471 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
472 redraw_mask |= REDRAW_FIELD;
473 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
474 redraw_mask |= REDRAW_DOOR_1;
475 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
476 redraw_mask |= REDRAW_DOOR_2;
477 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
478 redraw_mask |= REDRAW_DOOR_3;
480 redraw_mask = REDRAW_ALL;
483 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
486 if (draw_mask == REDRAW_NONE)
489 if (draw_mask & REDRAW_ALL)
492 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
495 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
498 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
501 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
507 boolean DrawingDeactivated(int x, int y, int width, int height)
509 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
512 boolean DrawingOnBackground(int x, int y)
514 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
515 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
518 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
519 int *width, int *height, boolean is_dest)
521 int clip_x, clip_y, clip_width, clip_height;
523 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
525 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
526 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
527 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
528 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
534 clip_width = bitmap->width;
535 clip_height = bitmap->height;
538 /* skip if rectangle completely outside bitmap */
540 if (*x + *width <= clip_x ||
541 *y + *height <= clip_y ||
542 *x >= clip_x + clip_width ||
543 *y >= clip_y + clip_height)
546 /* clip if rectangle overlaps bitmap */
550 *width -= clip_x - *x;
553 else if (*x + *width > clip_x + clip_width)
555 *width = clip_x + clip_width - *x;
560 *height -= clip_y - *y;
563 else if (*y + *height > clip_y + clip_height)
565 *height = clip_y + clip_height - *y;
571 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
572 int src_x, int src_y, int width, int height,
573 int dst_x, int dst_y)
575 int dst_x_unclipped = dst_x;
576 int dst_y_unclipped = dst_y;
578 if (src_bitmap == NULL || dst_bitmap == NULL)
581 if (DrawingDeactivated(dst_x, dst_y, width, height))
584 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
585 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
588 /* source x/y might need adjustment if destination x/y was clipped top/left */
589 src_x += dst_x - dst_x_unclipped;
590 src_y += dst_y - dst_y_unclipped;
592 #if defined(TARGET_SDL2)
593 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
594 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
595 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
596 but is already fixed in SVN and should therefore finally be fixed with
597 the next official SDL release, which is probably version 1.2.14.) */
598 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
600 if (src_bitmap == dst_bitmap)
602 /* needed when blitting directly to same bitmap -- should not be needed with
603 recent SDL libraries, but apparently does not work in 1.2.11 directly */
605 static Bitmap *tmp_bitmap = NULL;
606 static int tmp_bitmap_xsize = 0;
607 static int tmp_bitmap_ysize = 0;
609 /* start with largest static bitmaps for initial bitmap size ... */
610 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
612 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
613 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
616 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
617 if (src_bitmap->width > tmp_bitmap_xsize ||
618 src_bitmap->height > tmp_bitmap_ysize)
620 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
621 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
623 FreeBitmap(tmp_bitmap);
628 if (tmp_bitmap == NULL)
629 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
632 sysCopyArea(src_bitmap, tmp_bitmap,
633 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
634 sysCopyArea(tmp_bitmap, dst_bitmap,
635 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
641 sysCopyArea(src_bitmap, dst_bitmap,
642 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
645 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
646 int src_x, int src_y, int src_width, int src_height,
647 int dst_x, int dst_y, int dst_width, int dst_height)
649 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
650 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
651 int dst_xsize = dst_width;
652 int dst_ysize = dst_height;
653 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
654 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
657 for (y = 0; y < src_ysteps; y++)
659 for (x = 0; x < src_xsteps; x++)
661 int draw_x = dst_x + x * src_xsize;
662 int draw_y = dst_y + y * src_ysize;
663 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
664 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
666 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
672 void FadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
673 int fade_mode, int fade_delay, int post_delay,
674 void (*draw_border_function)(void))
676 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
677 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
680 SDLFadeRectangle(bitmap_cross, x, y, width, height,
681 fade_mode, fade_delay, post_delay, draw_border_function);
684 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
687 if (DrawingDeactivated(x, y, width, height))
690 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
693 sysFillRectangle(bitmap, x, y, width, height, color);
696 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
698 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
701 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
702 int width, int height)
704 if (DrawingOnBackground(x, y))
705 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
707 ClearRectangle(bitmap, x, y, width, height);
710 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
711 int src_x, int src_y, int width, int height,
712 int dst_x, int dst_y)
714 if (DrawingDeactivated(dst_x, dst_y, width, height))
717 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
718 dst_x, dst_y, BLIT_MASKED);
721 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
722 int src_x, int src_y, int width, int height,
723 int dst_x, int dst_y)
725 if (DrawingOnBackground(dst_x, dst_y))
727 /* draw background */
728 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
731 /* draw foreground */
732 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
736 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
740 void BlitTexture(Bitmap *bitmap,
741 int src_x, int src_y, int width, int height,
742 int dst_x, int dst_y)
747 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
751 void BlitTextureMasked(Bitmap *bitmap,
752 int src_x, int src_y, int width, int height,
753 int dst_x, int dst_y)
758 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
762 void BlitToScreen(Bitmap *bitmap,
763 int src_x, int src_y, int width, int height,
764 int dst_x, int dst_y)
769 #if USE_FINAL_SCREEN_BITMAP
770 BlitBitmap(bitmap, gfx.final_screen_bitmap, src_x, src_y,
771 width, height, dst_x, dst_y);
773 BlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y);
777 void BlitToScreenMasked(Bitmap *bitmap,
778 int src_x, int src_y, int width, int height,
779 int dst_x, int dst_y)
784 #if USE_FINAL_SCREEN_BITMAP
785 BlitBitmapMasked(bitmap, gfx.final_screen_bitmap, src_x, src_y,
786 width, height, dst_x, dst_y);
788 BlitTextureMasked(bitmap, src_x, src_y, width, height, dst_x, dst_y);
792 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
795 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
798 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
801 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
804 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
805 int to_x, int to_y, Pixel pixel, int line_width)
809 for (x = 0; x < line_width; x++)
811 for (y = 0; y < line_width; y++)
813 int dx = x - line_width / 2;
814 int dy = y - line_width / 2;
816 if ((x == 0 && y == 0) ||
817 (x == 0 && y == line_width - 1) ||
818 (x == line_width - 1 && y == 0) ||
819 (x == line_width - 1 && y == line_width - 1))
823 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
828 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
833 for (i = 0; i < num_points - 1; i++)
834 DrawLine(bitmap, points[i].x, points[i].y,
835 points[i + 1].x, points[i + 1].y, pixel, line_width);
838 SDLDrawLines(bitmap->surface, points, num_points, pixel);
842 Pixel GetPixel(Bitmap *bitmap, int x, int y)
844 if (x < 0 || x >= bitmap->width ||
845 y < 0 || y >= bitmap->height)
848 return SDLGetPixel(bitmap, x, y);
851 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
852 unsigned int color_g, unsigned int color_b)
854 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
857 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
859 unsigned int color_r = (color >> 16) & 0xff;
860 unsigned int color_g = (color >> 8) & 0xff;
861 unsigned int color_b = (color >> 0) & 0xff;
863 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
866 void KeyboardAutoRepeatOn(void)
868 #if defined(TARGET_SDL2)
869 keyrepeat_status = TRUE;
871 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
872 SDL_DEFAULT_REPEAT_INTERVAL / 2);
873 SDL_EnableUNICODE(1);
877 void KeyboardAutoRepeatOff(void)
879 #if defined(TARGET_SDL2)
880 keyrepeat_status = FALSE;
882 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
883 SDL_EnableUNICODE(0);
887 boolean SetVideoMode(boolean fullscreen)
889 return SDLSetVideoMode(fullscreen);
892 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
894 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
895 (!fullscreen && video.fullscreen_enabled))
896 fullscreen = SetVideoMode(fullscreen);
901 Bitmap *LoadImage(char *filename)
905 new_bitmap = SDLLoadImage(filename);
908 new_bitmap->source_filename = getStringCopy(filename);
913 Bitmap *LoadCustomImage(char *basename)
915 char *filename = getCustomImageFilename(basename);
918 if (filename == NULL)
919 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
921 if ((new_bitmap = LoadImage(filename)) == NULL)
922 Error(ERR_EXIT, "LoadImage('%s') failed: %s", basename, GetError());
927 void ReloadCustomImage(Bitmap *bitmap, char *basename)
929 char *filename = getCustomImageFilename(basename);
932 if (filename == NULL) /* (should never happen) */
934 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
938 if (strEqual(filename, bitmap->source_filename))
940 /* The old and new image are the same (have the same filename and path).
941 This usually means that this image does not exist in this graphic set
942 and a fallback to the existing image is done. */
947 if ((new_bitmap = LoadImage(filename)) == NULL)
949 Error(ERR_WARN, "LoadImage('%s') failed: %s", basename, GetError());
953 if (bitmap->width != new_bitmap->width ||
954 bitmap->height != new_bitmap->height)
956 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
958 FreeBitmap(new_bitmap);
962 TransferBitmapPointers(new_bitmap, bitmap);
966 Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
968 Bitmap *dst_bitmap = SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
973 static void SetMaskedBitmapSurface(Bitmap *bitmap)
978 SDL_Surface *surface = bitmap->surface;
980 if (bitmap->surface_masked)
981 SDL_FreeSurface(bitmap->surface_masked);
983 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
984 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
986 if ((bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
987 Error(ERR_EXIT, "SDL_DisplayFormat() failed");
989 SDL_SetColorKey(surface, UNSET_TRANSPARENT_PIXEL, 0);
992 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
994 if (bitmaps[IMG_BITMAP_CUSTOM])
996 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
998 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
1001 if (gfx.game_tile_size == gfx.standard_tile_size)
1003 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1008 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
1009 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
1010 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
1012 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
1014 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
1015 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
1017 SetMaskedBitmapSurface(bitmap_new);
1020 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1021 int tile_size, boolean create_small_bitmaps)
1023 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1024 Bitmap *tmp_bitmap_final = NULL;
1025 Bitmap *tmp_bitmap_0 = NULL;
1026 Bitmap *tmp_bitmap_1 = NULL;
1027 Bitmap *tmp_bitmap_2 = NULL;
1028 Bitmap *tmp_bitmap_4 = NULL;
1029 Bitmap *tmp_bitmap_8 = NULL;
1030 Bitmap *tmp_bitmap_16 = NULL;
1031 Bitmap *tmp_bitmap_32 = NULL;
1032 int width_final, height_final;
1033 int width_0, height_0;
1034 int width_1, height_1;
1035 int width_2, height_2;
1036 int width_4, height_4;
1037 int width_8, height_8;
1038 int width_16, height_16;
1039 int width_32, height_32;
1040 int old_width, old_height;
1043 print_timestamp_init("CreateScaledBitmaps");
1045 old_width = old_bitmap->width;
1046 old_height = old_bitmap->height;
1048 /* calculate new image dimensions for final image size */
1049 width_final = old_width * zoom_factor;
1050 height_final = old_height * zoom_factor;
1052 /* get image with final size (this might require scaling up) */
1053 /* ("final" size may result in non-standard tile size image) */
1054 if (zoom_factor != 1)
1055 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1057 tmp_bitmap_final = old_bitmap;
1059 UPDATE_BUSY_STATE();
1061 width_0 = width_1 = width_final;
1062 height_0 = height_1 = height_final;
1064 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1066 if (create_small_bitmaps)
1068 /* check if we have a non-gameplay tile size image */
1069 if (tile_size != gfx.game_tile_size)
1071 /* get image with gameplay tile size */
1072 width_0 = width_final * gfx.game_tile_size / tile_size;
1073 height_0 = height_final * gfx.game_tile_size / tile_size;
1075 if (width_0 == old_width)
1076 tmp_bitmap_0 = old_bitmap;
1077 else if (width_0 == width_final)
1078 tmp_bitmap_0 = tmp_bitmap_final;
1080 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1082 UPDATE_BUSY_STATE();
1085 /* check if we have a non-standard tile size image */
1086 if (tile_size != gfx.standard_tile_size)
1088 /* get image with standard tile size */
1089 width_1 = width_final * gfx.standard_tile_size / tile_size;
1090 height_1 = height_final * gfx.standard_tile_size / tile_size;
1092 if (width_1 == old_width)
1093 tmp_bitmap_1 = old_bitmap;
1094 else if (width_1 == width_final)
1095 tmp_bitmap_1 = tmp_bitmap_final;
1096 else if (width_1 == width_0)
1097 tmp_bitmap_1 = tmp_bitmap_0;
1099 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1101 UPDATE_BUSY_STATE();
1104 /* calculate new image dimensions for small images */
1105 width_2 = width_1 / 2;
1106 height_2 = height_1 / 2;
1107 width_4 = width_1 / 4;
1108 height_4 = height_1 / 4;
1109 width_8 = width_1 / 8;
1110 height_8 = height_1 / 8;
1111 width_16 = width_1 / 16;
1112 height_16 = height_1 / 16;
1113 width_32 = width_1 / 32;
1114 height_32 = height_1 / 32;
1116 /* get image with 1/2 of normal size (for use in the level editor) */
1117 if (width_2 == old_width)
1118 tmp_bitmap_2 = old_bitmap;
1120 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1122 UPDATE_BUSY_STATE();
1124 /* get image with 1/4 of normal size (for use in the level editor) */
1125 if (width_4 == old_width)
1126 tmp_bitmap_4 = old_bitmap;
1128 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1130 UPDATE_BUSY_STATE();
1132 /* get image with 1/8 of normal size (for use on the preview screen) */
1133 if (width_8 == old_width)
1134 tmp_bitmap_8 = old_bitmap;
1136 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1138 UPDATE_BUSY_STATE();
1140 /* get image with 1/16 of normal size (for use on the preview screen) */
1141 if (width_16 == old_width)
1142 tmp_bitmap_16 = old_bitmap;
1144 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1146 UPDATE_BUSY_STATE();
1148 /* get image with 1/32 of normal size (for use on the preview screen) */
1149 if (width_32 == old_width)
1150 tmp_bitmap_32 = old_bitmap;
1152 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1154 UPDATE_BUSY_STATE();
1156 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1157 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1158 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1159 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1160 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1161 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1163 if (width_0 != width_1)
1164 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1166 if (bitmaps[IMG_BITMAP_CUSTOM])
1167 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1169 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1171 boolean free_old_bitmap = TRUE;
1173 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1174 if (bitmaps[i] == old_bitmap)
1175 free_old_bitmap = FALSE;
1177 if (free_old_bitmap)
1178 FreeBitmap(old_bitmap);
1182 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1185 // create corresponding bitmaps for masked blitting
1186 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1187 if (bitmaps[i] != NULL &&
1188 bitmaps[i] != old_bitmap)
1189 SetMaskedBitmapSurface(bitmaps[i]);
1191 UPDATE_BUSY_STATE();
1193 print_timestamp_done("CreateScaledBitmaps");
1196 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1199 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1202 void CreateBitmapTextures(Bitmap **bitmaps)
1204 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1207 void FreeBitmapTextures(Bitmap **bitmaps)
1209 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1212 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1214 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1218 /* ------------------------------------------------------------------------- */
1219 /* mouse pointer functions */
1220 /* ------------------------------------------------------------------------- */
1222 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1224 /* XPM image definitions */
1225 static const char *cursor_image_none[] =
1227 /* width height num_colors chars_per_pixel */
1257 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1258 static const char *cursor_image_dot[] =
1260 /* width height num_colors chars_per_pixel */
1289 static const char **cursor_image_playfield = cursor_image_dot;
1291 /* some people complained about a "white dot" on the screen and thought it
1292 was a graphical error... OK, let's just remove the whole pointer :-) */
1293 static const char **cursor_image_playfield = cursor_image_none;
1296 static const int cursor_bit_order = BIT_ORDER_MSB;
1298 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1300 struct MouseCursorInfo *cursor;
1301 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1302 int header_lines = 4;
1305 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1307 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1310 for (y = 0; y < cursor->width; y++)
1312 for (x = 0; x < cursor->height; x++)
1315 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1320 cursor->data[i] = cursor->mask[i] = 0;
1323 switch (image[header_lines + y][x])
1326 cursor->data[i] |= bit_mask;
1327 cursor->mask[i] |= bit_mask;
1331 cursor->mask[i] |= bit_mask;
1340 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1345 void SetMouseCursor(int mode)
1347 static struct MouseCursorInfo *cursor_none = NULL;
1348 static struct MouseCursorInfo *cursor_playfield = NULL;
1349 struct MouseCursorInfo *cursor_new;
1351 if (cursor_none == NULL)
1352 cursor_none = get_cursor_from_image(cursor_image_none);
1354 if (cursor_playfield == NULL)
1355 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1357 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1358 mode == CURSOR_NONE ? cursor_none :
1359 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1361 SDLSetMouseCursor(cursor_new);
1363 gfx.cursor_mode = mode;
1367 /* ========================================================================= */
1368 /* audio functions */
1369 /* ========================================================================= */
1371 void OpenAudio(void)
1373 /* always start with reliable default values */
1374 audio.sound_available = FALSE;
1375 audio.music_available = FALSE;
1376 audio.loops_available = FALSE;
1378 audio.sound_enabled = FALSE;
1379 audio.sound_deactivated = FALSE;
1381 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1382 audio.mixer_pid = 0;
1383 audio.device_name = NULL;
1384 audio.device_fd = -1;
1386 audio.num_channels = 0;
1387 audio.music_channel = 0;
1388 audio.first_sound_channel = 0;
1393 void CloseAudio(void)
1397 audio.sound_enabled = FALSE;
1400 void SetAudioMode(boolean enabled)
1402 if (!audio.sound_available)
1405 audio.sound_enabled = enabled;
1409 /* ========================================================================= */
1410 /* event functions */
1411 /* ========================================================================= */
1413 void InitEventFilter(EventFilter filter_function)
1415 /* set event filter to filter out certain events */
1416 #if defined(TARGET_SDL2)
1417 SDL_SetEventFilter(filter_function, NULL);
1419 SDL_SetEventFilter(filter_function);
1423 boolean PendingEvent(void)
1425 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1428 void NextEvent(Event *event)
1430 SDLNextEvent(event);
1433 void PeekEvent(Event *event)
1435 #if defined(TARGET_SDL2)
1436 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1438 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1442 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1444 #if defined(TARGET_SDL2)
1445 /* key up/down events in SDL2 do not return text characters anymore */
1446 return event->keysym.sym;
1449 #if ENABLE_UNUSED_CODE
1450 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1451 (int)event->keysym.unicode,
1452 (int)event->keysym.sym,
1453 (int)SDL_GetModState());
1456 if (with_modifiers &&
1457 event->keysym.unicode > 0x0000 &&
1458 event->keysym.unicode < 0x2000)
1459 return event->keysym.unicode;
1461 return event->keysym.sym;
1466 KeyMod HandleKeyModState(Key key, int key_status)
1468 static KeyMod current_modifiers = KMOD_None;
1470 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1472 KeyMod new_modifier = KMOD_None;
1477 new_modifier = KMOD_Shift_L;
1480 new_modifier = KMOD_Shift_R;
1482 case KSYM_Control_L:
1483 new_modifier = KMOD_Control_L;
1485 case KSYM_Control_R:
1486 new_modifier = KMOD_Control_R;
1489 new_modifier = KMOD_Meta_L;
1492 new_modifier = KMOD_Meta_R;
1495 new_modifier = KMOD_Alt_L;
1498 new_modifier = KMOD_Alt_R;
1504 if (key_status == KEY_PRESSED)
1505 current_modifiers |= new_modifier;
1507 current_modifiers &= ~new_modifier;
1510 return current_modifiers;
1513 KeyMod GetKeyModState()
1515 return (KeyMod)SDL_GetModState();
1518 KeyMod GetKeyModStateFromEvents()
1520 /* always use key modifier state as tracked from key events (this is needed
1521 if the modifier key event was injected into the event queue, but the key
1522 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1523 query the keys as held pressed on the keyboard) -- this case is currently
1524 only used to filter out clipboard insert events from "True X-Mouse" tool */
1526 return HandleKeyModState(KSYM_UNDEFINED, 0);
1529 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1531 if (event->type != EVENT_CLIENTMESSAGE)
1534 return TRUE; /* the only possible message here is SDL_QUIT */
1538 /* ========================================================================= */
1539 /* joystick functions */
1540 /* ========================================================================= */
1542 void InitJoysticks()
1546 #if defined(NO_JOYSTICK)
1547 return; /* joysticks generally deactivated by compile-time directive */
1550 /* always start with reliable default values */
1551 joystick.status = JOYSTICK_NOT_AVAILABLE;
1552 for (i = 0; i < MAX_PLAYERS; i++)
1553 joystick.fd[i] = -1; /* joystick device closed */
1558 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1560 return SDLReadJoystick(nr, x, y, b1, b2);