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 OverlayInfo overlay;
37 struct ArtworkInfo artwork;
38 struct JoystickInfo joystick;
39 struct SetupInfo setup;
41 LevelDirTree *leveldir_first_all = NULL;
42 LevelDirTree *leveldir_first = NULL;
43 LevelDirTree *leveldir_current = NULL;
46 struct LevelStats level_stats[MAX_LEVELS];
48 DrawWindow *window = NULL;
49 DrawBuffer *backbuffer = NULL;
50 DrawBuffer *drawto = NULL;
52 int button_status = MB_NOT_PRESSED;
53 boolean motion_status = FALSE;
54 int wheel_steps = DEFAULT_WHEEL_STEPS;
55 #if defined(TARGET_SDL2)
56 boolean keyrepeat_status = TRUE;
59 int redraw_mask = REDRAW_NONE;
64 /* ========================================================================= */
65 /* init/close functions */
66 /* ========================================================================= */
68 void InitProgramInfo(char *argv0, char *config_filename, char *userdata_subdir,
69 char *program_title, char *icon_title,
70 char *icon_filename, char *cookie_prefix,
73 program.command_basepath = getBasePath(argv0);
74 program.command_basename = getBaseName(argv0);
76 program.config_filename = config_filename;
78 program.userdata_subdir = userdata_subdir;
79 program.userdata_path = getUserGameDataDir();
81 program.program_title = program_title;
82 program.window_title = "(undefined)";
83 program.icon_title = icon_title;
85 program.icon_filename = icon_filename;
87 program.cookie_prefix = cookie_prefix;
89 program.version_major = VERSION_MAJOR(program_version);
90 program.version_minor = VERSION_MINOR(program_version);
91 program.version_patch = VERSION_PATCH(program_version);
92 program.version_build = VERSION_BUILD(program_version);
93 program.version_ident = program_version;
95 program.log_filename[LOG_OUT_ID] = getLogFilename(LOG_OUT_BASENAME);
96 program.log_filename[LOG_ERR_ID] = getLogFilename(LOG_ERR_BASENAME);
97 program.log_file[LOG_OUT_ID] = program.log_file_default[LOG_OUT_ID] = stdout;
98 program.log_file[LOG_ERR_ID] = program.log_file_default[LOG_ERR_ID] = stderr;
101 void SetWindowTitle()
103 program.window_title = program.window_title_function();
108 void InitWindowTitleFunction(char *(*window_title_function)(void))
110 program.window_title_function = window_title_function;
113 void InitExitMessageFunction(void (*exit_message_function)(char *, va_list))
115 program.exit_message_function = exit_message_function;
118 void InitExitFunction(void (*exit_function)(int))
120 program.exit_function = exit_function;
122 /* set signal handlers to custom exit function */
123 // signal(SIGINT, exit_function);
124 signal(SIGTERM, exit_function);
126 /* set exit function to automatically cleanup SDL stuff after exit() */
130 void InitPlatformDependentStuff(void)
132 // this is initialized in GetOptions(), but may already be used before
133 options.verbose = TRUE;
137 #if defined(TARGET_SDL2)
138 int sdl_init_flags = SDL_INIT_EVENTS | SDL_INIT_NOPARACHUTE;
140 int sdl_init_flags = SDL_INIT_EVENTTHREAD | SDL_INIT_NOPARACHUTE;
143 if (SDL_Init(sdl_init_flags) < 0)
144 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
149 void ClosePlatformDependentStuff(void)
154 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
155 int real_sx, int real_sy,
156 int full_sxsize, int full_sysize,
157 Bitmap *field_save_buffer)
163 gfx.real_sx = real_sx;
164 gfx.real_sy = real_sy;
165 gfx.full_sxsize = full_sxsize;
166 gfx.full_sysize = full_sysize;
168 gfx.field_save_buffer = field_save_buffer;
170 SetDrawDeactivationMask(REDRAW_NONE); /* do not deactivate drawing */
171 SetDrawBackgroundMask(REDRAW_NONE); /* deactivate masked drawing */
174 void InitGfxTileSizeInfo(int game_tile_size, int standard_tile_size)
176 gfx.game_tile_size = game_tile_size;
177 gfx.standard_tile_size = standard_tile_size;
180 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
188 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
196 void InitGfxDoor3Info(int ex, int ey, int exsize, int eysize)
204 void InitGfxWindowInfo(int win_xsize, int win_ysize)
206 if (win_xsize != gfx.win_xsize || win_ysize != gfx.win_ysize)
208 ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize);
210 #if defined(TARGET_SDL2)
211 ReCreateBitmap(&gfx.final_screen_bitmap, win_xsize, win_ysize);
214 ReCreateBitmap(&gfx.fade_bitmap_backup, win_xsize, win_ysize);
215 ReCreateBitmap(&gfx.fade_bitmap_source, win_xsize, win_ysize);
216 ReCreateBitmap(&gfx.fade_bitmap_target, win_xsize, win_ysize);
217 ReCreateBitmap(&gfx.fade_bitmap_black, win_xsize, win_ysize);
219 ClearRectangle(gfx.fade_bitmap_black, 0, 0, win_xsize, win_ysize);
222 gfx.win_xsize = win_xsize;
223 gfx.win_ysize = win_ysize;
225 gfx.background_bitmap_mask = REDRAW_NONE;
228 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
230 /* currently only used by MSDOS code to alloc VRAM buffer, if available */
231 /* 2009-03-24: also (temporarily?) used for overlapping blit workaround */
232 gfx.scrollbuffer_width = scrollbuffer_width;
233 gfx.scrollbuffer_height = scrollbuffer_height;
236 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
238 gfx.clipping_enabled = enabled;
241 gfx.clip_width = width;
242 gfx.clip_height = height;
245 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
247 gfx.draw_busy_anim_function = draw_busy_anim_function;
250 void InitGfxDrawGlobalAnimFunction(void (*draw_global_anim_function)(int, int))
252 gfx.draw_global_anim_function = draw_global_anim_function;
255 void InitGfxDrawGlobalBorderFunction(void (*draw_global_border_function)(int))
257 gfx.draw_global_border_function = draw_global_border_function;
260 void InitGfxCustomArtworkInfo()
262 gfx.override_level_graphics = FALSE;
263 gfx.override_level_sounds = FALSE;
264 gfx.override_level_music = FALSE;
266 gfx.draw_init_text = TRUE;
269 void InitGfxOtherSettings()
271 gfx.cursor_mode = CURSOR_DEFAULT;
274 void InitOverlayInfo()
276 overlay.active = FALSE;
279 void SetOverlayActive(boolean active)
281 overlay.active = active;
284 boolean GetOverlayActive()
286 return overlay.active;
289 void SetDrawDeactivationMask(int draw_deactivation_mask)
291 gfx.draw_deactivation_mask = draw_deactivation_mask;
294 void SetDrawBackgroundMask(int draw_background_mask)
296 gfx.draw_background_mask = draw_background_mask;
299 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
301 if (background_bitmap_tile != NULL)
302 gfx.background_bitmap_mask |= mask;
304 gfx.background_bitmap_mask &= ~mask;
306 if (background_bitmap_tile == NULL) /* empty background requested */
309 if (mask == REDRAW_ALL)
310 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
311 0, 0, video.width, video.height);
312 else if (mask == REDRAW_FIELD)
313 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
314 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
315 else if (mask == REDRAW_DOOR_1)
316 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
317 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
320 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
322 /* remove every mask before setting mask for window */
323 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
324 SetBackgroundBitmap(NULL, 0xffff); /* !!! FIX THIS !!! */
325 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
328 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
330 /* remove window area mask before setting mask for main area */
331 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
332 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
333 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
336 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
338 /* remove window area mask before setting mask for door area */
339 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
340 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
341 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
345 /* ========================================================================= */
346 /* video functions */
347 /* ========================================================================= */
349 inline static int GetRealDepth(int depth)
351 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
354 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
355 int width, int height, Pixel color)
357 SDLFillRectangle(bitmap, x, y, width, height, color);
359 if (bitmap == backbuffer)
360 SetRedrawMaskFromArea(x, y, width, height);
363 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
364 int src_x, int src_y, int width, int height,
365 int dst_x, int dst_y, int mask_mode)
367 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
368 dst_x, dst_y, mask_mode);
370 if (dst_bitmap == backbuffer)
371 SetRedrawMaskFromArea(dst_x, dst_y, width, height);
374 void LimitScreenUpdates(boolean enable)
376 SDLLimitScreenUpdates(enable);
379 void InitVideoDisplay(void)
381 SDLInitVideoDisplay();
382 #if defined(TARGET_SDL2)
387 void CloseVideoDisplay(void)
389 KeyboardAutoRepeatOn();
391 SDL_QuitSubSystem(SDL_INIT_VIDEO);
394 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
397 video.height = height;
398 video.depth = GetRealDepth(depth);
400 video.screen_width = width;
401 video.screen_height = height;
402 video.screen_xoffset = 0;
403 video.screen_yoffset = 0;
405 video.fullscreen_available = FULLSCREEN_STATUS;
406 video.fullscreen_enabled = FALSE;
408 video.window_scaling_available = WINDOW_SCALING_STATUS;
410 video.frame_delay = 0;
411 video.frame_delay_value = GAME_FRAME_DELAY;
413 video.shifted_up = FALSE;
414 video.shifted_up_pos = 0;
415 video.shifted_up_pos_last = 0;
416 video.shifted_up_delay = 0;
417 video.shifted_up_delay_value = ONE_SECOND_DELAY / 4;
419 SDLInitVideoBuffer(fullscreen);
421 video.initialized = TRUE;
426 inline static void FreeBitmapPointers(Bitmap *bitmap)
431 SDLFreeBitmapPointers(bitmap);
433 checked_free(bitmap->source_filename);
434 bitmap->source_filename = NULL;
437 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
440 if (src_bitmap == NULL || dst_bitmap == NULL)
443 FreeBitmapPointers(dst_bitmap);
445 *dst_bitmap = *src_bitmap;
448 void FreeBitmap(Bitmap *bitmap)
453 FreeBitmapPointers(bitmap);
458 Bitmap *CreateBitmapStruct(void)
460 return checked_calloc(sizeof(Bitmap));
463 Bitmap *CreateBitmap(int width, int height, int depth)
465 Bitmap *new_bitmap = CreateBitmapStruct();
466 int real_width = MAX(1, width); /* prevent zero bitmap width */
467 int real_height = MAX(1, height); /* prevent zero bitmap height */
468 int real_depth = GetRealDepth(depth);
470 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
472 new_bitmap->width = real_width;
473 new_bitmap->height = real_height;
478 void ReCreateBitmap(Bitmap **bitmap, int width, int height)
480 Bitmap *new_bitmap = CreateBitmap(width, height, DEFAULT_DEPTH);
484 *bitmap = new_bitmap;
488 TransferBitmapPointers(new_bitmap, *bitmap);
493 void CloseWindow(DrawWindow *window)
497 void SetRedrawMaskFromArea(int x, int y, int width, int height)
501 int x2 = x + width - 1;
502 int y2 = y + height - 1;
504 if (width == 0 || height == 0)
507 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
508 redraw_mask |= REDRAW_FIELD;
509 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
510 redraw_mask |= REDRAW_DOOR_1;
511 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
512 redraw_mask |= REDRAW_DOOR_2;
513 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
514 redraw_mask |= REDRAW_DOOR_3;
516 redraw_mask = REDRAW_ALL;
519 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
522 if (draw_mask == REDRAW_NONE)
525 if (draw_mask & REDRAW_ALL)
528 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
531 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
534 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
537 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
543 boolean DrawingDeactivated(int x, int y, int width, int height)
545 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
548 boolean DrawingOnBackground(int x, int y)
550 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
551 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
554 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
555 int *width, int *height, boolean is_dest)
557 int clip_x, clip_y, clip_width, clip_height;
559 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
561 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
562 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
563 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
564 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
570 clip_width = bitmap->width;
571 clip_height = bitmap->height;
574 /* skip if rectangle completely outside bitmap */
576 if (*x + *width <= clip_x ||
577 *y + *height <= clip_y ||
578 *x >= clip_x + clip_width ||
579 *y >= clip_y + clip_height)
582 /* clip if rectangle overlaps bitmap */
586 *width -= clip_x - *x;
589 else if (*x + *width > clip_x + clip_width)
591 *width = clip_x + clip_width - *x;
596 *height -= clip_y - *y;
599 else if (*y + *height > clip_y + clip_height)
601 *height = clip_y + clip_height - *y;
607 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
608 int src_x, int src_y, int width, int height,
609 int dst_x, int dst_y)
611 int dst_x_unclipped = dst_x;
612 int dst_y_unclipped = dst_y;
614 if (src_bitmap == NULL || dst_bitmap == NULL)
617 if (DrawingDeactivated(dst_x, dst_y, width, height))
620 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
621 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
624 /* source x/y might need adjustment if destination x/y was clipped top/left */
625 src_x += dst_x - dst_x_unclipped;
626 src_y += dst_y - dst_y_unclipped;
628 #if defined(TARGET_SDL2)
629 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
630 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
631 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
632 but is already fixed in SVN and should therefore finally be fixed with
633 the next official SDL release, which is probably version 1.2.14.) */
634 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
636 if (src_bitmap == dst_bitmap)
638 /* needed when blitting directly to same bitmap -- should not be needed with
639 recent SDL libraries, but apparently does not work in 1.2.11 directly */
641 static Bitmap *tmp_bitmap = NULL;
642 static int tmp_bitmap_xsize = 0;
643 static int tmp_bitmap_ysize = 0;
645 /* start with largest static bitmaps for initial bitmap size ... */
646 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
648 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
649 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
652 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
653 if (src_bitmap->width > tmp_bitmap_xsize ||
654 src_bitmap->height > tmp_bitmap_ysize)
656 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
657 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
659 FreeBitmap(tmp_bitmap);
664 if (tmp_bitmap == NULL)
665 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
668 sysCopyArea(src_bitmap, tmp_bitmap,
669 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
670 sysCopyArea(tmp_bitmap, dst_bitmap,
671 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
677 sysCopyArea(src_bitmap, dst_bitmap,
678 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
681 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
682 int src_x, int src_y, int src_width, int src_height,
683 int dst_x, int dst_y, int dst_width, int dst_height)
685 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
686 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
687 int dst_xsize = dst_width;
688 int dst_ysize = dst_height;
689 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
690 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
693 for (y = 0; y < src_ysteps; y++)
695 for (x = 0; x < src_xsteps; x++)
697 int draw_x = dst_x + x * src_xsize;
698 int draw_y = dst_y + y * src_ysize;
699 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
700 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
702 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
708 void FadeRectangle(int x, int y, int width, int height,
709 int fade_mode, int fade_delay, int post_delay,
710 void (*draw_border_function)(void))
712 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
713 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
716 SDLFadeRectangle(x, y, width, height,
717 fade_mode, fade_delay, post_delay, draw_border_function);
720 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
723 if (DrawingDeactivated(x, y, width, height))
726 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
729 sysFillRectangle(bitmap, x, y, width, height, color);
732 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
734 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
737 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
738 int width, int height)
740 if (DrawingOnBackground(x, y))
741 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
743 ClearRectangle(bitmap, x, y, width, height);
746 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
747 int src_x, int src_y, int width, int height,
748 int dst_x, int dst_y)
750 if (DrawingDeactivated(dst_x, dst_y, width, height))
753 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
754 dst_x, dst_y, BLIT_MASKED);
757 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
758 int src_x, int src_y, int width, int height,
759 int dst_x, int dst_y)
761 if (DrawingOnBackground(dst_x, dst_y))
763 /* draw background */
764 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
767 /* draw foreground */
768 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
772 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
776 void BlitTexture(Bitmap *bitmap,
777 int src_x, int src_y, int width, int height,
778 int dst_x, int dst_y)
783 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
787 void BlitTextureMasked(Bitmap *bitmap,
788 int src_x, int src_y, int width, int height,
789 int dst_x, int dst_y)
794 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
798 void BlitToScreen(Bitmap *bitmap,
799 int src_x, int src_y, int width, int height,
800 int dst_x, int dst_y)
805 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
806 BlitBitmap(bitmap, gfx.final_screen_bitmap, src_x, src_y,
807 width, height, dst_x, dst_y);
809 BlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y);
812 void BlitToScreenMasked(Bitmap *bitmap,
813 int src_x, int src_y, int width, int height,
814 int dst_x, int dst_y)
819 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
820 BlitBitmapMasked(bitmap, gfx.final_screen_bitmap, src_x, src_y,
821 width, height, dst_x, dst_y);
823 BlitTextureMasked(bitmap, src_x, src_y, width, height, dst_x, dst_y);
826 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
829 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
832 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
835 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
838 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
839 int to_x, int to_y, Pixel pixel, int line_width)
843 for (x = 0; x < line_width; x++)
845 for (y = 0; y < line_width; y++)
847 int dx = x - line_width / 2;
848 int dy = y - line_width / 2;
850 if ((x == 0 && y == 0) ||
851 (x == 0 && y == line_width - 1) ||
852 (x == line_width - 1 && y == 0) ||
853 (x == line_width - 1 && y == line_width - 1))
857 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
862 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
867 for (i = 0; i < num_points - 1; i++)
868 DrawLine(bitmap, points[i].x, points[i].y,
869 points[i + 1].x, points[i + 1].y, pixel, line_width);
872 SDLDrawLines(bitmap->surface, points, num_points, pixel);
876 Pixel GetPixel(Bitmap *bitmap, int x, int y)
878 if (x < 0 || x >= bitmap->width ||
879 y < 0 || y >= bitmap->height)
882 return SDLGetPixel(bitmap, x, y);
885 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
886 unsigned int color_g, unsigned int color_b)
888 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
891 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
893 unsigned int color_r = (color >> 16) & 0xff;
894 unsigned int color_g = (color >> 8) & 0xff;
895 unsigned int color_b = (color >> 0) & 0xff;
897 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
900 void KeyboardAutoRepeatOn(void)
902 #if defined(TARGET_SDL2)
903 keyrepeat_status = TRUE;
905 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
906 SDL_DEFAULT_REPEAT_INTERVAL / 2);
907 SDL_EnableUNICODE(1);
911 void KeyboardAutoRepeatOff(void)
913 #if defined(TARGET_SDL2)
914 keyrepeat_status = FALSE;
916 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
917 SDL_EnableUNICODE(0);
921 boolean SetVideoMode(boolean fullscreen)
923 return SDLSetVideoMode(fullscreen);
926 void SetVideoFrameDelay(unsigned int frame_delay_value)
928 video.frame_delay_value = frame_delay_value;
931 unsigned int GetVideoFrameDelay()
933 return video.frame_delay_value;
936 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
938 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
939 (!fullscreen && video.fullscreen_enabled))
940 fullscreen = SetVideoMode(fullscreen);
945 Bitmap *LoadImage(char *filename)
949 new_bitmap = SDLLoadImage(filename);
952 new_bitmap->source_filename = getStringCopy(filename);
957 Bitmap *LoadCustomImage(char *basename)
959 char *filename = getCustomImageFilename(basename);
962 if (filename == NULL)
963 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
965 if ((new_bitmap = LoadImage(filename)) == NULL)
966 Error(ERR_EXIT, "LoadImage('%s') failed: %s", basename, GetError());
971 void ReloadCustomImage(Bitmap *bitmap, char *basename)
973 char *filename = getCustomImageFilename(basename);
976 if (filename == NULL) /* (should never happen) */
978 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
982 if (strEqual(filename, bitmap->source_filename))
984 /* The old and new image are the same (have the same filename and path).
985 This usually means that this image does not exist in this graphic set
986 and a fallback to the existing image is done. */
991 if ((new_bitmap = LoadImage(filename)) == NULL)
993 Error(ERR_WARN, "LoadImage('%s') failed: %s", basename, GetError());
997 if (bitmap->width != new_bitmap->width ||
998 bitmap->height != new_bitmap->height)
1000 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
1002 FreeBitmap(new_bitmap);
1006 TransferBitmapPointers(new_bitmap, bitmap);
1010 static Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
1012 return SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
1015 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
1017 if (bitmaps[IMG_BITMAP_CUSTOM])
1019 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
1021 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
1024 if (gfx.game_tile_size == gfx.standard_tile_size)
1026 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1031 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
1032 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
1033 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
1035 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
1037 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
1038 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
1041 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1042 int tile_size, boolean create_small_bitmaps)
1044 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1045 Bitmap *tmp_bitmap_final = NULL;
1046 Bitmap *tmp_bitmap_0 = NULL;
1047 Bitmap *tmp_bitmap_1 = NULL;
1048 Bitmap *tmp_bitmap_2 = NULL;
1049 Bitmap *tmp_bitmap_4 = NULL;
1050 Bitmap *tmp_bitmap_8 = NULL;
1051 Bitmap *tmp_bitmap_16 = NULL;
1052 Bitmap *tmp_bitmap_32 = NULL;
1053 int width_final, height_final;
1054 int width_0, height_0;
1055 int width_1, height_1;
1056 int width_2, height_2;
1057 int width_4, height_4;
1058 int width_8, height_8;
1059 int width_16, height_16;
1060 int width_32, height_32;
1061 int old_width, old_height;
1064 print_timestamp_init("CreateScaledBitmaps");
1066 old_width = old_bitmap->width;
1067 old_height = old_bitmap->height;
1069 /* calculate new image dimensions for final image size */
1070 width_final = old_width * zoom_factor;
1071 height_final = old_height * zoom_factor;
1073 /* get image with final size (this might require scaling up) */
1074 /* ("final" size may result in non-standard tile size image) */
1075 if (zoom_factor != 1)
1076 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1078 tmp_bitmap_final = old_bitmap;
1080 UPDATE_BUSY_STATE();
1082 width_0 = width_1 = width_final;
1083 height_0 = height_1 = height_final;
1085 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1087 if (create_small_bitmaps)
1089 /* check if we have a non-gameplay tile size image */
1090 if (tile_size != gfx.game_tile_size)
1092 /* get image with gameplay tile size */
1093 width_0 = width_final * gfx.game_tile_size / tile_size;
1094 height_0 = height_final * gfx.game_tile_size / tile_size;
1096 if (width_0 == old_width)
1097 tmp_bitmap_0 = old_bitmap;
1098 else if (width_0 == width_final)
1099 tmp_bitmap_0 = tmp_bitmap_final;
1101 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1103 UPDATE_BUSY_STATE();
1106 /* check if we have a non-standard tile size image */
1107 if (tile_size != gfx.standard_tile_size)
1109 /* get image with standard tile size */
1110 width_1 = width_final * gfx.standard_tile_size / tile_size;
1111 height_1 = height_final * gfx.standard_tile_size / tile_size;
1113 if (width_1 == old_width)
1114 tmp_bitmap_1 = old_bitmap;
1115 else if (width_1 == width_final)
1116 tmp_bitmap_1 = tmp_bitmap_final;
1117 else if (width_1 == width_0)
1118 tmp_bitmap_1 = tmp_bitmap_0;
1120 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1122 UPDATE_BUSY_STATE();
1125 /* calculate new image dimensions for small images */
1126 width_2 = width_1 / 2;
1127 height_2 = height_1 / 2;
1128 width_4 = width_1 / 4;
1129 height_4 = height_1 / 4;
1130 width_8 = width_1 / 8;
1131 height_8 = height_1 / 8;
1132 width_16 = width_1 / 16;
1133 height_16 = height_1 / 16;
1134 width_32 = width_1 / 32;
1135 height_32 = height_1 / 32;
1137 /* get image with 1/2 of normal size (for use in the level editor) */
1138 if (width_2 == old_width)
1139 tmp_bitmap_2 = old_bitmap;
1141 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1143 UPDATE_BUSY_STATE();
1145 /* get image with 1/4 of normal size (for use in the level editor) */
1146 if (width_4 == old_width)
1147 tmp_bitmap_4 = old_bitmap;
1149 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1151 UPDATE_BUSY_STATE();
1153 /* get image with 1/8 of normal size (for use on the preview screen) */
1154 if (width_8 == old_width)
1155 tmp_bitmap_8 = old_bitmap;
1157 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1159 UPDATE_BUSY_STATE();
1161 /* get image with 1/16 of normal size (for use on the preview screen) */
1162 if (width_16 == old_width)
1163 tmp_bitmap_16 = old_bitmap;
1165 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1167 UPDATE_BUSY_STATE();
1169 /* get image with 1/32 of normal size (for use on the preview screen) */
1170 if (width_32 == old_width)
1171 tmp_bitmap_32 = old_bitmap;
1173 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1175 UPDATE_BUSY_STATE();
1177 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1178 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1179 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1180 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1181 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1182 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1184 if (width_0 != width_1)
1185 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1187 if (bitmaps[IMG_BITMAP_CUSTOM])
1188 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1190 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1192 boolean free_old_bitmap = TRUE;
1194 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1195 if (bitmaps[i] == old_bitmap)
1196 free_old_bitmap = FALSE;
1198 if (free_old_bitmap)
1199 FreeBitmap(old_bitmap);
1203 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1206 UPDATE_BUSY_STATE();
1208 print_timestamp_done("CreateScaledBitmaps");
1211 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1214 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1217 void CreateBitmapTextures(Bitmap **bitmaps)
1219 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1222 void FreeBitmapTextures(Bitmap **bitmaps)
1224 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1227 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1229 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1233 /* ------------------------------------------------------------------------- */
1234 /* mouse pointer functions */
1235 /* ------------------------------------------------------------------------- */
1237 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1239 /* XPM image definitions */
1240 static const char *cursor_image_none[] =
1242 /* width height num_colors chars_per_pixel */
1272 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1273 static const char *cursor_image_dot[] =
1275 /* width height num_colors chars_per_pixel */
1304 static const char **cursor_image_playfield = cursor_image_dot;
1306 /* some people complained about a "white dot" on the screen and thought it
1307 was a graphical error... OK, let's just remove the whole pointer :-) */
1308 static const char **cursor_image_playfield = cursor_image_none;
1311 static const int cursor_bit_order = BIT_ORDER_MSB;
1313 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1315 struct MouseCursorInfo *cursor;
1316 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1317 int header_lines = 4;
1320 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1322 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1325 for (y = 0; y < cursor->width; y++)
1327 for (x = 0; x < cursor->height; x++)
1330 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1335 cursor->data[i] = cursor->mask[i] = 0;
1338 switch (image[header_lines + y][x])
1341 cursor->data[i] |= bit_mask;
1342 cursor->mask[i] |= bit_mask;
1346 cursor->mask[i] |= bit_mask;
1355 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1360 void SetMouseCursor(int mode)
1362 static struct MouseCursorInfo *cursor_none = NULL;
1363 static struct MouseCursorInfo *cursor_playfield = NULL;
1364 struct MouseCursorInfo *cursor_new;
1366 if (cursor_none == NULL)
1367 cursor_none = get_cursor_from_image(cursor_image_none);
1369 if (cursor_playfield == NULL)
1370 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1372 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1373 mode == CURSOR_NONE ? cursor_none :
1374 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1376 SDLSetMouseCursor(cursor_new);
1378 gfx.cursor_mode = mode;
1382 /* ========================================================================= */
1383 /* audio functions */
1384 /* ========================================================================= */
1386 void OpenAudio(void)
1388 /* always start with reliable default values */
1389 audio.sound_available = FALSE;
1390 audio.music_available = FALSE;
1391 audio.loops_available = FALSE;
1393 audio.sound_enabled = FALSE;
1394 audio.sound_deactivated = FALSE;
1396 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1397 audio.mixer_pid = 0;
1398 audio.device_name = NULL;
1399 audio.device_fd = -1;
1401 audio.num_channels = 0;
1402 audio.music_channel = 0;
1403 audio.first_sound_channel = 0;
1408 void CloseAudio(void)
1412 audio.sound_enabled = FALSE;
1415 void SetAudioMode(boolean enabled)
1417 if (!audio.sound_available)
1420 audio.sound_enabled = enabled;
1424 /* ========================================================================= */
1425 /* event functions */
1426 /* ========================================================================= */
1428 boolean PendingEvent(void)
1430 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1433 void NextEvent(Event *event)
1435 SDLNextEvent(event);
1438 void PeekEvent(Event *event)
1440 #if defined(TARGET_SDL2)
1441 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1443 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1447 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1449 #if defined(TARGET_SDL2)
1450 /* key up/down events in SDL2 do not return text characters anymore */
1451 return event->keysym.sym;
1454 #if ENABLE_UNUSED_CODE
1455 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1456 (int)event->keysym.unicode,
1457 (int)event->keysym.sym,
1458 (int)SDL_GetModState());
1461 if (with_modifiers &&
1462 event->keysym.unicode > 0x0000 &&
1463 event->keysym.unicode < 0x2000)
1464 return event->keysym.unicode;
1466 return event->keysym.sym;
1471 KeyMod HandleKeyModState(Key key, int key_status)
1473 static KeyMod current_modifiers = KMOD_None;
1475 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1477 KeyMod new_modifier = KMOD_None;
1482 new_modifier = KMOD_Shift_L;
1485 new_modifier = KMOD_Shift_R;
1487 case KSYM_Control_L:
1488 new_modifier = KMOD_Control_L;
1490 case KSYM_Control_R:
1491 new_modifier = KMOD_Control_R;
1494 new_modifier = KMOD_Meta_L;
1497 new_modifier = KMOD_Meta_R;
1500 new_modifier = KMOD_Alt_L;
1503 new_modifier = KMOD_Alt_R;
1509 if (key_status == KEY_PRESSED)
1510 current_modifiers |= new_modifier;
1512 current_modifiers &= ~new_modifier;
1515 return current_modifiers;
1518 KeyMod GetKeyModState()
1520 return (KeyMod)SDL_GetModState();
1523 KeyMod GetKeyModStateFromEvents()
1525 /* always use key modifier state as tracked from key events (this is needed
1526 if the modifier key event was injected into the event queue, but the key
1527 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1528 query the keys as held pressed on the keyboard) -- this case is currently
1529 only used to filter out clipboard insert events from "True X-Mouse" tool */
1531 return HandleKeyModState(KSYM_UNDEFINED, 0);
1534 void StartTextInput(int x, int y, int width, int height)
1536 #if defined(TARGET_SDL2)
1537 SDL_StartTextInput();
1539 #if defined(HAS_SCREEN_KEYBOARD)
1540 if (y + height > SCREEN_KEYBOARD_POS(video.height))
1542 video.shifted_up_pos = y + height - SCREEN_KEYBOARD_POS(video.height);
1543 video.shifted_up_delay = SDL_GetTicks();
1544 video.shifted_up = TRUE;
1550 void StopTextInput()
1552 #if defined(TARGET_SDL2)
1553 SDL_StopTextInput();
1555 #if defined(HAS_SCREEN_KEYBOARD)
1556 if (video.shifted_up)
1558 video.shifted_up_pos = 0;
1559 video.shifted_up_delay = SDL_GetTicks();
1560 video.shifted_up = FALSE;
1566 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1568 if (event->type != EVENT_CLIENTMESSAGE)
1571 return TRUE; /* the only possible message here is SDL_QUIT */
1575 /* ========================================================================= */
1576 /* joystick functions */
1577 /* ========================================================================= */
1579 void InitJoysticks()
1583 #if defined(NO_JOYSTICK)
1584 return; /* joysticks generally deactivated by compile-time directive */
1587 /* always start with reliable default values */
1588 joystick.status = JOYSTICK_NOT_AVAILABLE;
1589 for (i = 0; i < MAX_PLAYERS; i++)
1590 joystick.fd[i] = -1; /* joystick device closed */
1595 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1597 return SDLReadJoystick(nr, x, y, b1, b2);