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);
97 program.error_filename = getErrorFilename(ERROR_BASENAME);
98 program.error_file = stderr;
101 void SetWindowTitle()
103 program.window_title = program.window_title_function();
105 #if defined(TARGET_SDL)
110 void InitWindowTitleFunction(char *(*window_title_function)(void))
112 program.window_title_function = window_title_function;
115 void InitExitMessageFunction(void (*exit_message_function)(char *, va_list))
117 program.exit_message_function = exit_message_function;
120 void InitExitFunction(void (*exit_function)(int))
122 program.exit_function = exit_function;
124 /* set signal handlers to custom exit function */
125 signal(SIGINT, exit_function);
126 signal(SIGTERM, exit_function);
128 #if defined(TARGET_SDL)
129 /* set exit function to automatically cleanup SDL stuff after exit() */
134 void InitPlatformDependentStuff(void)
136 // this is initialized in GetOptions(), but may already be used before
137 options.verbose = TRUE;
139 #if defined(PLATFORM_MACOSX)
140 updateUserGameDataDir();
146 #if !defined(PLATFORM_UNIX) || defined(PLATFORM_MACOSX)
151 #if defined(TARGET_SDL)
152 #if defined(TARGET_SDL2)
153 int sdl_init_flags = SDL_INIT_EVENTS | SDL_INIT_NOPARACHUTE;
155 int sdl_init_flags = SDL_INIT_EVENTTHREAD | SDL_INIT_NOPARACHUTE;
158 if (SDL_Init(sdl_init_flags) < 0)
159 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
165 void ClosePlatformDependentStuff(void)
167 #if defined(PLATFORM_WIN32)
172 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
173 int real_sx, int real_sy,
174 int full_sxsize, int full_sysize,
175 Bitmap *field_save_buffer)
181 gfx.real_sx = real_sx;
182 gfx.real_sy = real_sy;
183 gfx.full_sxsize = full_sxsize;
184 gfx.full_sysize = full_sysize;
186 gfx.field_save_buffer = field_save_buffer;
189 gfx.background_bitmap = NULL;
190 gfx.background_bitmap_mask = REDRAW_NONE;
193 SetDrawDeactivationMask(REDRAW_NONE); /* do not deactivate drawing */
194 SetDrawBackgroundMask(REDRAW_NONE); /* deactivate masked drawing */
197 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
205 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
213 void InitGfxDoor3Info(int ex, int ey, int exsize, int eysize)
221 void InitGfxWindowInfo(int win_xsize, int win_ysize)
223 gfx.win_xsize = win_xsize;
224 gfx.win_ysize = win_ysize;
227 gfx.background_bitmap_mask = REDRAW_NONE;
229 ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize, DEFAULT_DEPTH);
233 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
235 /* currently only used by MSDOS code to alloc VRAM buffer, if available */
236 /* 2009-03-24: also (temporarily?) used for overlapping blit workaround */
237 gfx.scrollbuffer_width = scrollbuffer_width;
238 gfx.scrollbuffer_height = scrollbuffer_height;
241 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
243 gfx.clipping_enabled = enabled;
246 gfx.clip_width = width;
247 gfx.clip_height = height;
250 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
252 gfx.draw_busy_anim_function = draw_busy_anim_function;
255 void InitGfxCustomArtworkInfo()
257 gfx.override_level_graphics = FALSE;
258 gfx.override_level_sounds = FALSE;
259 gfx.override_level_music = FALSE;
261 gfx.draw_init_text = TRUE;
264 void SetDrawDeactivationMask(int draw_deactivation_mask)
266 gfx.draw_deactivation_mask = draw_deactivation_mask;
269 void SetDrawBackgroundMask(int draw_background_mask)
271 gfx.draw_background_mask = draw_background_mask;
276 static void DrawBitmapFromTile(Bitmap *bitmap, Bitmap *tile,
277 int dest_x, int dest_y, int width, int height)
279 int bitmap_xsize = width;
280 int bitmap_ysize = height;
281 int tile_xsize = tile->width;
282 int tile_ysize = tile->height;
283 int tile_xsteps = (bitmap_xsize + tile_xsize - 1) / tile_xsize;
284 int tile_ysteps = (bitmap_ysize + tile_ysize - 1) / tile_ysize;
287 for (y = 0; y < tile_ysteps; y++)
289 for (x = 0; x < tile_xsteps; x++)
291 int draw_x = dest_x + x * tile_xsize;
292 int draw_y = dest_y + y * tile_ysize;
293 int draw_xsize = MIN(tile_xsize, bitmap_xsize - x * tile_xsize);
294 int draw_ysize = MIN(tile_ysize, bitmap_ysize - y * tile_ysize);
296 BlitBitmap(tile, bitmap, 0, 0, draw_xsize, draw_ysize, draw_x, draw_y);
301 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
303 if (background_bitmap_tile != NULL)
304 gfx.background_bitmap_mask |= mask;
306 gfx.background_bitmap_mask &= ~mask;
309 if (gfx.background_bitmap == NULL)
310 gfx.background_bitmap = CreateBitmap(video.width, video.height,
314 if (background_bitmap_tile == NULL) /* empty background requested */
317 if (mask == REDRAW_ALL)
318 DrawBitmapFromTile(gfx.background_bitmap, background_bitmap_tile,
319 0, 0, video.width, video.height);
320 else if (mask == REDRAW_FIELD)
321 DrawBitmapFromTile(gfx.background_bitmap, background_bitmap_tile,
322 gfx.real_sx, gfx.real_sy,
323 gfx.full_sxsize, gfx.full_sysize);
324 else if (mask == REDRAW_DOOR_1)
325 DrawBitmapFromTile(gfx.background_bitmap, background_bitmap_tile,
327 gfx.dxsize, gfx.dysize);
332 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
334 if (background_bitmap_tile != NULL)
335 gfx.background_bitmap_mask |= mask;
337 gfx.background_bitmap_mask &= ~mask;
340 if (gfx.background_bitmap == NULL)
341 gfx.background_bitmap = CreateBitmap(video.width, video.height,
345 if (background_bitmap_tile == NULL) /* empty background requested */
348 if (mask == REDRAW_ALL)
349 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
350 0, 0, video.width, video.height);
351 else if (mask == REDRAW_FIELD)
352 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
353 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
354 else if (mask == REDRAW_DOOR_1)
355 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
356 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
361 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
363 /* remove every mask before setting mask for window */
364 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
365 SetBackgroundBitmap(NULL, 0xffff); /* !!! FIX THIS !!! */
366 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
369 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
371 /* remove window area mask before setting mask for main area */
372 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
373 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
374 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
377 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
379 /* remove window area mask before setting mask for door area */
380 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
381 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
382 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
386 /* ========================================================================= */
387 /* video functions */
388 /* ========================================================================= */
390 inline static int GetRealDepth(int depth)
392 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
395 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
396 int width, int height, Pixel color)
398 SDLFillRectangle(bitmap, x, y, width, height, color);
401 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
402 int src_x, int src_y, int width, int height,
403 int dst_x, int dst_y, int mask_mode)
405 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
406 dst_x, dst_y, mask_mode);
409 void LimitScreenUpdates(boolean enable)
411 #if defined(TARGET_SDL)
412 SDLLimitScreenUpdates(enable);
416 void InitVideoDisplay(void)
418 SDLInitVideoDisplay();
421 void CloseVideoDisplay(void)
423 KeyboardAutoRepeatOn();
425 #if defined(TARGET_SDL)
426 SDL_QuitSubSystem(SDL_INIT_VIDEO);
429 XCloseDisplay(display);
433 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
436 printf("::: InitVideoBuffer\n");
440 video.height = height;
441 video.depth = GetRealDepth(depth);
443 video.fullscreen_available = FULLSCREEN_STATUS;
444 video.fullscreen_enabled = FALSE;
445 // video.fullscreen_initial = FALSE;
447 video.fullscreen_mode_current = NULL;
448 video.fullscreen_modes = NULL;
451 video.window_scaling_available = WINDOW_SCALING_STATUS;
453 SDLInitVideoBuffer(&backbuffer, &window, fullscreen);
458 inline static void FreeBitmapPointers(Bitmap *bitmap)
463 SDLFreeBitmapPointers(bitmap);
465 checked_free(bitmap->source_filename);
466 bitmap->source_filename = NULL;
469 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
472 if (src_bitmap == NULL || dst_bitmap == NULL)
475 FreeBitmapPointers(dst_bitmap);
477 *dst_bitmap = *src_bitmap;
480 void FreeBitmap(Bitmap *bitmap)
485 FreeBitmapPointers(bitmap);
490 Bitmap *CreateBitmapStruct(void)
492 return checked_calloc(sizeof(struct SDLSurfaceInfo));
495 Bitmap *CreateBitmap(int width, int height, int depth)
497 Bitmap *new_bitmap = CreateBitmapStruct();
498 int real_width = MAX(1, width); /* prevent zero bitmap width */
499 int real_height = MAX(1, height); /* prevent zero bitmap height */
500 int real_depth = GetRealDepth(depth);
502 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
504 new_bitmap->width = real_width;
505 new_bitmap->height = real_height;
510 void ReCreateBitmap(Bitmap **bitmap, int width, int height, int depth)
512 Bitmap *new_bitmap = CreateBitmap(width, height, depth);
516 *bitmap = new_bitmap;
520 TransferBitmapPointers(new_bitmap, *bitmap);
525 void CloseWindow(DrawWindow *window)
529 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
532 if (draw_mask == REDRAW_NONE)
535 if (draw_mask & REDRAW_ALL)
539 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
542 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
545 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
548 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
551 if ((draw_mask & REDRAW_FIELD) &&
552 x >= gfx.real_sx && x < gfx.real_sx + gfx.full_sxsize)
555 if ((draw_mask & REDRAW_DOOR_1) &&
556 x >= gfx.dx && y < gfx.dy + gfx.dysize)
559 if ((draw_mask & REDRAW_DOOR_2) &&
560 x >= gfx.dx && y >= gfx.vy)
567 boolean DrawingDeactivated(int x, int y, int width, int height)
569 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
572 boolean DrawingOnBackground(int x, int y)
574 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
575 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
578 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
579 int *width, int *height, boolean is_dest)
582 int clip_x, clip_y, clip_width, clip_height;
584 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
586 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
587 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
588 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
589 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
595 clip_width = bitmap->width;
596 clip_height = bitmap->height;
599 /* skip if rectangle completely outside bitmap */
601 if (*x + *width <= clip_x ||
602 *y + *height <= clip_y ||
603 *x >= clip_x + clip_width ||
604 *y >= clip_y + clip_height)
607 /* clip if rectangle overlaps bitmap */
611 *width -= clip_x - *x;
614 else if (*x + *width > clip_x + clip_width)
616 *width = clip_x + clip_width - *x;
621 *height -= clip_y - *y;
624 else if (*y + *height > clip_y + clip_height)
626 *height = clip_y + clip_height - *y;
633 /* skip if rectangle completely outside bitmap */
635 if (*x + *width <= 0 ||
637 *x >= bitmap->width ||
638 *y >= bitmap->height)
641 /* clip if rectangle overlaps bitmap */
648 else if (*x + *width > bitmap->width)
650 *width = bitmap->width - *x;
658 else if (*y + *height > bitmap->height)
660 *height = bitmap->height - *y;
667 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
668 int src_x, int src_y, int width, int height,
669 int dst_x, int dst_y)
671 int dst_x_unclipped = dst_x;
672 int dst_y_unclipped = dst_y;
674 if (src_bitmap == NULL || dst_bitmap == NULL)
677 if (DrawingDeactivated(dst_x, dst_y, width, height))
681 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
682 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
685 /* source x/y might need adjustment if destination x/y was clipped top/left */
686 src_x += dst_x - dst_x_unclipped;
687 src_y += dst_y - dst_y_unclipped;
690 /* skip if rectangle starts outside bitmap */
691 if (src_x >= src_bitmap->width ||
692 src_y >= src_bitmap->height ||
693 dst_x >= dst_bitmap->width ||
694 dst_y >= dst_bitmap->height)
697 /* clip if rectangle overlaps bitmap */
698 if (src_x + width > src_bitmap->width)
699 width = src_bitmap->width - src_x;
700 if (src_y + height > src_bitmap->height)
701 height = src_bitmap->height - src_y;
702 if (dst_x + width > dst_bitmap->width)
703 width = dst_bitmap->width - dst_x;
704 if (dst_y + height > dst_bitmap->height)
705 height = dst_bitmap->height - dst_y;
709 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
711 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
712 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
713 but is already fixed in SVN and should therefore finally be fixed with
714 the next official SDL release, which is probably version 1.2.14.) */
716 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
717 //#if defined(TARGET_SDL) && defined(PLATFORM_WIN32)
718 #if defined(TARGET_SDL2)
719 if (src_bitmap == dst_bitmap)
721 /* !!! THIS IS A BUG (IN THE SDL LIBRARY?) AND SHOULD BE FIXED !!! */
723 /* needed when blitting directly to same bitmap -- should not be needed with
724 recent SDL libraries, but apparently does not work in 1.2.11 directly */
726 static Bitmap *tmp_bitmap = NULL;
727 static int tmp_bitmap_xsize = 0;
728 static int tmp_bitmap_ysize = 0;
730 /* start with largest static bitmaps for initial bitmap size ... */
731 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
733 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
734 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
737 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
738 if (src_bitmap->width > tmp_bitmap_xsize ||
739 src_bitmap->height > tmp_bitmap_ysize)
741 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
742 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
744 FreeBitmap(tmp_bitmap);
749 if (tmp_bitmap == NULL)
750 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
753 sysCopyArea(src_bitmap, tmp_bitmap,
754 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
755 sysCopyArea(tmp_bitmap, dst_bitmap,
756 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
766 if (dst_x < gfx.sx + gfx.sxsize)
767 printf("::: %d: BlitBitmap(%d, %d, %d, %d)\n",
768 FrameCounter, dst_x, dst_y, width, height);
771 sysCopyArea(src_bitmap, dst_bitmap,
772 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
775 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
776 int src_x, int src_y, int src_width, int src_height,
777 int dst_x, int dst_y, int dst_width, int dst_height)
779 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
780 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
781 int dst_xsize = dst_width;
782 int dst_ysize = dst_height;
783 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
784 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
787 for (y = 0; y < src_ysteps; y++)
789 for (x = 0; x < src_xsteps; x++)
791 int draw_x = dst_x + x * src_xsize;
792 int draw_y = dst_y + y * src_ysize;
793 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
794 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
796 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
802 void FadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
803 int fade_mode, int fade_delay, int post_delay,
804 void (*draw_border_function)(void))
807 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
808 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
812 SDLFadeRectangle(bitmap_cross, x, y, width, height,
813 fade_mode, fade_delay, post_delay, draw_border_function);
816 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
819 if (DrawingDeactivated(x, y, width, height))
823 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
826 /* skip if rectangle starts outside bitmap */
827 if (x >= bitmap->width ||
831 /* clip if rectangle overlaps bitmap */
832 if (x + width > bitmap->width)
833 width = bitmap->width - x;
834 if (y + height > bitmap->height)
835 height = bitmap->height - y;
838 sysFillRectangle(bitmap, x, y, width, height, color);
841 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
843 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
846 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
847 int width, int height)
849 if (DrawingOnBackground(x, y))
850 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
852 ClearRectangle(bitmap, x, y, width, height);
855 void SetClipMask(Bitmap *bitmap, GC clip_gc, Pixmap clip_pixmap)
859 void SetClipOrigin(Bitmap *bitmap, GC clip_gc, int clip_x, int clip_y)
863 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
864 int src_x, int src_y, int width, int height,
865 int dst_x, int dst_y)
867 if (DrawingDeactivated(dst_x, dst_y, width, height))
870 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
871 dst_x, dst_y, BLIT_MASKED);
874 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
875 int src_x, int src_y, int width, int height,
876 int dst_x, int dst_y)
878 if (DrawingOnBackground(dst_x, dst_y))
880 /* draw background */
881 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
884 /* draw foreground */
885 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
886 dst_x - src_x, dst_y - src_y);
887 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
891 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
895 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
898 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
901 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
904 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
907 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
908 int to_x, int to_y, Pixel pixel, int line_width)
912 for (x = 0; x < line_width; x++)
914 for (y = 0; y < line_width; y++)
916 int dx = x - line_width / 2;
917 int dy = y - line_width / 2;
919 if ((x == 0 && y == 0) ||
920 (x == 0 && y == line_width - 1) ||
921 (x == line_width - 1 && y == 0) ||
922 (x == line_width - 1 && y == line_width - 1))
926 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
931 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
936 for (i = 0; i < num_points - 1; i++)
937 DrawLine(bitmap, points[i].x, points[i].y,
938 points[i + 1].x, points[i + 1].y, pixel, line_width);
941 SDLDrawLines(bitmap->surface, points, num_points, pixel);
945 Pixel GetPixel(Bitmap *bitmap, int x, int y)
947 if (x < 0 || x >= bitmap->width ||
948 y < 0 || y >= bitmap->height)
951 return SDLGetPixel(bitmap, x, y);
954 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
955 unsigned int color_g, unsigned int color_b)
957 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
960 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
962 unsigned int color_r = (color >> 16) & 0xff;
963 unsigned int color_g = (color >> 8) & 0xff;
964 unsigned int color_b = (color >> 0) & 0xff;
966 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
969 /* execute all pending screen drawing operations */
970 void FlushDisplay(void)
974 /* execute and wait for all pending screen drawing operations */
975 void SyncDisplay(void)
979 void KeyboardAutoRepeatOn(void)
981 #if defined(TARGET_SDL)
982 #if defined(TARGET_SDL2)
983 keyrepeat_status = TRUE;
985 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
986 SDL_DEFAULT_REPEAT_INTERVAL / 2);
987 SDL_EnableUNICODE(1);
991 XAutoRepeatOn(display);
995 void KeyboardAutoRepeatOff(void)
997 #if defined(TARGET_SDL)
998 #if defined(TARGET_SDL2)
999 keyrepeat_status = FALSE;
1001 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
1002 SDL_EnableUNICODE(0);
1006 XAutoRepeatOff(display);
1010 boolean PointerInWindow(DrawWindow *window)
1012 #if defined(TARGET_SDL)
1020 /* if XQueryPointer() returns False, the pointer
1021 is not on the same screen as the specified window */
1022 return XQueryPointer(display, window->drawable, &root, &child,
1023 &root_x, &root_y, &win_x, &win_y, &mask);
1027 boolean SetVideoMode(boolean fullscreen)
1029 return SDLSetVideoMode(&backbuffer, fullscreen);
1032 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
1034 #if defined(TARGET_SDL)
1035 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
1036 (!fullscreen && video.fullscreen_enabled))
1037 fullscreen = SetVideoMode(fullscreen);
1043 Bitmap *LoadImage(char *filename)
1047 new_bitmap = SDLLoadImage(filename);
1050 new_bitmap->source_filename = getStringCopy(filename);
1055 Bitmap *LoadCustomImage(char *basename)
1057 char *filename = getCustomImageFilename(basename);
1060 if (filename == NULL)
1061 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
1063 if ((new_bitmap = LoadImage(filename)) == NULL)
1064 Error(ERR_EXIT, "LoadImage() failed: %s", GetError());
1069 void ReloadCustomImage(Bitmap *bitmap, char *basename)
1071 char *filename = getCustomImageFilename(basename);
1074 if (filename == NULL) /* (should never happen) */
1076 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
1080 if (strEqual(filename, bitmap->source_filename))
1082 /* The old and new image are the same (have the same filename and path).
1083 This usually means that this image does not exist in this graphic set
1084 and a fallback to the existing image is done. */
1089 if ((new_bitmap = LoadImage(filename)) == NULL)
1091 Error(ERR_WARN, "LoadImage() failed: %s", GetError());
1095 if (bitmap->width != new_bitmap->width ||
1096 bitmap->height != new_bitmap->height)
1098 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
1100 FreeBitmap(new_bitmap);
1104 TransferBitmapPointers(new_bitmap, bitmap);
1108 Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
1110 Bitmap *dst_bitmap = CreateBitmap(zoom_width, zoom_height, DEFAULT_DEPTH);
1112 SDLZoomBitmap(src_bitmap, dst_bitmap);
1117 static void CreateScaledBitmaps(Bitmap *old_bitmap, int zoom_factor,
1118 boolean create_small_bitmaps)
1122 Bitmap *tmp_bitmap_1;
1123 Bitmap *tmp_bitmap_2;
1124 Bitmap *tmp_bitmap_4;
1125 Bitmap *tmp_bitmap_8;
1126 Bitmap *tmp_bitmap_16;
1127 Bitmap *tmp_bitmap_32;
1128 int width_1, height_1;
1129 int width_2, height_2;
1130 int width_4, height_4;
1131 int width_8, height_8;
1132 int width_16, height_16;
1134 int width_32, height_32;
1136 int new_width, new_height;
1138 /* calculate new image dimensions for normal sized image */
1139 width_1 = old_bitmap->width * zoom_factor;
1140 height_1 = old_bitmap->height * zoom_factor;
1142 /* get image with normal size (this might require scaling up) */
1143 if (zoom_factor != 1)
1144 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1146 tmp_bitmap_1 = old_bitmap;
1148 /* this is only needed to make compilers happy */
1149 tmp_bitmap_2 = NULL;
1150 tmp_bitmap_4 = NULL;
1151 tmp_bitmap_8 = NULL;
1152 tmp_bitmap_16 = NULL;
1153 tmp_bitmap_32 = NULL;
1155 if (create_small_bitmaps)
1157 /* calculate new image dimensions for small images */
1158 width_2 = width_1 / 2;
1159 height_2 = height_1 / 2;
1160 width_4 = width_1 / 4;
1161 height_4 = height_1 / 4;
1162 width_8 = width_1 / 8;
1163 height_8 = height_1 / 8;
1164 width_16 = width_1 / 16;
1165 height_16 = height_1 / 16;
1167 width_32 = width_1 / 32;
1168 height_32 = height_1 / 32;
1171 UPDATE_BUSY_STATE();
1173 /* get image with 1/2 of normal size (for use in the level editor) */
1174 if (zoom_factor != 2)
1175 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_1 / 2, height_1 / 2);
1177 tmp_bitmap_2 = old_bitmap;
1179 UPDATE_BUSY_STATE();
1181 /* get image with 1/4 of normal size (for use in the level editor) */
1182 if (zoom_factor != 4)
1183 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_2 / 2, height_2 / 2);
1185 tmp_bitmap_4 = old_bitmap;
1187 UPDATE_BUSY_STATE();
1189 /* get image with 1/8 of normal size (for use on the preview screen) */
1190 if (zoom_factor != 8)
1191 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_4 / 2, height_4 / 2);
1193 tmp_bitmap_8 = old_bitmap;
1195 UPDATE_BUSY_STATE();
1197 /* get image with 1/16 of normal size (for use on the preview screen) */
1198 if (zoom_factor != 16)
1199 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_8 / 2, height_8 / 2);
1201 tmp_bitmap_16 = old_bitmap;
1203 UPDATE_BUSY_STATE();
1205 /* get image with 1/32 of normal size (for use on the preview screen) */
1206 if (zoom_factor != 32)
1207 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_16 / 2, height_16 / 2);
1209 tmp_bitmap_32 = old_bitmap;
1211 UPDATE_BUSY_STATE();
1215 /* if image was scaled up, create new clipmask for normal size image */
1216 if (zoom_factor != 1)
1218 SDL_Surface *tmp_surface_1 = tmp_bitmap_1->surface;
1220 if (old_bitmap->surface_masked)
1221 SDL_FreeSurface(old_bitmap->surface_masked);
1223 SDL_SetColorKey(tmp_surface_1, SET_TRANSPARENT_PIXEL,
1224 SDL_MapRGB(tmp_surface_1->format, 0x00, 0x00, 0x00));
1225 if ((old_bitmap->surface_masked = SDL_DisplayFormat(tmp_surface_1)) ==NULL)
1226 Error(ERR_EXIT, "SDL_DisplayFormat() failed");
1227 SDL_SetColorKey(tmp_surface_1, UNSET_TRANSPARENT_PIXEL, 0);
1231 if (create_small_bitmaps)
1233 new_width = width_1;
1234 new_height = height_1 + (height_1 + 1) / 2; /* prevent odd height */
1236 new_bitmap = CreateBitmap(new_width, new_height, DEFAULT_DEPTH);
1238 BlitBitmap(tmp_bitmap_1, new_bitmap, 0, 0, width_1, height_1, 0, 0);
1239 BlitBitmap(tmp_bitmap_2, new_bitmap, 0, 0, width_1 / 2, height_1 / 2,
1241 BlitBitmap(tmp_bitmap_4, new_bitmap, 0, 0, width_1 / 4, height_1 / 4,
1242 width_1 / 2, height_1);
1243 BlitBitmap(tmp_bitmap_8, new_bitmap, 0, 0, width_1 / 8, height_1 / 8,
1244 3 * width_1 / 4, height_1);
1245 BlitBitmap(tmp_bitmap_16, new_bitmap, 0, 0, width_1 / 16, height_1 / 16,
1246 7 * width_1 / 8, height_1);
1247 BlitBitmap(tmp_bitmap_32, new_bitmap, 0, 0, width_1 / 32, height_1 / 32,
1248 15 * width_1 / 16, height_1);
1250 UPDATE_BUSY_STATE();
1254 new_width = width_1;
1255 new_height = height_1;
1257 new_bitmap = tmp_bitmap_1; /* directly use tmp_bitmap_1 as new bitmap */
1260 if (create_small_bitmaps)
1262 /* if no small bitmaps created, tmp_bitmap_1 is used as new bitmap now */
1263 if (zoom_factor != 1)
1264 FreeBitmap(tmp_bitmap_1);
1266 if (zoom_factor != 2)
1267 FreeBitmap(tmp_bitmap_2);
1269 if (zoom_factor != 4)
1270 FreeBitmap(tmp_bitmap_4);
1272 if (zoom_factor != 8)
1273 FreeBitmap(tmp_bitmap_8);
1275 if (zoom_factor != 16)
1276 FreeBitmap(tmp_bitmap_16);
1278 if (zoom_factor != 32)
1279 FreeBitmap(tmp_bitmap_32);
1282 /* replace image with extended image (containing 1/1, 1/2, 1/4, 1/8 size) */
1283 #if defined(TARGET_SDL)
1284 swap_bitmap.surface = old_bitmap->surface;
1285 old_bitmap->surface = new_bitmap->surface;
1286 new_bitmap->surface = swap_bitmap.surface;
1288 swap_bitmap.drawable = old_bitmap->drawable;
1289 old_bitmap->drawable = new_bitmap->drawable;
1290 new_bitmap->drawable = swap_bitmap.drawable;
1293 old_bitmap->width = new_bitmap->width;
1294 old_bitmap->height = new_bitmap->height;
1297 /* this replaces all blit masks created when loading -- maybe optimize this */
1299 SDL_Surface *old_surface = old_bitmap->surface;
1301 if (old_bitmap->surface_masked)
1302 SDL_FreeSurface(old_bitmap->surface_masked);
1304 SDL_SetColorKey(old_surface, SET_TRANSPARENT_PIXEL,
1305 SDL_MapRGB(old_surface->format, 0x00, 0x00, 0x00));
1306 if ((old_bitmap->surface_masked = SDL_DisplayFormat(old_surface)) ==NULL)
1307 Error(ERR_EXIT, "SDL_DisplayFormat() failed");
1308 SDL_SetColorKey(old_surface, UNSET_TRANSPARENT_PIXEL, 0);
1312 UPDATE_BUSY_STATE();
1314 FreeBitmap(new_bitmap); /* this actually frees the _old_ bitmap now */
1317 void CreateBitmapWithSmallBitmaps(Bitmap *old_bitmap, int zoom_factor)
1319 CreateScaledBitmaps(old_bitmap, zoom_factor, TRUE);
1322 void ScaleBitmap(Bitmap *old_bitmap, int zoom_factor)
1324 CreateScaledBitmaps(old_bitmap, zoom_factor, FALSE);
1328 /* ------------------------------------------------------------------------- */
1329 /* mouse pointer functions */
1330 /* ------------------------------------------------------------------------- */
1332 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1334 /* XPM image definitions */
1335 static const char *cursor_image_none[] =
1337 /* width height num_colors chars_per_pixel */
1367 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1368 static const char *cursor_image_dot[] =
1370 /* width height num_colors chars_per_pixel */
1399 static const char **cursor_image_playfield = cursor_image_dot;
1401 /* some people complained about a "white dot" on the screen and thought it
1402 was a graphical error... OK, let's just remove the whole pointer :-) */
1403 static const char **cursor_image_playfield = cursor_image_none;
1406 static const int cursor_bit_order = BIT_ORDER_MSB;
1408 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1410 struct MouseCursorInfo *cursor;
1411 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1412 int header_lines = 4;
1415 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1417 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1420 for (y = 0; y < cursor->width; y++)
1422 for (x = 0; x < cursor->height; x++)
1425 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1430 cursor->data[i] = cursor->mask[i] = 0;
1433 switch (image[header_lines + y][x])
1436 cursor->data[i] |= bit_mask;
1437 cursor->mask[i] |= bit_mask;
1441 cursor->mask[i] |= bit_mask;
1450 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1455 void SetMouseCursor(int mode)
1457 static struct MouseCursorInfo *cursor_none = NULL;
1458 static struct MouseCursorInfo *cursor_playfield = NULL;
1459 struct MouseCursorInfo *cursor_new;
1461 if (cursor_none == NULL)
1462 cursor_none = get_cursor_from_image(cursor_image_none);
1464 if (cursor_playfield == NULL)
1465 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1467 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1468 mode == CURSOR_NONE ? cursor_none :
1469 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1471 SDLSetMouseCursor(cursor_new);
1475 /* ========================================================================= */
1476 /* audio functions */
1477 /* ========================================================================= */
1479 void OpenAudio(void)
1481 /* always start with reliable default values */
1482 audio.sound_available = FALSE;
1483 audio.music_available = FALSE;
1484 audio.loops_available = FALSE;
1486 audio.sound_enabled = FALSE;
1487 audio.sound_deactivated = FALSE;
1489 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1490 audio.mixer_pid = 0;
1491 audio.device_name = NULL;
1492 audio.device_fd = -1;
1494 audio.num_channels = 0;
1495 audio.music_channel = 0;
1496 audio.first_sound_channel = 0;
1498 #if defined(TARGET_SDL)
1503 void CloseAudio(void)
1505 #if defined(TARGET_SDL)
1509 audio.sound_enabled = FALSE;
1512 void SetAudioMode(boolean enabled)
1514 if (!audio.sound_available)
1517 audio.sound_enabled = enabled;
1521 /* ========================================================================= */
1522 /* event functions */
1523 /* ========================================================================= */
1525 void InitEventFilter(EventFilter filter_function)
1527 /* set event filter to filter out certain events */
1528 #if defined(TARGET_SDL)
1529 #if defined(TARGET_SDL2)
1530 SDL_SetEventFilter(filter_function, NULL);
1532 SDL_SetEventFilter(filter_function);
1537 boolean PendingEvent(void)
1539 #if defined(TARGET_SDL)
1540 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1542 return (XPending(display) ? TRUE : FALSE);
1546 void NextEvent(Event *event)
1548 #if defined(TARGET_SDL)
1549 SDLNextEvent(event);
1551 XNextEvent(display, event);
1555 void PeekEvent(Event *event)
1557 #if defined(TARGET_SDL)
1558 #if defined(TARGET_SDL2)
1559 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1561 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1564 XPeekEvent(display, event);
1568 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1570 #if defined(TARGET_SDL)
1571 #if defined(TARGET_SDL2)
1572 /* key up/down events in SDL2 do not return text characters anymore */
1573 return event->keysym.sym;
1577 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1578 (int)event->keysym.unicode,
1579 (int)event->keysym.sym,
1580 (int)SDL_GetModState());
1583 if (with_modifiers &&
1584 event->keysym.unicode > 0x0000 &&
1585 event->keysym.unicode < 0x2000)
1586 return event->keysym.unicode;
1588 return event->keysym.sym;
1594 printf("with modifiers == '0x%04x', without modifiers == '0x%04x'\n",
1595 (int)XLookupKeysym(event, event->state),
1596 (int)XLookupKeysym(event, 0));
1600 return XLookupKeysym(event, event->state);
1602 return XLookupKeysym(event, 0);
1606 KeyMod HandleKeyModState(Key key, int key_status)
1608 static KeyMod current_modifiers = KMOD_None;
1610 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1612 KeyMod new_modifier = KMOD_None;
1617 new_modifier = KMOD_Shift_L;
1620 new_modifier = KMOD_Shift_R;
1622 case KSYM_Control_L:
1623 new_modifier = KMOD_Control_L;
1625 case KSYM_Control_R:
1626 new_modifier = KMOD_Control_R;
1629 new_modifier = KMOD_Meta_L;
1632 new_modifier = KMOD_Meta_R;
1635 new_modifier = KMOD_Alt_L;
1638 new_modifier = KMOD_Alt_R;
1644 if (key_status == KEY_PRESSED)
1645 current_modifiers |= new_modifier;
1647 current_modifiers &= ~new_modifier;
1650 return current_modifiers;
1653 KeyMod GetKeyModState()
1655 #if defined(TARGET_SDL)
1656 return (KeyMod)SDL_GetModState();
1658 return HandleKeyModState(KSYM_UNDEFINED, 0);
1662 KeyMod GetKeyModStateFromEvents()
1664 /* always use key modifier state as tracked from key events (this is needed
1665 if the modifier key event was injected into the event queue, but the key
1666 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1667 query the keys as held pressed on the keyboard) -- this case is currently
1668 only used to filter out clipboard insert events from "True X-Mouse" tool */
1670 return HandleKeyModState(KSYM_UNDEFINED, 0);
1673 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1675 if (event->type != EVENT_CLIENTMESSAGE)
1678 #if defined(TARGET_SDL)
1679 return TRUE; /* the only possible message here is SDL_QUIT */
1686 /* ========================================================================= */
1687 /* joystick functions */
1688 /* ========================================================================= */
1690 void InitJoysticks()
1694 #if defined(NO_JOYSTICK)
1695 return; /* joysticks generally deactivated by compile-time directive */
1698 /* always start with reliable default values */
1699 joystick.status = JOYSTICK_NOT_AVAILABLE;
1700 for (i = 0; i < MAX_PLAYERS; i++)
1701 joystick.fd[i] = -1; /* joystick device closed */
1703 #if defined(TARGET_SDL)
1708 for (i = 0; i < MAX_PLAYERS; i++)
1709 printf("::: Joystick for player %d: %d\n", i, joystick.fd[i]);
1713 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1715 #if defined(TARGET_SDL)
1716 return SDLReadJoystick(nr, x, y, b1, b2);