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 int wheel_steps = DEFAULT_WHEEL_STEPS;
54 #if defined(TARGET_SDL2)
55 boolean keyrepeat_status = TRUE;
58 int redraw_mask = REDRAW_NONE;
63 /* ========================================================================= */
64 /* init/close functions */
65 /* ========================================================================= */
67 void InitProgramInfo(char *argv0, char *config_filename, char *userdata_subdir,
68 char *program_title, char *icon_title,
69 char *icon_filename, char *cookie_prefix,
72 program.command_basepath = getBasePath(argv0);
73 program.command_basename = getBaseName(argv0);
75 program.config_filename = config_filename;
77 program.userdata_subdir = userdata_subdir;
78 program.userdata_path = getUserGameDataDir();
80 program.program_title = program_title;
81 program.window_title = "(undefined)";
82 program.icon_title = icon_title;
84 program.icon_filename = icon_filename;
86 program.cookie_prefix = cookie_prefix;
88 program.version_major = VERSION_MAJOR(program_version);
89 program.version_minor = VERSION_MINOR(program_version);
90 program.version_patch = VERSION_PATCH(program_version);
91 program.version_build = VERSION_BUILD(program_version);
92 program.version_ident = program_version;
94 program.log_filename[LOG_OUT_ID] = getLogFilename(LOG_OUT_BASENAME);
95 program.log_filename[LOG_ERR_ID] = getLogFilename(LOG_ERR_BASENAME);
96 program.log_file[LOG_OUT_ID] = program.log_file_default[LOG_OUT_ID] = stdout;
97 program.log_file[LOG_ERR_ID] = program.log_file_default[LOG_ERR_ID] = stderr;
100 void SetWindowTitle()
102 program.window_title = program.window_title_function();
107 void InitWindowTitleFunction(char *(*window_title_function)(void))
109 program.window_title_function = window_title_function;
112 void InitExitMessageFunction(void (*exit_message_function)(char *, va_list))
114 program.exit_message_function = exit_message_function;
117 void InitExitFunction(void (*exit_function)(int))
119 program.exit_function = exit_function;
121 /* set signal handlers to custom exit function */
122 // signal(SIGINT, exit_function);
123 signal(SIGTERM, exit_function);
125 /* set exit function to automatically cleanup SDL stuff after exit() */
129 void InitPlatformDependentStuff(void)
131 // this is initialized in GetOptions(), but may already be used before
132 options.verbose = TRUE;
136 #if defined(TARGET_SDL2)
137 int sdl_init_flags = SDL_INIT_EVENTS | SDL_INIT_NOPARACHUTE;
139 int sdl_init_flags = SDL_INIT_EVENTTHREAD | SDL_INIT_NOPARACHUTE;
142 if (SDL_Init(sdl_init_flags) < 0)
143 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
148 void ClosePlatformDependentStuff(void)
153 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
154 int real_sx, int real_sy,
155 int full_sxsize, int full_sysize,
156 Bitmap *field_save_buffer)
162 gfx.real_sx = real_sx;
163 gfx.real_sy = real_sy;
164 gfx.full_sxsize = full_sxsize;
165 gfx.full_sysize = full_sysize;
167 gfx.field_save_buffer = field_save_buffer;
169 SetDrawDeactivationMask(REDRAW_NONE); /* do not deactivate drawing */
170 SetDrawBackgroundMask(REDRAW_NONE); /* deactivate masked drawing */
173 void InitGfxTileSizeInfo(int game_tile_size, int standard_tile_size)
175 gfx.game_tile_size = game_tile_size;
176 gfx.standard_tile_size = standard_tile_size;
179 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
187 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
195 void InitGfxDoor3Info(int ex, int ey, int exsize, int eysize)
203 void InitGfxWindowInfo(int win_xsize, int win_ysize)
205 if (win_xsize != gfx.win_xsize || win_ysize != gfx.win_ysize)
207 ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize);
209 #if defined(TARGET_SDL2)
210 ReCreateBitmap(&gfx.final_screen_bitmap, win_xsize, win_ysize);
213 ReCreateBitmap(&gfx.fade_bitmap_backup, win_xsize, win_ysize);
214 ReCreateBitmap(&gfx.fade_bitmap_source, win_xsize, win_ysize);
215 ReCreateBitmap(&gfx.fade_bitmap_target, win_xsize, win_ysize);
216 ReCreateBitmap(&gfx.fade_bitmap_black, win_xsize, win_ysize);
218 ClearRectangle(gfx.fade_bitmap_black, 0, 0, win_xsize, win_ysize);
221 gfx.win_xsize = win_xsize;
222 gfx.win_ysize = win_ysize;
224 gfx.background_bitmap_mask = REDRAW_NONE;
227 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
229 /* currently only used by MSDOS code to alloc VRAM buffer, if available */
230 /* 2009-03-24: also (temporarily?) used for overlapping blit workaround */
231 gfx.scrollbuffer_width = scrollbuffer_width;
232 gfx.scrollbuffer_height = scrollbuffer_height;
235 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
237 gfx.clipping_enabled = enabled;
240 gfx.clip_width = width;
241 gfx.clip_height = height;
244 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
246 gfx.draw_busy_anim_function = draw_busy_anim_function;
249 void InitGfxDrawGlobalAnimFunction(void (*draw_global_anim_function)(int, int))
251 gfx.draw_global_anim_function = draw_global_anim_function;
254 void InitGfxDrawGlobalBorderFunction(void (*draw_global_border_function)(int))
256 gfx.draw_global_border_function = draw_global_border_function;
259 void InitGfxCustomArtworkInfo()
261 gfx.override_level_graphics = FALSE;
262 gfx.override_level_sounds = FALSE;
263 gfx.override_level_music = FALSE;
265 gfx.draw_init_text = TRUE;
268 void InitGfxOtherSettings()
270 gfx.cursor_mode = CURSOR_DEFAULT;
273 void SetDrawDeactivationMask(int draw_deactivation_mask)
275 gfx.draw_deactivation_mask = draw_deactivation_mask;
278 void SetDrawBackgroundMask(int draw_background_mask)
280 gfx.draw_background_mask = draw_background_mask;
283 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
285 if (background_bitmap_tile != NULL)
286 gfx.background_bitmap_mask |= mask;
288 gfx.background_bitmap_mask &= ~mask;
290 if (background_bitmap_tile == NULL) /* empty background requested */
293 if (mask == REDRAW_ALL)
294 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
295 0, 0, video.width, video.height);
296 else if (mask == REDRAW_FIELD)
297 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
298 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
299 else if (mask == REDRAW_DOOR_1)
300 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
301 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
304 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
306 /* remove every mask before setting mask for window */
307 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
308 SetBackgroundBitmap(NULL, 0xffff); /* !!! FIX THIS !!! */
309 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
312 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
314 /* remove window area mask before setting mask for main area */
315 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
316 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
317 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
320 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
322 /* remove window area mask before setting mask for door area */
323 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
324 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
325 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
329 /* ========================================================================= */
330 /* video functions */
331 /* ========================================================================= */
333 inline static int GetRealDepth(int depth)
335 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
338 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
339 int width, int height, Pixel color)
341 SDLFillRectangle(bitmap, x, y, width, height, color);
343 if (bitmap == backbuffer)
344 SetRedrawMaskFromArea(x, y, width, height);
347 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
348 int src_x, int src_y, int width, int height,
349 int dst_x, int dst_y, int mask_mode)
351 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
352 dst_x, dst_y, mask_mode);
354 if (dst_bitmap == backbuffer)
355 SetRedrawMaskFromArea(dst_x, dst_y, width, height);
358 void LimitScreenUpdates(boolean enable)
360 SDLLimitScreenUpdates(enable);
363 void InitVideoDisplay(void)
365 SDLInitVideoDisplay();
369 void CloseVideoDisplay(void)
371 KeyboardAutoRepeatOn();
373 SDL_QuitSubSystem(SDL_INIT_VIDEO);
376 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
379 video.height = height;
380 video.depth = GetRealDepth(depth);
382 video.screen_width = width;
383 video.screen_height = height;
384 video.screen_xoffset = 0;
385 video.screen_yoffset = 0;
387 video.fullscreen_available = FULLSCREEN_STATUS;
388 video.fullscreen_enabled = FALSE;
390 video.window_scaling_available = WINDOW_SCALING_STATUS;
392 video.frame_delay = 0;
393 video.frame_delay_value = GAME_FRAME_DELAY;
395 video.shifted_up = FALSE;
396 video.shifted_up_pos = 0;
397 video.shifted_up_pos_last = 0;
398 video.shifted_up_delay = 0;
399 video.shifted_up_delay_value = ONE_SECOND_DELAY / 4;
401 SDLInitVideoBuffer(fullscreen);
403 video.initialized = TRUE;
408 inline static void FreeBitmapPointers(Bitmap *bitmap)
413 SDLFreeBitmapPointers(bitmap);
415 checked_free(bitmap->source_filename);
416 bitmap->source_filename = NULL;
419 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
422 if (src_bitmap == NULL || dst_bitmap == NULL)
425 FreeBitmapPointers(dst_bitmap);
427 *dst_bitmap = *src_bitmap;
430 void FreeBitmap(Bitmap *bitmap)
435 FreeBitmapPointers(bitmap);
440 Bitmap *CreateBitmapStruct(void)
442 return checked_calloc(sizeof(Bitmap));
445 Bitmap *CreateBitmap(int width, int height, int depth)
447 Bitmap *new_bitmap = CreateBitmapStruct();
448 int real_width = MAX(1, width); /* prevent zero bitmap width */
449 int real_height = MAX(1, height); /* prevent zero bitmap height */
450 int real_depth = GetRealDepth(depth);
452 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
454 new_bitmap->width = real_width;
455 new_bitmap->height = real_height;
460 void ReCreateBitmap(Bitmap **bitmap, int width, int height)
462 Bitmap *new_bitmap = CreateBitmap(width, height, DEFAULT_DEPTH);
466 *bitmap = new_bitmap;
470 TransferBitmapPointers(new_bitmap, *bitmap);
475 void CloseWindow(DrawWindow *window)
479 void SetRedrawMaskFromArea(int x, int y, int width, int height)
483 int x2 = x + width - 1;
484 int y2 = y + height - 1;
486 if (width == 0 || height == 0)
489 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
490 redraw_mask |= REDRAW_FIELD;
491 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
492 redraw_mask |= REDRAW_DOOR_1;
493 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
494 redraw_mask |= REDRAW_DOOR_2;
495 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
496 redraw_mask |= REDRAW_DOOR_3;
498 redraw_mask = REDRAW_ALL;
501 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
504 if (draw_mask == REDRAW_NONE)
507 if (draw_mask & REDRAW_ALL)
510 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
513 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
516 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
519 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
525 boolean DrawingDeactivated(int x, int y, int width, int height)
527 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
530 boolean DrawingOnBackground(int x, int y)
532 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
533 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
536 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
537 int *width, int *height, boolean is_dest)
539 int clip_x, clip_y, clip_width, clip_height;
541 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
543 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
544 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
545 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
546 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
552 clip_width = bitmap->width;
553 clip_height = bitmap->height;
556 /* skip if rectangle completely outside bitmap */
558 if (*x + *width <= clip_x ||
559 *y + *height <= clip_y ||
560 *x >= clip_x + clip_width ||
561 *y >= clip_y + clip_height)
564 /* clip if rectangle overlaps bitmap */
568 *width -= clip_x - *x;
571 else if (*x + *width > clip_x + clip_width)
573 *width = clip_x + clip_width - *x;
578 *height -= clip_y - *y;
581 else if (*y + *height > clip_y + clip_height)
583 *height = clip_y + clip_height - *y;
589 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
590 int src_x, int src_y, int width, int height,
591 int dst_x, int dst_y)
593 int dst_x_unclipped = dst_x;
594 int dst_y_unclipped = dst_y;
596 if (src_bitmap == NULL || dst_bitmap == NULL)
599 if (DrawingDeactivated(dst_x, dst_y, width, height))
602 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
603 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
606 /* source x/y might need adjustment if destination x/y was clipped top/left */
607 src_x += dst_x - dst_x_unclipped;
608 src_y += dst_y - dst_y_unclipped;
610 #if defined(TARGET_SDL2)
611 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
612 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
613 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
614 but is already fixed in SVN and should therefore finally be fixed with
615 the next official SDL release, which is probably version 1.2.14.) */
616 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
618 if (src_bitmap == dst_bitmap)
620 /* needed when blitting directly to same bitmap -- should not be needed with
621 recent SDL libraries, but apparently does not work in 1.2.11 directly */
623 static Bitmap *tmp_bitmap = NULL;
624 static int tmp_bitmap_xsize = 0;
625 static int tmp_bitmap_ysize = 0;
627 /* start with largest static bitmaps for initial bitmap size ... */
628 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
630 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
631 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
634 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
635 if (src_bitmap->width > tmp_bitmap_xsize ||
636 src_bitmap->height > tmp_bitmap_ysize)
638 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
639 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
641 FreeBitmap(tmp_bitmap);
646 if (tmp_bitmap == NULL)
647 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
650 sysCopyArea(src_bitmap, tmp_bitmap,
651 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
652 sysCopyArea(tmp_bitmap, dst_bitmap,
653 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
659 sysCopyArea(src_bitmap, dst_bitmap,
660 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
663 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
664 int src_x, int src_y, int src_width, int src_height,
665 int dst_x, int dst_y, int dst_width, int dst_height)
667 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
668 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
669 int dst_xsize = dst_width;
670 int dst_ysize = dst_height;
671 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
672 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
675 for (y = 0; y < src_ysteps; y++)
677 for (x = 0; x < src_xsteps; x++)
679 int draw_x = dst_x + x * src_xsize;
680 int draw_y = dst_y + y * src_ysize;
681 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
682 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
684 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
690 void FadeRectangle(int x, int y, int width, int height,
691 int fade_mode, int fade_delay, int post_delay,
692 void (*draw_border_function)(void))
694 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
695 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
698 SDLFadeRectangle(x, y, width, height,
699 fade_mode, fade_delay, post_delay, draw_border_function);
702 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
705 if (DrawingDeactivated(x, y, width, height))
708 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
711 sysFillRectangle(bitmap, x, y, width, height, color);
714 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
716 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
719 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
720 int width, int height)
722 if (DrawingOnBackground(x, y))
723 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
725 ClearRectangle(bitmap, x, y, width, height);
728 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
729 int src_x, int src_y, int width, int height,
730 int dst_x, int dst_y)
732 if (DrawingDeactivated(dst_x, dst_y, width, height))
735 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
736 dst_x, dst_y, BLIT_MASKED);
739 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
740 int src_x, int src_y, int width, int height,
741 int dst_x, int dst_y)
743 if (DrawingOnBackground(dst_x, dst_y))
745 /* draw background */
746 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
749 /* draw foreground */
750 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
754 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
758 void BlitTexture(Bitmap *bitmap,
759 int src_x, int src_y, int width, int height,
760 int dst_x, int dst_y)
765 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
769 void BlitTextureMasked(Bitmap *bitmap,
770 int src_x, int src_y, int width, int height,
771 int dst_x, int dst_y)
776 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
780 void BlitToScreen(Bitmap *bitmap,
781 int src_x, int src_y, int width, int height,
782 int dst_x, int dst_y)
787 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
788 BlitBitmap(bitmap, gfx.final_screen_bitmap, src_x, src_y,
789 width, height, dst_x, dst_y);
791 BlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y);
794 void BlitToScreenMasked(Bitmap *bitmap,
795 int src_x, int src_y, int width, int height,
796 int dst_x, int dst_y)
801 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
802 BlitBitmapMasked(bitmap, gfx.final_screen_bitmap, src_x, src_y,
803 width, height, dst_x, dst_y);
805 BlitTextureMasked(bitmap, src_x, src_y, width, height, dst_x, dst_y);
808 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
811 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
814 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
817 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
820 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
821 int to_x, int to_y, Pixel pixel, int line_width)
825 for (x = 0; x < line_width; x++)
827 for (y = 0; y < line_width; y++)
829 int dx = x - line_width / 2;
830 int dy = y - line_width / 2;
832 if ((x == 0 && y == 0) ||
833 (x == 0 && y == line_width - 1) ||
834 (x == line_width - 1 && y == 0) ||
835 (x == line_width - 1 && y == line_width - 1))
839 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
844 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
849 for (i = 0; i < num_points - 1; i++)
850 DrawLine(bitmap, points[i].x, points[i].y,
851 points[i + 1].x, points[i + 1].y, pixel, line_width);
854 SDLDrawLines(bitmap->surface, points, num_points, pixel);
858 Pixel GetPixel(Bitmap *bitmap, int x, int y)
860 if (x < 0 || x >= bitmap->width ||
861 y < 0 || y >= bitmap->height)
864 return SDLGetPixel(bitmap, x, y);
867 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
868 unsigned int color_g, unsigned int color_b)
870 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
873 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
875 unsigned int color_r = (color >> 16) & 0xff;
876 unsigned int color_g = (color >> 8) & 0xff;
877 unsigned int color_b = (color >> 0) & 0xff;
879 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
882 void KeyboardAutoRepeatOn(void)
884 #if defined(TARGET_SDL2)
885 keyrepeat_status = TRUE;
887 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
888 SDL_DEFAULT_REPEAT_INTERVAL / 2);
889 SDL_EnableUNICODE(1);
893 void KeyboardAutoRepeatOff(void)
895 #if defined(TARGET_SDL2)
896 keyrepeat_status = FALSE;
898 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
899 SDL_EnableUNICODE(0);
903 boolean SetVideoMode(boolean fullscreen)
905 return SDLSetVideoMode(fullscreen);
908 void SetVideoFrameDelay(unsigned int frame_delay_value)
910 video.frame_delay_value = frame_delay_value;
913 unsigned int GetVideoFrameDelay()
915 return video.frame_delay_value;
918 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
920 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
921 (!fullscreen && video.fullscreen_enabled))
922 fullscreen = SetVideoMode(fullscreen);
927 Bitmap *LoadImage(char *filename)
931 new_bitmap = SDLLoadImage(filename);
934 new_bitmap->source_filename = getStringCopy(filename);
939 Bitmap *LoadCustomImage(char *basename)
941 char *filename = getCustomImageFilename(basename);
944 if (filename == NULL)
945 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
947 if ((new_bitmap = LoadImage(filename)) == NULL)
948 Error(ERR_EXIT, "LoadImage('%s') failed: %s", basename, GetError());
953 void ReloadCustomImage(Bitmap *bitmap, char *basename)
955 char *filename = getCustomImageFilename(basename);
958 if (filename == NULL) /* (should never happen) */
960 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
964 if (strEqual(filename, bitmap->source_filename))
966 /* The old and new image are the same (have the same filename and path).
967 This usually means that this image does not exist in this graphic set
968 and a fallback to the existing image is done. */
973 if ((new_bitmap = LoadImage(filename)) == NULL)
975 Error(ERR_WARN, "LoadImage('%s') failed: %s", basename, GetError());
979 if (bitmap->width != new_bitmap->width ||
980 bitmap->height != new_bitmap->height)
982 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
984 FreeBitmap(new_bitmap);
988 TransferBitmapPointers(new_bitmap, bitmap);
992 static Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
994 return SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
997 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
999 if (bitmaps[IMG_BITMAP_CUSTOM])
1001 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
1003 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
1006 if (gfx.game_tile_size == gfx.standard_tile_size)
1008 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1013 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
1014 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
1015 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
1017 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
1019 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
1020 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
1023 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1024 int tile_size, boolean create_small_bitmaps)
1026 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1027 Bitmap *tmp_bitmap_final = NULL;
1028 Bitmap *tmp_bitmap_0 = NULL;
1029 Bitmap *tmp_bitmap_1 = NULL;
1030 Bitmap *tmp_bitmap_2 = NULL;
1031 Bitmap *tmp_bitmap_4 = NULL;
1032 Bitmap *tmp_bitmap_8 = NULL;
1033 Bitmap *tmp_bitmap_16 = NULL;
1034 Bitmap *tmp_bitmap_32 = NULL;
1035 int width_final, height_final;
1036 int width_0, height_0;
1037 int width_1, height_1;
1038 int width_2, height_2;
1039 int width_4, height_4;
1040 int width_8, height_8;
1041 int width_16, height_16;
1042 int width_32, height_32;
1043 int old_width, old_height;
1046 print_timestamp_init("CreateScaledBitmaps");
1048 old_width = old_bitmap->width;
1049 old_height = old_bitmap->height;
1051 /* calculate new image dimensions for final image size */
1052 width_final = old_width * zoom_factor;
1053 height_final = old_height * zoom_factor;
1055 /* get image with final size (this might require scaling up) */
1056 /* ("final" size may result in non-standard tile size image) */
1057 if (zoom_factor != 1)
1058 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1060 tmp_bitmap_final = old_bitmap;
1062 UPDATE_BUSY_STATE();
1064 width_0 = width_1 = width_final;
1065 height_0 = height_1 = height_final;
1067 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1069 if (create_small_bitmaps)
1071 /* check if we have a non-gameplay tile size image */
1072 if (tile_size != gfx.game_tile_size)
1074 /* get image with gameplay tile size */
1075 width_0 = width_final * gfx.game_tile_size / tile_size;
1076 height_0 = height_final * gfx.game_tile_size / tile_size;
1078 if (width_0 == old_width)
1079 tmp_bitmap_0 = old_bitmap;
1080 else if (width_0 == width_final)
1081 tmp_bitmap_0 = tmp_bitmap_final;
1083 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1085 UPDATE_BUSY_STATE();
1088 /* check if we have a non-standard tile size image */
1089 if (tile_size != gfx.standard_tile_size)
1091 /* get image with standard tile size */
1092 width_1 = width_final * gfx.standard_tile_size / tile_size;
1093 height_1 = height_final * gfx.standard_tile_size / tile_size;
1095 if (width_1 == old_width)
1096 tmp_bitmap_1 = old_bitmap;
1097 else if (width_1 == width_final)
1098 tmp_bitmap_1 = tmp_bitmap_final;
1099 else if (width_1 == width_0)
1100 tmp_bitmap_1 = tmp_bitmap_0;
1102 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1104 UPDATE_BUSY_STATE();
1107 /* calculate new image dimensions for small images */
1108 width_2 = width_1 / 2;
1109 height_2 = height_1 / 2;
1110 width_4 = width_1 / 4;
1111 height_4 = height_1 / 4;
1112 width_8 = width_1 / 8;
1113 height_8 = height_1 / 8;
1114 width_16 = width_1 / 16;
1115 height_16 = height_1 / 16;
1116 width_32 = width_1 / 32;
1117 height_32 = height_1 / 32;
1119 /* get image with 1/2 of normal size (for use in the level editor) */
1120 if (width_2 == old_width)
1121 tmp_bitmap_2 = old_bitmap;
1123 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1125 UPDATE_BUSY_STATE();
1127 /* get image with 1/4 of normal size (for use in the level editor) */
1128 if (width_4 == old_width)
1129 tmp_bitmap_4 = old_bitmap;
1131 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1133 UPDATE_BUSY_STATE();
1135 /* get image with 1/8 of normal size (for use on the preview screen) */
1136 if (width_8 == old_width)
1137 tmp_bitmap_8 = old_bitmap;
1139 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1141 UPDATE_BUSY_STATE();
1143 /* get image with 1/16 of normal size (for use on the preview screen) */
1144 if (width_16 == old_width)
1145 tmp_bitmap_16 = old_bitmap;
1147 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1149 UPDATE_BUSY_STATE();
1151 /* get image with 1/32 of normal size (for use on the preview screen) */
1152 if (width_32 == old_width)
1153 tmp_bitmap_32 = old_bitmap;
1155 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1157 UPDATE_BUSY_STATE();
1159 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1160 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1161 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1162 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1163 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1164 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1166 if (width_0 != width_1)
1167 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1169 if (bitmaps[IMG_BITMAP_CUSTOM])
1170 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1172 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1174 boolean free_old_bitmap = TRUE;
1176 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1177 if (bitmaps[i] == old_bitmap)
1178 free_old_bitmap = FALSE;
1180 if (free_old_bitmap)
1181 FreeBitmap(old_bitmap);
1185 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1188 UPDATE_BUSY_STATE();
1190 print_timestamp_done("CreateScaledBitmaps");
1193 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1196 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1199 void CreateBitmapTextures(Bitmap **bitmaps)
1201 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1204 void FreeBitmapTextures(Bitmap **bitmaps)
1206 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1209 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1211 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1215 /* ------------------------------------------------------------------------- */
1216 /* mouse pointer functions */
1217 /* ------------------------------------------------------------------------- */
1219 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1221 /* XPM image definitions */
1222 static const char *cursor_image_none[] =
1224 /* width height num_colors chars_per_pixel */
1254 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1255 static const char *cursor_image_dot[] =
1257 /* width height num_colors chars_per_pixel */
1286 static const char **cursor_image_playfield = cursor_image_dot;
1288 /* some people complained about a "white dot" on the screen and thought it
1289 was a graphical error... OK, let's just remove the whole pointer :-) */
1290 static const char **cursor_image_playfield = cursor_image_none;
1293 static const int cursor_bit_order = BIT_ORDER_MSB;
1295 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1297 struct MouseCursorInfo *cursor;
1298 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1299 int header_lines = 4;
1302 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1304 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1307 for (y = 0; y < cursor->width; y++)
1309 for (x = 0; x < cursor->height; x++)
1312 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1317 cursor->data[i] = cursor->mask[i] = 0;
1320 switch (image[header_lines + y][x])
1323 cursor->data[i] |= bit_mask;
1324 cursor->mask[i] |= bit_mask;
1328 cursor->mask[i] |= bit_mask;
1337 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1342 void SetMouseCursor(int mode)
1344 static struct MouseCursorInfo *cursor_none = NULL;
1345 static struct MouseCursorInfo *cursor_playfield = NULL;
1346 struct MouseCursorInfo *cursor_new;
1348 if (cursor_none == NULL)
1349 cursor_none = get_cursor_from_image(cursor_image_none);
1351 if (cursor_playfield == NULL)
1352 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1354 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1355 mode == CURSOR_NONE ? cursor_none :
1356 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1358 SDLSetMouseCursor(cursor_new);
1360 gfx.cursor_mode = mode;
1364 /* ========================================================================= */
1365 /* audio functions */
1366 /* ========================================================================= */
1368 void OpenAudio(void)
1370 /* always start with reliable default values */
1371 audio.sound_available = FALSE;
1372 audio.music_available = FALSE;
1373 audio.loops_available = FALSE;
1375 audio.sound_enabled = FALSE;
1376 audio.sound_deactivated = FALSE;
1378 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1379 audio.mixer_pid = 0;
1380 audio.device_name = NULL;
1381 audio.device_fd = -1;
1383 audio.num_channels = 0;
1384 audio.music_channel = 0;
1385 audio.first_sound_channel = 0;
1390 void CloseAudio(void)
1394 audio.sound_enabled = FALSE;
1397 void SetAudioMode(boolean enabled)
1399 if (!audio.sound_available)
1402 audio.sound_enabled = enabled;
1406 /* ========================================================================= */
1407 /* event functions */
1408 /* ========================================================================= */
1410 boolean PendingEvent(void)
1412 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1415 void NextEvent(Event *event)
1417 SDLNextEvent(event);
1420 void PeekEvent(Event *event)
1422 #if defined(TARGET_SDL2)
1423 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1425 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1429 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1431 #if defined(TARGET_SDL2)
1432 /* key up/down events in SDL2 do not return text characters anymore */
1433 return event->keysym.sym;
1436 #if ENABLE_UNUSED_CODE
1437 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1438 (int)event->keysym.unicode,
1439 (int)event->keysym.sym,
1440 (int)SDL_GetModState());
1443 if (with_modifiers &&
1444 event->keysym.unicode > 0x0000 &&
1445 event->keysym.unicode < 0x2000)
1446 return event->keysym.unicode;
1448 return event->keysym.sym;
1453 KeyMod HandleKeyModState(Key key, int key_status)
1455 static KeyMod current_modifiers = KMOD_None;
1457 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1459 KeyMod new_modifier = KMOD_None;
1464 new_modifier = KMOD_Shift_L;
1467 new_modifier = KMOD_Shift_R;
1469 case KSYM_Control_L:
1470 new_modifier = KMOD_Control_L;
1472 case KSYM_Control_R:
1473 new_modifier = KMOD_Control_R;
1476 new_modifier = KMOD_Meta_L;
1479 new_modifier = KMOD_Meta_R;
1482 new_modifier = KMOD_Alt_L;
1485 new_modifier = KMOD_Alt_R;
1491 if (key_status == KEY_PRESSED)
1492 current_modifiers |= new_modifier;
1494 current_modifiers &= ~new_modifier;
1497 return current_modifiers;
1500 KeyMod GetKeyModState()
1502 return (KeyMod)SDL_GetModState();
1505 KeyMod GetKeyModStateFromEvents()
1507 /* always use key modifier state as tracked from key events (this is needed
1508 if the modifier key event was injected into the event queue, but the key
1509 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1510 query the keys as held pressed on the keyboard) -- this case is currently
1511 only used to filter out clipboard insert events from "True X-Mouse" tool */
1513 return HandleKeyModState(KSYM_UNDEFINED, 0);
1516 void StartTextInput(int x, int y, int width, int height)
1518 #if defined(TARGET_SDL2)
1519 SDL_StartTextInput();
1521 #if defined(HAS_SCREEN_KEYBOARD)
1522 if (y + height > SCREEN_KEYBOARD_POS(video.height))
1524 video.shifted_up_pos = y + height - SCREEN_KEYBOARD_POS(video.height);
1525 video.shifted_up_delay = SDL_GetTicks();
1526 video.shifted_up = TRUE;
1532 void StopTextInput()
1534 #if defined(TARGET_SDL2)
1535 SDL_StopTextInput();
1537 #if defined(HAS_SCREEN_KEYBOARD)
1538 if (video.shifted_up)
1540 video.shifted_up_pos = 0;
1541 video.shifted_up_delay = SDL_GetTicks();
1542 video.shifted_up = FALSE;
1548 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1550 if (event->type != EVENT_CLIENTMESSAGE)
1553 return TRUE; /* the only possible message here is SDL_QUIT */
1557 /* ========================================================================= */
1558 /* joystick functions */
1559 /* ========================================================================= */
1561 void InitJoysticks()
1565 #if defined(NO_JOYSTICK)
1566 return; /* joysticks generally deactivated by compile-time directive */
1569 /* always start with reliable default values */
1570 joystick.status = JOYSTICK_NOT_AVAILABLE;
1571 for (i = 0; i < MAX_PLAYERS; i++)
1572 joystick.fd[i] = -1; /* joystick device closed */
1577 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1579 return SDLReadJoystick(nr, x, y, b1, b2);