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,
71 char *program_version_string, int program_version)
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.version_string = program_version_string;
97 program.log_filename[LOG_OUT_ID] = getLogFilename(LOG_OUT_BASENAME);
98 program.log_filename[LOG_ERR_ID] = getLogFilename(LOG_ERR_BASENAME);
99 program.log_file[LOG_OUT_ID] = program.log_file_default[LOG_OUT_ID] = stdout;
100 program.log_file[LOG_ERR_ID] = program.log_file_default[LOG_ERR_ID] = stderr;
102 program.headless = FALSE;
105 void InitScoresInfo()
107 char *global_scores_dir = getPath2(getCommonDataDir(), SCORES_DIRECTORY);
109 program.global_scores = directoryExists(global_scores_dir);
110 program.many_scores_per_name = !program.global_scores;
114 if (program.global_scores)
116 Error(ERR_DEBUG, "Using global, multi-user scores directory '%s'.",
118 Error(ERR_DEBUG, "Remove to enable single-user scores directory.");
119 Error(ERR_DEBUG, "(This enables multipe score entries per user.)");
123 Error(ERR_DEBUG, "Using private, single-user scores directory.");
127 free(global_scores_dir);
130 void SetWindowTitle()
132 program.window_title = program.window_title_function();
137 void InitWindowTitleFunction(char *(*window_title_function)(void))
139 program.window_title_function = window_title_function;
142 void InitExitMessageFunction(void (*exit_message_function)(char *, va_list))
144 program.exit_message_function = exit_message_function;
147 void InitExitFunction(void (*exit_function)(int))
149 program.exit_function = exit_function;
151 /* set signal handlers to custom exit function */
152 // signal(SIGINT, exit_function);
153 signal(SIGTERM, exit_function);
155 /* set exit function to automatically cleanup SDL stuff after exit() */
159 void InitPlatformDependentStuff(void)
161 // this is initialized in GetOptions(), but may already be used before
162 options.verbose = TRUE;
166 #if defined(TARGET_SDL2)
167 int sdl_init_flags = SDL_INIT_EVENTS | SDL_INIT_NOPARACHUTE;
169 int sdl_init_flags = SDL_INIT_EVENTTHREAD | SDL_INIT_NOPARACHUTE;
172 if (SDL_Init(sdl_init_flags) < 0)
173 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
178 void ClosePlatformDependentStuff(void)
183 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
184 int real_sx, int real_sy,
185 int full_sxsize, int full_sysize,
186 Bitmap *field_save_buffer)
192 gfx.real_sx = real_sx;
193 gfx.real_sy = real_sy;
194 gfx.full_sxsize = full_sxsize;
195 gfx.full_sysize = full_sysize;
197 gfx.field_save_buffer = field_save_buffer;
199 SetDrawDeactivationMask(REDRAW_NONE); /* do not deactivate drawing */
200 SetDrawBackgroundMask(REDRAW_NONE); /* deactivate masked drawing */
203 void InitGfxTileSizeInfo(int game_tile_size, int standard_tile_size)
205 gfx.game_tile_size = game_tile_size;
206 gfx.standard_tile_size = standard_tile_size;
209 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
217 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
225 void InitGfxDoor3Info(int ex, int ey, int exsize, int eysize)
233 void InitGfxWindowInfo(int win_xsize, int win_ysize)
235 if (win_xsize != gfx.win_xsize || win_ysize != gfx.win_ysize)
237 ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize);
239 #if defined(TARGET_SDL2)
240 ReCreateBitmap(&gfx.final_screen_bitmap, win_xsize, win_ysize);
243 ReCreateBitmap(&gfx.fade_bitmap_backup, win_xsize, win_ysize);
244 ReCreateBitmap(&gfx.fade_bitmap_source, win_xsize, win_ysize);
245 ReCreateBitmap(&gfx.fade_bitmap_target, win_xsize, win_ysize);
246 ReCreateBitmap(&gfx.fade_bitmap_black, win_xsize, win_ysize);
248 ClearRectangle(gfx.fade_bitmap_black, 0, 0, win_xsize, win_ysize);
251 gfx.win_xsize = win_xsize;
252 gfx.win_ysize = win_ysize;
254 gfx.background_bitmap_mask = REDRAW_NONE;
257 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
259 /* currently only used by MSDOS code to alloc VRAM buffer, if available */
260 /* 2009-03-24: also (temporarily?) used for overlapping blit workaround */
261 gfx.scrollbuffer_width = scrollbuffer_width;
262 gfx.scrollbuffer_height = scrollbuffer_height;
265 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
267 gfx.clipping_enabled = enabled;
270 gfx.clip_width = width;
271 gfx.clip_height = height;
274 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
276 gfx.draw_busy_anim_function = draw_busy_anim_function;
279 void InitGfxDrawGlobalAnimFunction(void (*draw_global_anim_function)(int, int))
281 gfx.draw_global_anim_function = draw_global_anim_function;
284 void InitGfxDrawGlobalBorderFunction(void (*draw_global_border_function)(int))
286 gfx.draw_global_border_function = draw_global_border_function;
289 void InitGfxCustomArtworkInfo()
291 gfx.override_level_graphics = FALSE;
292 gfx.override_level_sounds = FALSE;
293 gfx.override_level_music = FALSE;
295 gfx.draw_init_text = TRUE;
298 void InitGfxOtherSettings()
300 gfx.cursor_mode = CURSOR_DEFAULT;
303 void InitOverlayInfo()
305 overlay.enabled = FALSE;
306 overlay.active = FALSE;
308 #if defined(PLATFORM_ANDROID)
309 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
310 overlay.enabled = TRUE;
314 void SetOverlayEnabled(boolean enabled)
316 overlay.enabled = enabled;
319 void SetOverlayActive(boolean active)
321 overlay.active = active;
324 boolean GetOverlayActive()
326 return overlay.active;
329 void SetDrawDeactivationMask(int draw_deactivation_mask)
331 gfx.draw_deactivation_mask = draw_deactivation_mask;
334 int GetDrawDeactivationMask()
336 return gfx.draw_deactivation_mask;
339 void SetDrawBackgroundMask(int draw_background_mask)
341 gfx.draw_background_mask = draw_background_mask;
344 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
346 if (background_bitmap_tile != NULL)
347 gfx.background_bitmap_mask |= mask;
349 gfx.background_bitmap_mask &= ~mask;
351 if (background_bitmap_tile == NULL) /* empty background requested */
354 if (mask == REDRAW_ALL)
355 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
356 0, 0, video.width, video.height);
357 else if (mask == REDRAW_FIELD)
358 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
359 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
360 else if (mask == REDRAW_DOOR_1)
361 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
362 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
365 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
367 /* remove every mask before setting mask for window */
368 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
369 SetBackgroundBitmap(NULL, 0xffff); /* !!! FIX THIS !!! */
370 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
373 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
375 /* remove window area mask before setting mask for main area */
376 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
377 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
378 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
381 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
383 /* remove window area mask before setting mask for door area */
384 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
385 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
386 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
390 /* ========================================================================= */
391 /* video functions */
392 /* ========================================================================= */
394 inline static int GetRealDepth(int depth)
396 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
399 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
400 int width, int height, Pixel color)
402 SDLFillRectangle(bitmap, x, y, width, height, color);
404 if (bitmap == backbuffer)
405 SetRedrawMaskFromArea(x, y, width, height);
408 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
409 int src_x, int src_y, int width, int height,
410 int dst_x, int dst_y, int mask_mode)
412 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
413 dst_x, dst_y, mask_mode);
415 if (dst_bitmap == backbuffer)
416 SetRedrawMaskFromArea(dst_x, dst_y, width, height);
419 void LimitScreenUpdates(boolean enable)
421 SDLLimitScreenUpdates(enable);
424 void InitVideoDefaults(void)
426 video.default_depth = 32;
429 void InitVideoDisplay(void)
431 if (program.headless)
434 SDLInitVideoDisplay();
435 #if defined(TARGET_SDL2)
440 void CloseVideoDisplay(void)
442 KeyboardAutoRepeatOn();
444 SDL_QuitSubSystem(SDL_INIT_VIDEO);
447 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
450 video.height = height;
451 video.depth = GetRealDepth(depth);
453 video.screen_width = width;
454 video.screen_height = height;
455 video.screen_xoffset = 0;
456 video.screen_yoffset = 0;
458 video.fullscreen_available = FULLSCREEN_STATUS;
459 video.fullscreen_enabled = FALSE;
461 video.window_scaling_available = WINDOW_SCALING_STATUS;
463 video.frame_delay = 0;
464 video.frame_delay_value = GAME_FRAME_DELAY;
466 video.shifted_up = FALSE;
467 video.shifted_up_pos = 0;
468 video.shifted_up_pos_last = 0;
469 video.shifted_up_delay = 0;
470 video.shifted_up_delay_value = ONE_SECOND_DELAY / 4;
472 SDLInitVideoBuffer(fullscreen);
474 video.initialized = !program.headless;
479 inline static void FreeBitmapPointers(Bitmap *bitmap)
484 SDLFreeBitmapPointers(bitmap);
486 checked_free(bitmap->source_filename);
487 bitmap->source_filename = NULL;
490 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
493 if (src_bitmap == NULL || dst_bitmap == NULL)
496 FreeBitmapPointers(dst_bitmap);
498 *dst_bitmap = *src_bitmap;
501 void FreeBitmap(Bitmap *bitmap)
506 FreeBitmapPointers(bitmap);
511 Bitmap *CreateBitmapStruct(void)
513 return checked_calloc(sizeof(Bitmap));
516 Bitmap *CreateBitmap(int width, int height, int depth)
518 Bitmap *new_bitmap = CreateBitmapStruct();
519 int real_width = MAX(1, width); /* prevent zero bitmap width */
520 int real_height = MAX(1, height); /* prevent zero bitmap height */
521 int real_depth = GetRealDepth(depth);
523 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
525 new_bitmap->width = real_width;
526 new_bitmap->height = real_height;
531 void ReCreateBitmap(Bitmap **bitmap, int width, int height)
535 /* if new bitmap size fits into old one, no need to re-create it */
536 if (width <= (*bitmap)->width &&
537 height <= (*bitmap)->height)
540 /* else adjust size so that old and new bitmap size fit into it */
541 width = MAX(width, (*bitmap)->width);
542 height = MAX(height, (*bitmap)->height);
545 Bitmap *new_bitmap = CreateBitmap(width, height, DEFAULT_DEPTH);
549 *bitmap = new_bitmap;
553 TransferBitmapPointers(new_bitmap, *bitmap);
558 void CloseWindow(DrawWindow *window)
562 void SetRedrawMaskFromArea(int x, int y, int width, int height)
566 int x2 = x + width - 1;
567 int y2 = y + height - 1;
569 if (width == 0 || height == 0)
572 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
573 redraw_mask |= REDRAW_FIELD;
574 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
575 redraw_mask |= REDRAW_DOOR_1;
576 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
577 redraw_mask |= REDRAW_DOOR_2;
578 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
579 redraw_mask |= REDRAW_DOOR_3;
581 redraw_mask = REDRAW_ALL;
584 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
587 if (draw_mask == REDRAW_NONE)
590 if (draw_mask & REDRAW_ALL)
593 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
596 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
599 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
602 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
608 boolean DrawingDeactivatedField()
610 if (program.headless)
613 if (gfx.draw_deactivation_mask & REDRAW_FIELD)
619 boolean DrawingDeactivated(int x, int y, int width, int height)
621 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
624 boolean DrawingOnBackground(int x, int y)
626 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
627 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
630 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
631 int *width, int *height, boolean is_dest)
633 int clip_x, clip_y, clip_width, clip_height;
635 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
637 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
638 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
639 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
640 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
646 clip_width = bitmap->width;
647 clip_height = bitmap->height;
650 /* skip if rectangle completely outside bitmap */
652 if (*x + *width <= clip_x ||
653 *y + *height <= clip_y ||
654 *x >= clip_x + clip_width ||
655 *y >= clip_y + clip_height)
658 /* clip if rectangle overlaps bitmap */
662 *width -= clip_x - *x;
665 else if (*x + *width > clip_x + clip_width)
667 *width = clip_x + clip_width - *x;
672 *height -= clip_y - *y;
675 else if (*y + *height > clip_y + clip_height)
677 *height = clip_y + clip_height - *y;
683 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
684 int src_x, int src_y, int width, int height,
685 int dst_x, int dst_y)
687 int dst_x_unclipped = dst_x;
688 int dst_y_unclipped = dst_y;
690 if (program.headless)
693 if (src_bitmap == NULL || dst_bitmap == NULL)
696 if (DrawingDeactivated(dst_x, dst_y, width, height))
699 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
700 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
703 /* source x/y might need adjustment if destination x/y was clipped top/left */
704 src_x += dst_x - dst_x_unclipped;
705 src_y += dst_y - dst_y_unclipped;
707 #if defined(TARGET_SDL2)
708 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
709 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
710 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
711 but is already fixed in SVN and should therefore finally be fixed with
712 the next official SDL release, which is probably version 1.2.14.) */
713 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
715 if (src_bitmap == dst_bitmap)
717 /* needed when blitting directly to same bitmap -- should not be needed with
718 recent SDL libraries, but apparently does not work in 1.2.11 directly */
720 static Bitmap *tmp_bitmap = NULL;
721 static int tmp_bitmap_xsize = 0;
722 static int tmp_bitmap_ysize = 0;
724 /* start with largest static bitmaps for initial bitmap size ... */
725 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
727 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
728 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
731 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
732 if (src_bitmap->width > tmp_bitmap_xsize ||
733 src_bitmap->height > tmp_bitmap_ysize)
735 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
736 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
738 FreeBitmap(tmp_bitmap);
743 if (tmp_bitmap == NULL)
744 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
747 sysCopyArea(src_bitmap, tmp_bitmap,
748 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
749 sysCopyArea(tmp_bitmap, dst_bitmap,
750 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
756 sysCopyArea(src_bitmap, dst_bitmap,
757 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
760 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
761 int src_x, int src_y, int src_width, int src_height,
762 int dst_x, int dst_y, int dst_width, int dst_height)
764 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
765 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
766 int dst_xsize = dst_width;
767 int dst_ysize = dst_height;
768 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
769 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
772 for (y = 0; y < src_ysteps; y++)
774 for (x = 0; x < src_xsteps; x++)
776 int draw_x = dst_x + x * src_xsize;
777 int draw_y = dst_y + y * src_ysize;
778 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
779 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
781 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
787 void FadeRectangle(int x, int y, int width, int height,
788 int fade_mode, int fade_delay, int post_delay,
789 void (*draw_border_function)(void))
791 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
792 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
795 SDLFadeRectangle(x, y, width, height,
796 fade_mode, fade_delay, post_delay, draw_border_function);
799 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
802 if (DrawingDeactivated(x, y, width, height))
805 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
808 sysFillRectangle(bitmap, x, y, width, height, color);
811 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
813 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
816 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
817 int width, int height)
819 if (DrawingOnBackground(x, y))
820 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
822 ClearRectangle(bitmap, x, y, width, height);
825 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
826 int src_x, int src_y, int width, int height,
827 int dst_x, int dst_y)
829 if (DrawingDeactivated(dst_x, dst_y, width, height))
832 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
833 dst_x, dst_y, BLIT_MASKED);
836 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
837 int src_x, int src_y, int width, int height,
838 int dst_x, int dst_y)
840 if (DrawingOnBackground(dst_x, dst_y))
842 /* draw background */
843 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
846 /* draw foreground */
847 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
851 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
855 void BlitTexture(Bitmap *bitmap,
856 int src_x, int src_y, int width, int height,
857 int dst_x, int dst_y)
862 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
866 void BlitTextureMasked(Bitmap *bitmap,
867 int src_x, int src_y, int width, int height,
868 int dst_x, int dst_y)
873 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
877 void BlitToScreen(Bitmap *bitmap,
878 int src_x, int src_y, int width, int height,
879 int dst_x, int dst_y)
884 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
885 BlitBitmap(bitmap, gfx.final_screen_bitmap, src_x, src_y,
886 width, height, dst_x, dst_y);
888 BlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y);
891 void BlitToScreenMasked(Bitmap *bitmap,
892 int src_x, int src_y, int width, int height,
893 int dst_x, int dst_y)
898 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
899 BlitBitmapMasked(bitmap, gfx.final_screen_bitmap, src_x, src_y,
900 width, height, dst_x, dst_y);
902 BlitTextureMasked(bitmap, src_x, src_y, width, height, dst_x, dst_y);
905 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
908 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
911 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
914 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
917 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
918 int to_x, int to_y, Pixel pixel, int line_width)
922 if (program.headless)
925 for (x = 0; x < line_width; x++)
927 for (y = 0; y < line_width; y++)
929 int dx = x - line_width / 2;
930 int dy = y - line_width / 2;
932 if ((x == 0 && y == 0) ||
933 (x == 0 && y == line_width - 1) ||
934 (x == line_width - 1 && y == 0) ||
935 (x == line_width - 1 && y == line_width - 1))
939 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
944 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
949 for (i = 0; i < num_points - 1; i++)
950 DrawLine(bitmap, points[i].x, points[i].y,
951 points[i + 1].x, points[i + 1].y, pixel, line_width);
954 SDLDrawLines(bitmap->surface, points, num_points, pixel);
958 Pixel GetPixel(Bitmap *bitmap, int x, int y)
960 if (program.headless)
963 if (x < 0 || x >= bitmap->width ||
964 y < 0 || y >= bitmap->height)
967 return SDLGetPixel(bitmap, x, y);
970 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
971 unsigned int color_g, unsigned int color_b)
973 if (program.headless)
976 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
979 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
981 unsigned int color_r = (color >> 16) & 0xff;
982 unsigned int color_g = (color >> 8) & 0xff;
983 unsigned int color_b = (color >> 0) & 0xff;
985 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
988 void KeyboardAutoRepeatOn(void)
990 #if defined(TARGET_SDL2)
991 keyrepeat_status = TRUE;
993 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
994 SDL_DEFAULT_REPEAT_INTERVAL / 2);
995 SDL_EnableUNICODE(1);
999 void KeyboardAutoRepeatOff(void)
1001 #if defined(TARGET_SDL2)
1002 keyrepeat_status = FALSE;
1004 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
1005 SDL_EnableUNICODE(0);
1009 boolean SetVideoMode(boolean fullscreen)
1011 return SDLSetVideoMode(fullscreen);
1014 void SetVideoFrameDelay(unsigned int frame_delay_value)
1016 video.frame_delay_value = frame_delay_value;
1019 unsigned int GetVideoFrameDelay()
1021 return video.frame_delay_value;
1024 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
1026 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
1027 (!fullscreen && video.fullscreen_enabled))
1028 fullscreen = SetVideoMode(fullscreen);
1033 Bitmap *LoadImage(char *filename)
1037 new_bitmap = SDLLoadImage(filename);
1040 new_bitmap->source_filename = getStringCopy(filename);
1045 Bitmap *LoadCustomImage(char *basename)
1047 char *filename = getCustomImageFilename(basename);
1050 if (filename == NULL)
1051 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
1053 if ((new_bitmap = LoadImage(filename)) == NULL)
1054 Error(ERR_EXIT, "LoadImage('%s') failed: %s", basename, GetError());
1059 void ReloadCustomImage(Bitmap *bitmap, char *basename)
1061 char *filename = getCustomImageFilename(basename);
1064 if (filename == NULL) /* (should never happen) */
1066 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
1070 if (strEqual(filename, bitmap->source_filename))
1072 /* The old and new image are the same (have the same filename and path).
1073 This usually means that this image does not exist in this graphic set
1074 and a fallback to the existing image is done. */
1079 if ((new_bitmap = LoadImage(filename)) == NULL)
1081 Error(ERR_WARN, "LoadImage('%s') failed: %s", basename, GetError());
1085 if (bitmap->width != new_bitmap->width ||
1086 bitmap->height != new_bitmap->height)
1088 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
1090 FreeBitmap(new_bitmap);
1094 TransferBitmapPointers(new_bitmap, bitmap);
1098 static Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
1100 return SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
1103 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
1105 if (bitmaps[IMG_BITMAP_CUSTOM])
1107 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
1109 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
1112 if (gfx.game_tile_size == gfx.standard_tile_size)
1114 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1119 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
1120 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
1121 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
1123 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
1125 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
1126 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
1129 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1130 int tile_size, boolean create_small_bitmaps)
1132 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1133 Bitmap *tmp_bitmap_final = NULL;
1134 Bitmap *tmp_bitmap_0 = NULL;
1135 Bitmap *tmp_bitmap_1 = NULL;
1136 Bitmap *tmp_bitmap_2 = NULL;
1137 Bitmap *tmp_bitmap_4 = NULL;
1138 Bitmap *tmp_bitmap_8 = NULL;
1139 Bitmap *tmp_bitmap_16 = NULL;
1140 Bitmap *tmp_bitmap_32 = NULL;
1141 int width_final, height_final;
1142 int width_0, height_0;
1143 int width_1, height_1;
1144 int width_2, height_2;
1145 int width_4, height_4;
1146 int width_8, height_8;
1147 int width_16, height_16;
1148 int width_32, height_32;
1149 int old_width, old_height;
1152 print_timestamp_init("CreateScaledBitmaps");
1154 old_width = old_bitmap->width;
1155 old_height = old_bitmap->height;
1157 /* calculate new image dimensions for final image size */
1158 width_final = old_width * zoom_factor;
1159 height_final = old_height * zoom_factor;
1161 /* get image with final size (this might require scaling up) */
1162 /* ("final" size may result in non-standard tile size image) */
1163 if (zoom_factor != 1)
1164 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1166 tmp_bitmap_final = old_bitmap;
1168 UPDATE_BUSY_STATE();
1170 width_0 = width_1 = width_final;
1171 height_0 = height_1 = height_final;
1173 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1175 if (create_small_bitmaps)
1177 /* check if we have a non-gameplay tile size image */
1178 if (tile_size != gfx.game_tile_size)
1180 /* get image with gameplay tile size */
1181 width_0 = width_final * gfx.game_tile_size / tile_size;
1182 height_0 = height_final * gfx.game_tile_size / tile_size;
1184 if (width_0 == old_width)
1185 tmp_bitmap_0 = old_bitmap;
1186 else if (width_0 == width_final)
1187 tmp_bitmap_0 = tmp_bitmap_final;
1189 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1191 UPDATE_BUSY_STATE();
1194 /* check if we have a non-standard tile size image */
1195 if (tile_size != gfx.standard_tile_size)
1197 /* get image with standard tile size */
1198 width_1 = width_final * gfx.standard_tile_size / tile_size;
1199 height_1 = height_final * gfx.standard_tile_size / tile_size;
1201 if (width_1 == old_width)
1202 tmp_bitmap_1 = old_bitmap;
1203 else if (width_1 == width_final)
1204 tmp_bitmap_1 = tmp_bitmap_final;
1205 else if (width_1 == width_0)
1206 tmp_bitmap_1 = tmp_bitmap_0;
1208 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1210 UPDATE_BUSY_STATE();
1213 /* calculate new image dimensions for small images */
1214 width_2 = width_1 / 2;
1215 height_2 = height_1 / 2;
1216 width_4 = width_1 / 4;
1217 height_4 = height_1 / 4;
1218 width_8 = width_1 / 8;
1219 height_8 = height_1 / 8;
1220 width_16 = width_1 / 16;
1221 height_16 = height_1 / 16;
1222 width_32 = width_1 / 32;
1223 height_32 = height_1 / 32;
1225 /* get image with 1/2 of normal size (for use in the level editor) */
1226 if (width_2 == old_width)
1227 tmp_bitmap_2 = old_bitmap;
1229 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1231 UPDATE_BUSY_STATE();
1233 /* get image with 1/4 of normal size (for use in the level editor) */
1234 if (width_4 == old_width)
1235 tmp_bitmap_4 = old_bitmap;
1237 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1239 UPDATE_BUSY_STATE();
1241 /* get image with 1/8 of normal size (for use on the preview screen) */
1242 if (width_8 == old_width)
1243 tmp_bitmap_8 = old_bitmap;
1245 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1247 UPDATE_BUSY_STATE();
1249 /* get image with 1/16 of normal size (for use on the preview screen) */
1250 if (width_16 == old_width)
1251 tmp_bitmap_16 = old_bitmap;
1253 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1255 UPDATE_BUSY_STATE();
1257 /* get image with 1/32 of normal size (for use on the preview screen) */
1258 if (width_32 == old_width)
1259 tmp_bitmap_32 = old_bitmap;
1261 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1263 UPDATE_BUSY_STATE();
1265 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1266 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1267 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1268 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1269 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1270 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1272 if (width_0 != width_1)
1273 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1275 if (bitmaps[IMG_BITMAP_CUSTOM])
1276 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1278 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1280 boolean free_old_bitmap = TRUE;
1282 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1283 if (bitmaps[i] == old_bitmap)
1284 free_old_bitmap = FALSE;
1286 if (free_old_bitmap)
1287 FreeBitmap(old_bitmap);
1291 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1294 UPDATE_BUSY_STATE();
1296 print_timestamp_done("CreateScaledBitmaps");
1299 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1302 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1305 void CreateBitmapTextures(Bitmap **bitmaps)
1307 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1310 void FreeBitmapTextures(Bitmap **bitmaps)
1312 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1315 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1317 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1321 /* ------------------------------------------------------------------------- */
1322 /* mouse pointer functions */
1323 /* ------------------------------------------------------------------------- */
1325 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1327 /* XPM image definitions */
1328 static const char *cursor_image_none[] =
1330 /* width height num_colors chars_per_pixel */
1360 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1361 static const char *cursor_image_dot[] =
1363 /* width height num_colors chars_per_pixel */
1392 static const char **cursor_image_playfield = cursor_image_dot;
1394 /* some people complained about a "white dot" on the screen and thought it
1395 was a graphical error... OK, let's just remove the whole pointer :-) */
1396 static const char **cursor_image_playfield = cursor_image_none;
1399 static const int cursor_bit_order = BIT_ORDER_MSB;
1401 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1403 struct MouseCursorInfo *cursor;
1404 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1405 int header_lines = 4;
1408 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1410 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1413 for (y = 0; y < cursor->width; y++)
1415 for (x = 0; x < cursor->height; x++)
1418 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1423 cursor->data[i] = cursor->mask[i] = 0;
1426 switch (image[header_lines + y][x])
1429 cursor->data[i] |= bit_mask;
1430 cursor->mask[i] |= bit_mask;
1434 cursor->mask[i] |= bit_mask;
1443 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1448 void SetMouseCursor(int mode)
1450 static struct MouseCursorInfo *cursor_none = NULL;
1451 static struct MouseCursorInfo *cursor_playfield = NULL;
1452 struct MouseCursorInfo *cursor_new;
1454 if (cursor_none == NULL)
1455 cursor_none = get_cursor_from_image(cursor_image_none);
1457 if (cursor_playfield == NULL)
1458 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1460 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1461 mode == CURSOR_NONE ? cursor_none :
1462 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1464 SDLSetMouseCursor(cursor_new);
1466 gfx.cursor_mode = mode;
1470 /* ========================================================================= */
1471 /* audio functions */
1472 /* ========================================================================= */
1474 void OpenAudio(void)
1476 /* always start with reliable default values */
1477 audio.sound_available = FALSE;
1478 audio.music_available = FALSE;
1479 audio.loops_available = FALSE;
1481 audio.sound_enabled = FALSE;
1482 audio.sound_deactivated = FALSE;
1484 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1485 audio.mixer_pid = 0;
1486 audio.device_name = NULL;
1487 audio.device_fd = -1;
1489 audio.num_channels = 0;
1490 audio.music_channel = 0;
1491 audio.first_sound_channel = 0;
1496 void CloseAudio(void)
1500 audio.sound_enabled = FALSE;
1503 void SetAudioMode(boolean enabled)
1505 if (!audio.sound_available)
1508 audio.sound_enabled = enabled;
1512 /* ========================================================================= */
1513 /* event functions */
1514 /* ========================================================================= */
1516 boolean PendingEvent(void)
1518 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1521 void WaitEvent(Event *event)
1523 SDLWaitEvent(event);
1526 void PeekEvent(Event *event)
1528 #if defined(TARGET_SDL2)
1529 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1531 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1535 void CheckQuitEvent(void)
1537 if (SDL_QuitRequested())
1538 program.exit_function(0);
1541 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1543 #if defined(TARGET_SDL2)
1544 /* key up/down events in SDL2 do not return text characters anymore */
1545 return event->keysym.sym;
1548 #if ENABLE_UNUSED_CODE
1549 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1550 (int)event->keysym.unicode,
1551 (int)event->keysym.sym,
1552 (int)SDL_GetModState());
1555 if (with_modifiers &&
1556 event->keysym.unicode > 0x0000 &&
1557 event->keysym.unicode < 0x2000)
1558 return event->keysym.unicode;
1560 return event->keysym.sym;
1565 KeyMod HandleKeyModState(Key key, int key_status)
1567 static KeyMod current_modifiers = KMOD_None;
1569 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1571 KeyMod new_modifier = KMOD_None;
1576 new_modifier = KMOD_Shift_L;
1579 new_modifier = KMOD_Shift_R;
1581 case KSYM_Control_L:
1582 new_modifier = KMOD_Control_L;
1584 case KSYM_Control_R:
1585 new_modifier = KMOD_Control_R;
1588 new_modifier = KMOD_Meta_L;
1591 new_modifier = KMOD_Meta_R;
1594 new_modifier = KMOD_Alt_L;
1597 new_modifier = KMOD_Alt_R;
1603 if (key_status == KEY_PRESSED)
1604 current_modifiers |= new_modifier;
1606 current_modifiers &= ~new_modifier;
1609 return current_modifiers;
1612 KeyMod GetKeyModState()
1614 return (KeyMod)SDL_GetModState();
1617 KeyMod GetKeyModStateFromEvents()
1619 /* always use key modifier state as tracked from key events (this is needed
1620 if the modifier key event was injected into the event queue, but the key
1621 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1622 query the keys as held pressed on the keyboard) -- this case is currently
1623 only used to filter out clipboard insert events from "True X-Mouse" tool */
1625 return HandleKeyModState(KSYM_UNDEFINED, 0);
1628 void StartTextInput(int x, int y, int width, int height)
1630 #if defined(TARGET_SDL2)
1631 #if defined(HAS_SCREEN_KEYBOARD)
1632 SDL_StartTextInput();
1634 if (y + height > SCREEN_KEYBOARD_POS(video.height))
1636 video.shifted_up_pos = y + height - SCREEN_KEYBOARD_POS(video.height);
1637 video.shifted_up_delay = SDL_GetTicks();
1638 video.shifted_up = TRUE;
1644 void StopTextInput()
1646 #if defined(TARGET_SDL2)
1647 #if defined(HAS_SCREEN_KEYBOARD)
1648 SDL_StopTextInput();
1650 if (video.shifted_up)
1652 video.shifted_up_pos = 0;
1653 video.shifted_up_delay = SDL_GetTicks();
1654 video.shifted_up = FALSE;
1660 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1662 if (event->type != EVENT_CLIENTMESSAGE)
1665 return TRUE; /* the only possible message here is SDL_QUIT */
1669 /* ========================================================================= */
1670 /* joystick functions */
1671 /* ========================================================================= */
1673 void InitJoysticks()
1677 #if defined(NO_JOYSTICK)
1678 return; /* joysticks generally deactivated by compile-time directive */
1681 /* always start with reliable default values */
1682 joystick.status = JOYSTICK_NOT_AVAILABLE;
1683 for (i = 0; i < MAX_PLAYERS; i++)
1684 joystick.nr[i] = -1; /* no joystick configured */
1689 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1691 return SDLReadJoystick(nr, x, y, b1, b2);
1694 boolean CheckJoystickOpened(int nr)
1696 return SDLCheckJoystickOpened(nr);
1699 void ClearJoystickState()
1701 SDLClearJoystickState();