1 /***********************************************************
2 * Artsoft Retro-Game Library *
3 *----------------------------------------------------------*
4 * (c) 1994-2006 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
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 Display *display = NULL;
48 Visual *visual = NULL;
52 DrawWindow *window = NULL;
53 DrawBuffer *backbuffer = NULL;
54 DrawBuffer *drawto = NULL;
56 int button_status = MB_NOT_PRESSED;
57 boolean motion_status = FALSE;
58 #if defined(TARGET_SDL2)
59 boolean keyrepeat_status = TRUE;
62 int redraw_mask = REDRAW_NONE;
68 /* ========================================================================= */
69 /* init/close functions */
70 /* ========================================================================= */
72 void InitProgramInfo(char *argv0,
73 char *userdata_subdir, char *userdata_subdir_unix,
74 char *program_title, char *icon_title,
75 char *sdl_icon_filename, char *cookie_prefix,
78 program.command_basepath = getBasePath(argv0);
79 program.command_basename = getBaseName(argv0);
81 program.userdata_subdir = userdata_subdir;
82 program.userdata_subdir_unix = userdata_subdir_unix;
83 program.userdata_path = getUserGameDataDir();
85 program.program_title = program_title;
86 program.window_title = "(undefined)";
87 program.icon_title = icon_title;
89 program.sdl_icon_filename = sdl_icon_filename;
91 program.cookie_prefix = cookie_prefix;
93 program.version_major = VERSION_MAJOR(program_version);
94 program.version_minor = VERSION_MINOR(program_version);
95 program.version_patch = VERSION_PATCH(program_version);
96 program.version_build = VERSION_BUILD(program_version);
97 program.version_ident = program_version;
99 program.error_filename = getErrorFilename(ERROR_BASENAME);
100 program.error_file = stderr;
103 void SetWindowTitle()
105 program.window_title = program.window_title_function();
107 #if defined(TARGET_SDL)
112 void InitWindowTitleFunction(char *(*window_title_function)(void))
114 program.window_title_function = window_title_function;
117 void InitExitMessageFunction(void (*exit_message_function)(char *, va_list))
119 program.exit_message_function = exit_message_function;
122 void InitExitFunction(void (*exit_function)(int))
124 program.exit_function = exit_function;
126 /* set signal handlers to custom exit function */
127 signal(SIGINT, exit_function);
128 signal(SIGTERM, exit_function);
130 #if defined(TARGET_SDL)
131 /* set exit function to automatically cleanup SDL stuff after exit() */
136 void InitPlatformDependentStuff(void)
138 // this is initialized in GetOptions(), but may already be used before
139 options.verbose = TRUE;
141 #if defined(PLATFORM_MACOSX)
142 updateUserGameDataDir();
148 #if !defined(PLATFORM_UNIX) || defined(PLATFORM_MACOSX)
153 #if defined(TARGET_SDL)
154 #if defined(TARGET_SDL2)
155 int sdl_init_flags = SDL_INIT_EVENTS | SDL_INIT_NOPARACHUTE;
157 int sdl_init_flags = SDL_INIT_EVENTTHREAD | SDL_INIT_NOPARACHUTE;
160 if (SDL_Init(sdl_init_flags) < 0)
161 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
167 void ClosePlatformDependentStuff(void)
169 #if defined(PLATFORM_WIN32)
174 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
175 int real_sx, int real_sy,
176 int full_sxsize, int full_sysize,
177 Bitmap *field_save_buffer)
183 gfx.real_sx = real_sx;
184 gfx.real_sy = real_sy;
185 gfx.full_sxsize = full_sxsize;
186 gfx.full_sysize = full_sysize;
188 gfx.field_save_buffer = field_save_buffer;
191 gfx.background_bitmap = NULL;
192 gfx.background_bitmap_mask = REDRAW_NONE;
195 SetDrawDeactivationMask(REDRAW_NONE); /* do not deactivate drawing */
196 SetDrawBackgroundMask(REDRAW_NONE); /* deactivate masked drawing */
199 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
207 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
215 void InitGfxDoor3Info(int ex, int ey, int exsize, int eysize)
223 void InitGfxWindowInfo(int win_xsize, int win_ysize)
225 gfx.win_xsize = win_xsize;
226 gfx.win_ysize = win_ysize;
229 gfx.background_bitmap_mask = REDRAW_NONE;
231 ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize, DEFAULT_DEPTH);
235 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
237 /* currently only used by MSDOS code to alloc VRAM buffer, if available */
238 /* 2009-03-24: also (temporarily?) used for overlapping blit workaround */
239 gfx.scrollbuffer_width = scrollbuffer_width;
240 gfx.scrollbuffer_height = scrollbuffer_height;
243 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
245 gfx.clipping_enabled = enabled;
248 gfx.clip_width = width;
249 gfx.clip_height = height;
252 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
254 gfx.draw_busy_anim_function = draw_busy_anim_function;
257 void InitGfxCustomArtworkInfo()
259 gfx.override_level_graphics = FALSE;
260 gfx.override_level_sounds = FALSE;
261 gfx.override_level_music = FALSE;
263 gfx.draw_init_text = TRUE;
266 void SetDrawDeactivationMask(int draw_deactivation_mask)
268 gfx.draw_deactivation_mask = draw_deactivation_mask;
271 void SetDrawBackgroundMask(int draw_background_mask)
273 gfx.draw_background_mask = draw_background_mask;
278 static void DrawBitmapFromTile(Bitmap *bitmap, Bitmap *tile,
279 int dest_x, int dest_y, int width, int height)
281 int bitmap_xsize = width;
282 int bitmap_ysize = height;
283 int tile_xsize = tile->width;
284 int tile_ysize = tile->height;
285 int tile_xsteps = (bitmap_xsize + tile_xsize - 1) / tile_xsize;
286 int tile_ysteps = (bitmap_ysize + tile_ysize - 1) / tile_ysize;
289 for (y = 0; y < tile_ysteps; y++)
291 for (x = 0; x < tile_xsteps; x++)
293 int draw_x = dest_x + x * tile_xsize;
294 int draw_y = dest_y + y * tile_ysize;
295 int draw_xsize = MIN(tile_xsize, bitmap_xsize - x * tile_xsize);
296 int draw_ysize = MIN(tile_ysize, bitmap_ysize - y * tile_ysize);
298 BlitBitmap(tile, bitmap, 0, 0, draw_xsize, draw_ysize, draw_x, draw_y);
303 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
305 if (background_bitmap_tile != NULL)
306 gfx.background_bitmap_mask |= mask;
308 gfx.background_bitmap_mask &= ~mask;
311 if (gfx.background_bitmap == NULL)
312 gfx.background_bitmap = CreateBitmap(video.width, video.height,
316 if (background_bitmap_tile == NULL) /* empty background requested */
319 if (mask == REDRAW_ALL)
320 DrawBitmapFromTile(gfx.background_bitmap, background_bitmap_tile,
321 0, 0, video.width, video.height);
322 else if (mask == REDRAW_FIELD)
323 DrawBitmapFromTile(gfx.background_bitmap, background_bitmap_tile,
324 gfx.real_sx, gfx.real_sy,
325 gfx.full_sxsize, gfx.full_sysize);
326 else if (mask == REDRAW_DOOR_1)
327 DrawBitmapFromTile(gfx.background_bitmap, background_bitmap_tile,
329 gfx.dxsize, gfx.dysize);
334 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
336 if (background_bitmap_tile != NULL)
337 gfx.background_bitmap_mask |= mask;
339 gfx.background_bitmap_mask &= ~mask;
342 if (gfx.background_bitmap == NULL)
343 gfx.background_bitmap = CreateBitmap(video.width, video.height,
347 if (background_bitmap_tile == NULL) /* empty background requested */
350 if (mask == REDRAW_ALL)
351 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
352 0, 0, video.width, video.height);
353 else if (mask == REDRAW_FIELD)
354 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
355 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
356 else if (mask == REDRAW_DOOR_1)
357 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
358 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
363 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
365 /* remove every mask before setting mask for window */
366 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
367 SetBackgroundBitmap(NULL, 0xffff); /* !!! FIX THIS !!! */
368 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
371 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
373 /* remove window area mask before setting mask for main area */
374 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
375 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
376 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
379 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
381 /* remove window area mask before setting mask for door area */
382 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
383 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
384 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
388 /* ========================================================================= */
389 /* video functions */
390 /* ========================================================================= */
392 inline static int GetRealDepth(int depth)
394 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
397 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
398 int width, int height, Pixel color)
400 SDLFillRectangle(bitmap, x, y, width, height, color);
403 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
404 int src_x, int src_y, int width, int height,
405 int dst_x, int dst_y, int mask_mode)
407 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
408 dst_x, dst_y, mask_mode);
411 void LimitScreenUpdates(boolean enable)
413 #if defined(TARGET_SDL)
414 SDLLimitScreenUpdates(enable);
418 void InitVideoDisplay(void)
420 SDLInitVideoDisplay();
423 void CloseVideoDisplay(void)
425 KeyboardAutoRepeatOn();
427 #if defined(TARGET_SDL)
428 SDL_QuitSubSystem(SDL_INIT_VIDEO);
431 XCloseDisplay(display);
435 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
438 printf("::: InitVideoBuffer\n");
442 video.height = height;
443 video.depth = GetRealDepth(depth);
445 video.fullscreen_available = FULLSCREEN_STATUS;
446 video.fullscreen_enabled = FALSE;
447 // video.fullscreen_initial = FALSE;
449 video.fullscreen_mode_current = NULL;
450 video.fullscreen_modes = NULL;
453 video.window_scaling_available = WINDOW_SCALING_STATUS;
455 SDLInitVideoBuffer(&backbuffer, &window, fullscreen);
460 inline static void FreeBitmapPointers(Bitmap *bitmap)
465 SDLFreeBitmapPointers(bitmap);
467 checked_free(bitmap->source_filename);
468 bitmap->source_filename = NULL;
471 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
474 if (src_bitmap == NULL || dst_bitmap == NULL)
477 FreeBitmapPointers(dst_bitmap);
479 *dst_bitmap = *src_bitmap;
482 void FreeBitmap(Bitmap *bitmap)
487 FreeBitmapPointers(bitmap);
492 Bitmap *CreateBitmapStruct(void)
494 return checked_calloc(sizeof(struct SDLSurfaceInfo));
497 Bitmap *CreateBitmap(int width, int height, int depth)
499 Bitmap *new_bitmap = CreateBitmapStruct();
500 int real_width = MAX(1, width); /* prevent zero bitmap width */
501 int real_height = MAX(1, height); /* prevent zero bitmap height */
502 int real_depth = GetRealDepth(depth);
504 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
506 new_bitmap->width = real_width;
507 new_bitmap->height = real_height;
512 void ReCreateBitmap(Bitmap **bitmap, int width, int height, int depth)
514 Bitmap *new_bitmap = CreateBitmap(width, height, depth);
518 *bitmap = new_bitmap;
522 TransferBitmapPointers(new_bitmap, *bitmap);
527 void CloseWindow(DrawWindow *window)
531 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
534 if (draw_mask == REDRAW_NONE)
537 if (draw_mask & REDRAW_ALL)
541 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
544 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
547 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
550 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
553 if ((draw_mask & REDRAW_FIELD) &&
554 x >= gfx.real_sx && x < gfx.real_sx + gfx.full_sxsize)
557 if ((draw_mask & REDRAW_DOOR_1) &&
558 x >= gfx.dx && y < gfx.dy + gfx.dysize)
561 if ((draw_mask & REDRAW_DOOR_2) &&
562 x >= gfx.dx && y >= gfx.vy)
569 boolean DrawingDeactivated(int x, int y, int width, int height)
571 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
574 boolean DrawingOnBackground(int x, int y)
576 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
577 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
580 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
581 int *width, int *height, boolean is_dest)
584 int clip_x, clip_y, clip_width, clip_height;
586 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
588 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
589 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
590 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
591 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
597 clip_width = bitmap->width;
598 clip_height = bitmap->height;
601 /* skip if rectangle completely outside bitmap */
603 if (*x + *width <= clip_x ||
604 *y + *height <= clip_y ||
605 *x >= clip_x + clip_width ||
606 *y >= clip_y + clip_height)
609 /* clip if rectangle overlaps bitmap */
613 *width -= clip_x - *x;
616 else if (*x + *width > clip_x + clip_width)
618 *width = clip_x + clip_width - *x;
623 *height -= clip_y - *y;
626 else if (*y + *height > clip_y + clip_height)
628 *height = clip_y + clip_height - *y;
635 /* skip if rectangle completely outside bitmap */
637 if (*x + *width <= 0 ||
639 *x >= bitmap->width ||
640 *y >= bitmap->height)
643 /* clip if rectangle overlaps bitmap */
650 else if (*x + *width > bitmap->width)
652 *width = bitmap->width - *x;
660 else if (*y + *height > bitmap->height)
662 *height = bitmap->height - *y;
669 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
670 int src_x, int src_y, int width, int height,
671 int dst_x, int dst_y)
673 int dst_x_unclipped = dst_x;
674 int dst_y_unclipped = dst_y;
676 if (src_bitmap == NULL || dst_bitmap == NULL)
679 if (DrawingDeactivated(dst_x, dst_y, width, height))
683 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
684 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
687 /* source x/y might need adjustment if destination x/y was clipped top/left */
688 src_x += dst_x - dst_x_unclipped;
689 src_y += dst_y - dst_y_unclipped;
692 /* skip if rectangle starts outside bitmap */
693 if (src_x >= src_bitmap->width ||
694 src_y >= src_bitmap->height ||
695 dst_x >= dst_bitmap->width ||
696 dst_y >= dst_bitmap->height)
699 /* clip if rectangle overlaps bitmap */
700 if (src_x + width > src_bitmap->width)
701 width = src_bitmap->width - src_x;
702 if (src_y + height > src_bitmap->height)
703 height = src_bitmap->height - src_y;
704 if (dst_x + width > dst_bitmap->width)
705 width = dst_bitmap->width - dst_x;
706 if (dst_y + height > dst_bitmap->height)
707 height = dst_bitmap->height - dst_y;
711 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
713 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
714 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
715 but is already fixed in SVN and should therefore finally be fixed with
716 the next official SDL release, which is probably version 1.2.14.) */
718 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
719 //#if defined(TARGET_SDL) && defined(PLATFORM_WIN32)
720 #if defined(TARGET_SDL2)
721 if (src_bitmap == dst_bitmap)
723 /* !!! THIS IS A BUG (IN THE SDL LIBRARY?) AND SHOULD BE FIXED !!! */
725 /* needed when blitting directly to same bitmap -- should not be needed with
726 recent SDL libraries, but apparently does not work in 1.2.11 directly */
728 static Bitmap *tmp_bitmap = NULL;
729 static int tmp_bitmap_xsize = 0;
730 static int tmp_bitmap_ysize = 0;
732 /* start with largest static bitmaps for initial bitmap size ... */
733 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
735 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
736 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
739 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
740 if (src_bitmap->width > tmp_bitmap_xsize ||
741 src_bitmap->height > tmp_bitmap_ysize)
743 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
744 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
746 FreeBitmap(tmp_bitmap);
751 if (tmp_bitmap == NULL)
752 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
755 sysCopyArea(src_bitmap, tmp_bitmap,
756 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
757 sysCopyArea(tmp_bitmap, dst_bitmap,
758 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
768 if (dst_x < gfx.sx + gfx.sxsize)
769 printf("::: %d: BlitBitmap(%d, %d, %d, %d)\n",
770 FrameCounter, dst_x, dst_y, width, height);
773 sysCopyArea(src_bitmap, dst_bitmap,
774 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
777 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
778 int src_x, int src_y, int src_width, int src_height,
779 int dst_x, int dst_y, int dst_width, int dst_height)
781 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
782 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
783 int dst_xsize = dst_width;
784 int dst_ysize = dst_height;
785 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
786 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
789 for (y = 0; y < src_ysteps; y++)
791 for (x = 0; x < src_xsteps; x++)
793 int draw_x = dst_x + x * src_xsize;
794 int draw_y = dst_y + y * src_ysize;
795 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
796 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
798 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
804 void FadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
805 int fade_mode, int fade_delay, int post_delay,
806 void (*draw_border_function)(void))
809 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
810 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
814 SDLFadeRectangle(bitmap_cross, x, y, width, height,
815 fade_mode, fade_delay, post_delay, draw_border_function);
818 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
821 if (DrawingDeactivated(x, y, width, height))
825 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
828 /* skip if rectangle starts outside bitmap */
829 if (x >= bitmap->width ||
833 /* clip if rectangle overlaps bitmap */
834 if (x + width > bitmap->width)
835 width = bitmap->width - x;
836 if (y + height > bitmap->height)
837 height = bitmap->height - y;
840 sysFillRectangle(bitmap, x, y, width, height, color);
843 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
845 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
848 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
849 int width, int height)
851 if (DrawingOnBackground(x, y))
852 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
854 ClearRectangle(bitmap, x, y, width, height);
857 void SetClipMask(Bitmap *bitmap, GC clip_gc, Pixmap clip_pixmap)
861 void SetClipOrigin(Bitmap *bitmap, GC clip_gc, int clip_x, int clip_y)
865 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
866 int src_x, int src_y, int width, int height,
867 int dst_x, int dst_y)
869 if (DrawingDeactivated(dst_x, dst_y, width, height))
872 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
873 dst_x, dst_y, BLIT_MASKED);
876 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
877 int src_x, int src_y, int width, int height,
878 int dst_x, int dst_y)
880 if (DrawingOnBackground(dst_x, dst_y))
882 /* draw background */
883 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
886 /* draw foreground */
887 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
888 dst_x - src_x, dst_y - src_y);
889 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
893 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
897 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
900 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
903 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
906 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
909 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
910 int to_x, int to_y, Pixel pixel, int line_width)
914 for (x = 0; x < line_width; x++)
916 for (y = 0; y < line_width; y++)
918 int dx = x - line_width / 2;
919 int dy = y - line_width / 2;
921 if ((x == 0 && y == 0) ||
922 (x == 0 && y == line_width - 1) ||
923 (x == line_width - 1 && y == 0) ||
924 (x == line_width - 1 && y == line_width - 1))
928 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
933 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
938 for (i = 0; i < num_points - 1; i++)
939 DrawLine(bitmap, points[i].x, points[i].y,
940 points[i + 1].x, points[i + 1].y, pixel, line_width);
943 SDLDrawLines(bitmap->surface, points, num_points, pixel);
947 Pixel GetPixel(Bitmap *bitmap, int x, int y)
949 if (x < 0 || x >= bitmap->width ||
950 y < 0 || y >= bitmap->height)
953 return SDLGetPixel(bitmap, x, y);
956 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
957 unsigned int color_g, unsigned int color_b)
959 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
962 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
964 unsigned int color_r = (color >> 16) & 0xff;
965 unsigned int color_g = (color >> 8) & 0xff;
966 unsigned int color_b = (color >> 0) & 0xff;
968 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
971 /* execute all pending screen drawing operations */
972 void FlushDisplay(void)
976 /* execute and wait for all pending screen drawing operations */
977 void SyncDisplay(void)
981 void KeyboardAutoRepeatOn(void)
983 #if defined(TARGET_SDL)
984 #if defined(TARGET_SDL2)
985 keyrepeat_status = TRUE;
987 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
988 SDL_DEFAULT_REPEAT_INTERVAL / 2);
989 SDL_EnableUNICODE(1);
993 XAutoRepeatOn(display);
997 void KeyboardAutoRepeatOff(void)
999 #if defined(TARGET_SDL)
1000 #if defined(TARGET_SDL2)
1001 keyrepeat_status = FALSE;
1003 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
1004 SDL_EnableUNICODE(0);
1008 XAutoRepeatOff(display);
1012 boolean PointerInWindow(DrawWindow *window)
1014 #if defined(TARGET_SDL)
1022 /* if XQueryPointer() returns False, the pointer
1023 is not on the same screen as the specified window */
1024 return XQueryPointer(display, window->drawable, &root, &child,
1025 &root_x, &root_y, &win_x, &win_y, &mask);
1029 boolean SetVideoMode(boolean fullscreen)
1031 return SDLSetVideoMode(&backbuffer, fullscreen);
1034 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
1036 #if defined(TARGET_SDL)
1037 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
1038 (!fullscreen && video.fullscreen_enabled))
1039 fullscreen = SetVideoMode(fullscreen);
1045 Bitmap *LoadImage(char *filename)
1049 new_bitmap = SDLLoadImage(filename);
1052 new_bitmap->source_filename = getStringCopy(filename);
1057 Bitmap *LoadCustomImage(char *basename)
1059 char *filename = getCustomImageFilename(basename);
1062 if (filename == NULL)
1063 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
1065 if ((new_bitmap = LoadImage(filename)) == NULL)
1066 Error(ERR_EXIT, "LoadImage() failed: %s", GetError());
1071 void ReloadCustomImage(Bitmap *bitmap, char *basename)
1073 char *filename = getCustomImageFilename(basename);
1076 if (filename == NULL) /* (should never happen) */
1078 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
1082 if (strEqual(filename, bitmap->source_filename))
1084 /* The old and new image are the same (have the same filename and path).
1085 This usually means that this image does not exist in this graphic set
1086 and a fallback to the existing image is done. */
1091 if ((new_bitmap = LoadImage(filename)) == NULL)
1093 Error(ERR_WARN, "LoadImage() failed: %s", GetError());
1097 if (bitmap->width != new_bitmap->width ||
1098 bitmap->height != new_bitmap->height)
1100 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
1102 FreeBitmap(new_bitmap);
1106 TransferBitmapPointers(new_bitmap, bitmap);
1110 Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
1112 Bitmap *dst_bitmap = CreateBitmap(zoom_width, zoom_height, DEFAULT_DEPTH);
1114 SDLZoomBitmap(src_bitmap, dst_bitmap);
1119 static void CreateScaledBitmaps(Bitmap *old_bitmap, int zoom_factor,
1120 boolean create_small_bitmaps)
1124 Bitmap *tmp_bitmap_1;
1125 Bitmap *tmp_bitmap_2;
1126 Bitmap *tmp_bitmap_4;
1127 Bitmap *tmp_bitmap_8;
1128 Bitmap *tmp_bitmap_16;
1129 Bitmap *tmp_bitmap_32;
1130 int width_1, height_1;
1131 int width_2, height_2;
1132 int width_4, height_4;
1133 int width_8, height_8;
1134 int width_16, height_16;
1136 int width_32, height_32;
1138 int new_width, new_height;
1140 /* calculate new image dimensions for normal sized image */
1141 width_1 = old_bitmap->width * zoom_factor;
1142 height_1 = old_bitmap->height * zoom_factor;
1144 /* get image with normal size (this might require scaling up) */
1145 if (zoom_factor != 1)
1146 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1148 tmp_bitmap_1 = old_bitmap;
1150 /* this is only needed to make compilers happy */
1151 tmp_bitmap_2 = NULL;
1152 tmp_bitmap_4 = NULL;
1153 tmp_bitmap_8 = NULL;
1154 tmp_bitmap_16 = NULL;
1155 tmp_bitmap_32 = NULL;
1157 if (create_small_bitmaps)
1159 /* calculate new image dimensions for small images */
1160 width_2 = width_1 / 2;
1161 height_2 = height_1 / 2;
1162 width_4 = width_1 / 4;
1163 height_4 = height_1 / 4;
1164 width_8 = width_1 / 8;
1165 height_8 = height_1 / 8;
1166 width_16 = width_1 / 16;
1167 height_16 = height_1 / 16;
1169 width_32 = width_1 / 32;
1170 height_32 = height_1 / 32;
1173 UPDATE_BUSY_STATE();
1175 /* get image with 1/2 of normal size (for use in the level editor) */
1176 if (zoom_factor != 2)
1177 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_1 / 2, height_1 / 2);
1179 tmp_bitmap_2 = old_bitmap;
1181 UPDATE_BUSY_STATE();
1183 /* get image with 1/4 of normal size (for use in the level editor) */
1184 if (zoom_factor != 4)
1185 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_2 / 2, height_2 / 2);
1187 tmp_bitmap_4 = old_bitmap;
1189 UPDATE_BUSY_STATE();
1191 /* get image with 1/8 of normal size (for use on the preview screen) */
1192 if (zoom_factor != 8)
1193 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_4 / 2, height_4 / 2);
1195 tmp_bitmap_8 = old_bitmap;
1197 UPDATE_BUSY_STATE();
1199 /* get image with 1/16 of normal size (for use on the preview screen) */
1200 if (zoom_factor != 16)
1201 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_8 / 2, height_8 / 2);
1203 tmp_bitmap_16 = old_bitmap;
1205 UPDATE_BUSY_STATE();
1207 /* get image with 1/32 of normal size (for use on the preview screen) */
1208 if (zoom_factor != 32)
1209 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_16 / 2, height_16 / 2);
1211 tmp_bitmap_32 = old_bitmap;
1213 UPDATE_BUSY_STATE();
1217 /* if image was scaled up, create new clipmask for normal size image */
1218 if (zoom_factor != 1)
1220 SDL_Surface *tmp_surface_1 = tmp_bitmap_1->surface;
1222 if (old_bitmap->surface_masked)
1223 SDL_FreeSurface(old_bitmap->surface_masked);
1225 SDL_SetColorKey(tmp_surface_1, SET_TRANSPARENT_PIXEL,
1226 SDL_MapRGB(tmp_surface_1->format, 0x00, 0x00, 0x00));
1227 if ((old_bitmap->surface_masked = SDL_DisplayFormat(tmp_surface_1)) ==NULL)
1228 Error(ERR_EXIT, "SDL_DisplayFormat() failed");
1229 SDL_SetColorKey(tmp_surface_1, UNSET_TRANSPARENT_PIXEL, 0);
1233 if (create_small_bitmaps)
1235 new_width = width_1;
1236 new_height = height_1 + (height_1 + 1) / 2; /* prevent odd height */
1238 new_bitmap = CreateBitmap(new_width, new_height, DEFAULT_DEPTH);
1240 BlitBitmap(tmp_bitmap_1, new_bitmap, 0, 0, width_1, height_1, 0, 0);
1241 BlitBitmap(tmp_bitmap_2, new_bitmap, 0, 0, width_1 / 2, height_1 / 2,
1243 BlitBitmap(tmp_bitmap_4, new_bitmap, 0, 0, width_1 / 4, height_1 / 4,
1244 width_1 / 2, height_1);
1245 BlitBitmap(tmp_bitmap_8, new_bitmap, 0, 0, width_1 / 8, height_1 / 8,
1246 3 * width_1 / 4, height_1);
1247 BlitBitmap(tmp_bitmap_16, new_bitmap, 0, 0, width_1 / 16, height_1 / 16,
1248 7 * width_1 / 8, height_1);
1249 BlitBitmap(tmp_bitmap_32, new_bitmap, 0, 0, width_1 / 32, height_1 / 32,
1250 15 * width_1 / 16, height_1);
1252 UPDATE_BUSY_STATE();
1256 new_width = width_1;
1257 new_height = height_1;
1259 new_bitmap = tmp_bitmap_1; /* directly use tmp_bitmap_1 as new bitmap */
1262 if (create_small_bitmaps)
1264 /* if no small bitmaps created, tmp_bitmap_1 is used as new bitmap now */
1265 if (zoom_factor != 1)
1266 FreeBitmap(tmp_bitmap_1);
1268 if (zoom_factor != 2)
1269 FreeBitmap(tmp_bitmap_2);
1271 if (zoom_factor != 4)
1272 FreeBitmap(tmp_bitmap_4);
1274 if (zoom_factor != 8)
1275 FreeBitmap(tmp_bitmap_8);
1277 if (zoom_factor != 16)
1278 FreeBitmap(tmp_bitmap_16);
1280 if (zoom_factor != 32)
1281 FreeBitmap(tmp_bitmap_32);
1284 /* replace image with extended image (containing 1/1, 1/2, 1/4, 1/8 size) */
1285 #if defined(TARGET_SDL)
1286 swap_bitmap.surface = old_bitmap->surface;
1287 old_bitmap->surface = new_bitmap->surface;
1288 new_bitmap->surface = swap_bitmap.surface;
1290 swap_bitmap.drawable = old_bitmap->drawable;
1291 old_bitmap->drawable = new_bitmap->drawable;
1292 new_bitmap->drawable = swap_bitmap.drawable;
1295 old_bitmap->width = new_bitmap->width;
1296 old_bitmap->height = new_bitmap->height;
1299 /* this replaces all blit masks created when loading -- maybe optimize this */
1301 SDL_Surface *old_surface = old_bitmap->surface;
1303 if (old_bitmap->surface_masked)
1304 SDL_FreeSurface(old_bitmap->surface_masked);
1306 SDL_SetColorKey(old_surface, SET_TRANSPARENT_PIXEL,
1307 SDL_MapRGB(old_surface->format, 0x00, 0x00, 0x00));
1308 if ((old_bitmap->surface_masked = SDL_DisplayFormat(old_surface)) ==NULL)
1309 Error(ERR_EXIT, "SDL_DisplayFormat() failed");
1310 SDL_SetColorKey(old_surface, UNSET_TRANSPARENT_PIXEL, 0);
1314 UPDATE_BUSY_STATE();
1316 FreeBitmap(new_bitmap); /* this actually frees the _old_ bitmap now */
1319 void CreateBitmapWithSmallBitmaps(Bitmap *old_bitmap, int zoom_factor)
1321 CreateScaledBitmaps(old_bitmap, zoom_factor, TRUE);
1324 void ScaleBitmap(Bitmap *old_bitmap, int zoom_factor)
1326 CreateScaledBitmaps(old_bitmap, zoom_factor, FALSE);
1330 /* ------------------------------------------------------------------------- */
1331 /* mouse pointer functions */
1332 /* ------------------------------------------------------------------------- */
1334 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1336 /* XPM image definitions */
1337 static const char *cursor_image_none[] =
1339 /* width height num_colors chars_per_pixel */
1369 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1370 static const char *cursor_image_dot[] =
1372 /* width height num_colors chars_per_pixel */
1401 static const char **cursor_image_playfield = cursor_image_dot;
1403 /* some people complained about a "white dot" on the screen and thought it
1404 was a graphical error... OK, let's just remove the whole pointer :-) */
1405 static const char **cursor_image_playfield = cursor_image_none;
1408 static const int cursor_bit_order = BIT_ORDER_MSB;
1410 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1412 struct MouseCursorInfo *cursor;
1413 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1414 int header_lines = 4;
1417 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1419 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1422 for (y = 0; y < cursor->width; y++)
1424 for (x = 0; x < cursor->height; x++)
1427 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1432 cursor->data[i] = cursor->mask[i] = 0;
1435 switch (image[header_lines + y][x])
1438 cursor->data[i] |= bit_mask;
1439 cursor->mask[i] |= bit_mask;
1443 cursor->mask[i] |= bit_mask;
1452 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1457 void SetMouseCursor(int mode)
1459 static struct MouseCursorInfo *cursor_none = NULL;
1460 static struct MouseCursorInfo *cursor_playfield = NULL;
1461 struct MouseCursorInfo *cursor_new;
1463 if (cursor_none == NULL)
1464 cursor_none = get_cursor_from_image(cursor_image_none);
1466 if (cursor_playfield == NULL)
1467 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1469 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1470 mode == CURSOR_NONE ? cursor_none :
1471 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1473 SDLSetMouseCursor(cursor_new);
1477 /* ========================================================================= */
1478 /* audio functions */
1479 /* ========================================================================= */
1481 void OpenAudio(void)
1483 /* always start with reliable default values */
1484 audio.sound_available = FALSE;
1485 audio.music_available = FALSE;
1486 audio.loops_available = FALSE;
1488 audio.sound_enabled = FALSE;
1489 audio.sound_deactivated = FALSE;
1491 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1492 audio.mixer_pid = 0;
1493 audio.device_name = NULL;
1494 audio.device_fd = -1;
1496 audio.num_channels = 0;
1497 audio.music_channel = 0;
1498 audio.first_sound_channel = 0;
1500 #if defined(TARGET_SDL)
1505 void CloseAudio(void)
1507 #if defined(TARGET_SDL)
1511 audio.sound_enabled = FALSE;
1514 void SetAudioMode(boolean enabled)
1516 if (!audio.sound_available)
1519 audio.sound_enabled = enabled;
1523 /* ========================================================================= */
1524 /* event functions */
1525 /* ========================================================================= */
1527 void InitEventFilter(EventFilter filter_function)
1529 /* set event filter to filter out certain events */
1530 #if defined(TARGET_SDL)
1531 #if defined(TARGET_SDL2)
1532 SDL_SetEventFilter(filter_function, NULL);
1534 SDL_SetEventFilter(filter_function);
1539 boolean PendingEvent(void)
1541 #if defined(TARGET_SDL)
1542 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1544 return (XPending(display) ? TRUE : FALSE);
1548 void NextEvent(Event *event)
1550 #if defined(TARGET_SDL)
1551 SDLNextEvent(event);
1553 XNextEvent(display, event);
1557 void PeekEvent(Event *event)
1559 #if defined(TARGET_SDL)
1560 #if defined(TARGET_SDL2)
1561 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1563 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1566 XPeekEvent(display, event);
1570 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1572 #if defined(TARGET_SDL)
1573 #if defined(TARGET_SDL2)
1574 /* key up/down events in SDL2 do not return text characters anymore */
1575 return event->keysym.sym;
1579 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1580 (int)event->keysym.unicode,
1581 (int)event->keysym.sym,
1582 (int)SDL_GetModState());
1585 if (with_modifiers &&
1586 event->keysym.unicode > 0x0000 &&
1587 event->keysym.unicode < 0x2000)
1588 return event->keysym.unicode;
1590 return event->keysym.sym;
1596 printf("with modifiers == '0x%04x', without modifiers == '0x%04x'\n",
1597 (int)XLookupKeysym(event, event->state),
1598 (int)XLookupKeysym(event, 0));
1602 return XLookupKeysym(event, event->state);
1604 return XLookupKeysym(event, 0);
1608 KeyMod HandleKeyModState(Key key, int key_status)
1610 static KeyMod current_modifiers = KMOD_None;
1612 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1614 KeyMod new_modifier = KMOD_None;
1619 new_modifier = KMOD_Shift_L;
1622 new_modifier = KMOD_Shift_R;
1624 case KSYM_Control_L:
1625 new_modifier = KMOD_Control_L;
1627 case KSYM_Control_R:
1628 new_modifier = KMOD_Control_R;
1631 new_modifier = KMOD_Meta_L;
1634 new_modifier = KMOD_Meta_R;
1637 new_modifier = KMOD_Alt_L;
1640 new_modifier = KMOD_Alt_R;
1646 if (key_status == KEY_PRESSED)
1647 current_modifiers |= new_modifier;
1649 current_modifiers &= ~new_modifier;
1652 return current_modifiers;
1655 KeyMod GetKeyModState()
1657 #if defined(TARGET_SDL)
1658 return (KeyMod)SDL_GetModState();
1660 return HandleKeyModState(KSYM_UNDEFINED, 0);
1664 KeyMod GetKeyModStateFromEvents()
1666 /* always use key modifier state as tracked from key events (this is needed
1667 if the modifier key event was injected into the event queue, but the key
1668 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1669 query the keys as held pressed on the keyboard) -- this case is currently
1670 only used to filter out clipboard insert events from "True X-Mouse" tool */
1672 return HandleKeyModState(KSYM_UNDEFINED, 0);
1675 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1677 if (event->type != EVENT_CLIENTMESSAGE)
1680 #if defined(TARGET_SDL)
1681 return TRUE; /* the only possible message here is SDL_QUIT */
1688 /* ========================================================================= */
1689 /* joystick functions */
1690 /* ========================================================================= */
1692 void InitJoysticks()
1696 #if defined(NO_JOYSTICK)
1697 return; /* joysticks generally deactivated by compile-time directive */
1700 /* always start with reliable default values */
1701 joystick.status = JOYSTICK_NOT_AVAILABLE;
1702 for (i = 0; i < MAX_PLAYERS; i++)
1703 joystick.fd[i] = -1; /* joystick device closed */
1705 #if defined(TARGET_SDL)
1710 for (i = 0; i < MAX_PLAYERS; i++)
1711 printf("::: Joystick for player %d: %d\n", i, joystick.fd[i]);
1715 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1717 #if defined(TARGET_SDL)
1718 return SDLReadJoystick(nr, x, y, b1, b2);