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 TileCursorInfo tile_cursor;
37 struct OverlayInfo overlay;
38 struct ArtworkInfo artwork;
39 struct JoystickInfo joystick;
40 struct SetupInfo setup;
42 LevelDirTree *leveldir_first_all = NULL;
43 LevelDirTree *leveldir_first = NULL;
44 LevelDirTree *leveldir_current = NULL;
47 struct LevelStats level_stats[MAX_LEVELS];
49 DrawWindow *window = NULL;
50 DrawBuffer *backbuffer = NULL;
51 DrawBuffer *drawto = NULL;
53 int button_status = MB_NOT_PRESSED;
54 boolean motion_status = FALSE;
55 int wheel_steps = DEFAULT_WHEEL_STEPS;
56 #if defined(TARGET_SDL2)
57 boolean keyrepeat_status = TRUE;
60 int redraw_mask = REDRAW_NONE;
65 /* ========================================================================= */
66 /* init/close functions */
67 /* ========================================================================= */
69 void InitProgramInfo(char *argv0, char *config_filename, char *userdata_subdir,
70 char *program_title, char *icon_title,
71 char *icon_filename, char *cookie_prefix,
72 char *program_version_string, int program_version)
74 program.command_basepath = getBasePath(argv0);
75 program.command_basename = getBaseName(argv0);
77 program.config_filename = config_filename;
79 program.userdata_subdir = userdata_subdir;
80 program.userdata_path = getUserGameDataDir();
82 program.program_title = program_title;
83 program.window_title = "(undefined)";
84 program.icon_title = icon_title;
86 program.icon_filename = icon_filename;
88 program.cookie_prefix = cookie_prefix;
90 program.version_major = VERSION_MAJOR(program_version);
91 program.version_minor = VERSION_MINOR(program_version);
92 program.version_patch = VERSION_PATCH(program_version);
93 program.version_build = VERSION_BUILD(program_version);
94 program.version_ident = program_version;
96 program.version_string = program_version_string;
98 program.log_filename[LOG_OUT_ID] = getLogFilename(LOG_OUT_BASENAME);
99 program.log_filename[LOG_ERR_ID] = getLogFilename(LOG_ERR_BASENAME);
100 program.log_file[LOG_OUT_ID] = program.log_file_default[LOG_OUT_ID] = stdout;
101 program.log_file[LOG_ERR_ID] = program.log_file_default[LOG_ERR_ID] = stderr;
103 program.headless = FALSE;
106 void InitScoresInfo()
108 char *global_scores_dir = getPath2(getCommonDataDir(), SCORES_DIRECTORY);
110 program.global_scores = directoryExists(global_scores_dir);
111 program.many_scores_per_name = !program.global_scores;
115 if (program.global_scores)
117 Error(ERR_DEBUG, "Using global, multi-user scores directory '%s'.",
119 Error(ERR_DEBUG, "Remove to enable single-user scores directory.");
120 Error(ERR_DEBUG, "(This enables multipe score entries per user.)");
124 Error(ERR_DEBUG, "Using private, single-user scores directory.");
128 free(global_scores_dir);
131 void SetWindowTitle()
133 program.window_title = program.window_title_function();
138 void InitWindowTitleFunction(char *(*window_title_function)(void))
140 program.window_title_function = window_title_function;
143 void InitExitMessageFunction(void (*exit_message_function)(char *, va_list))
145 program.exit_message_function = exit_message_function;
148 void InitExitFunction(void (*exit_function)(int))
150 program.exit_function = exit_function;
152 /* set signal handlers to custom exit function */
153 // signal(SIGINT, exit_function);
154 signal(SIGTERM, exit_function);
156 /* set exit function to automatically cleanup SDL stuff after exit() */
160 void InitPlatformDependentStuff(void)
162 // this is initialized in GetOptions(), but may already be used before
163 options.verbose = TRUE;
167 #if defined(TARGET_SDL2)
168 int sdl_init_flags = SDL_INIT_EVENTS | SDL_INIT_NOPARACHUTE;
170 int sdl_init_flags = SDL_INIT_EVENTTHREAD | SDL_INIT_NOPARACHUTE;
173 if (SDL_Init(sdl_init_flags) < 0)
174 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
179 void ClosePlatformDependentStuff(void)
184 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
185 int real_sx, int real_sy,
186 int full_sxsize, int full_sysize,
187 Bitmap *field_save_buffer)
193 gfx.real_sx = real_sx;
194 gfx.real_sy = real_sy;
195 gfx.full_sxsize = full_sxsize;
196 gfx.full_sysize = full_sysize;
198 gfx.field_save_buffer = field_save_buffer;
200 SetDrawDeactivationMask(REDRAW_NONE); /* do not deactivate drawing */
201 SetDrawBackgroundMask(REDRAW_NONE); /* deactivate masked drawing */
204 void InitGfxTileSizeInfo(int game_tile_size, int standard_tile_size)
206 gfx.game_tile_size = game_tile_size;
207 gfx.standard_tile_size = standard_tile_size;
210 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
218 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
226 void InitGfxDoor3Info(int ex, int ey, int exsize, int eysize)
234 void InitGfxWindowInfo(int win_xsize, int win_ysize)
236 if (win_xsize != gfx.win_xsize || win_ysize != gfx.win_ysize)
238 ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize);
240 #if defined(TARGET_SDL2)
241 ReCreateBitmap(&gfx.final_screen_bitmap, win_xsize, win_ysize);
244 ReCreateBitmap(&gfx.fade_bitmap_backup, win_xsize, win_ysize);
245 ReCreateBitmap(&gfx.fade_bitmap_source, win_xsize, win_ysize);
246 ReCreateBitmap(&gfx.fade_bitmap_target, win_xsize, win_ysize);
247 ReCreateBitmap(&gfx.fade_bitmap_black, win_xsize, win_ysize);
249 ClearRectangle(gfx.fade_bitmap_black, 0, 0, win_xsize, win_ysize);
252 gfx.win_xsize = win_xsize;
253 gfx.win_ysize = win_ysize;
255 gfx.background_bitmap_mask = REDRAW_NONE;
258 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
260 /* currently only used by MSDOS code to alloc VRAM buffer, if available */
261 /* 2009-03-24: also (temporarily?) used for overlapping blit workaround */
262 gfx.scrollbuffer_width = scrollbuffer_width;
263 gfx.scrollbuffer_height = scrollbuffer_height;
266 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
268 gfx.clipping_enabled = enabled;
271 gfx.clip_width = width;
272 gfx.clip_height = height;
275 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
277 gfx.draw_busy_anim_function = draw_busy_anim_function;
280 void InitGfxDrawGlobalAnimFunction(void (*draw_global_anim_function)(int, int))
282 gfx.draw_global_anim_function = draw_global_anim_function;
285 void InitGfxDrawGlobalBorderFunction(void (*draw_global_border_function)(int))
287 gfx.draw_global_border_function = draw_global_border_function;
290 void InitGfxDrawTileCursorFunction(void (*draw_tile_cursor_function)(int))
292 gfx.draw_tile_cursor_function = draw_tile_cursor_function;
295 void InitGfxCustomArtworkInfo()
297 gfx.override_level_graphics = FALSE;
298 gfx.override_level_sounds = FALSE;
299 gfx.override_level_music = FALSE;
301 gfx.draw_init_text = TRUE;
304 void InitGfxOtherSettings()
306 gfx.cursor_mode = CURSOR_DEFAULT;
309 void InitTileCursorInfo()
311 tile_cursor.enabled = FALSE;
312 tile_cursor.active = FALSE;
314 tile_cursor.xpos = 0;
315 tile_cursor.ypos = 0;
318 tile_cursor.target_x = 0;
319 tile_cursor.target_y = 0;
322 void InitOverlayInfo()
324 overlay.enabled = FALSE;
325 overlay.active = FALSE;
327 #if defined(PLATFORM_ANDROID)
328 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
329 overlay.enabled = TRUE;
333 void SetTileCursorEnabled(boolean enabled)
335 tile_cursor.enabled = enabled;
338 void SetTileCursorActive(boolean active)
340 tile_cursor.active = active;
343 void SetTileCursorTargetXY(int x, int y)
345 // delayed placement of tile selection cursor at target position
346 // (tile cursor will be moved to target position step by step)
348 tile_cursor.xpos = x;
349 tile_cursor.ypos = y;
350 tile_cursor.target_x = gfx.sx + x * gfx.game_tile_size;
351 tile_cursor.target_y = gfx.sy + y * gfx.game_tile_size;
354 void SetTileCursorXY(int x, int y)
356 // immediate placement of tile selection cursor at target position
358 SetTileCursorTargetXY(x, y);
360 tile_cursor.x = tile_cursor.target_x;
361 tile_cursor.y = tile_cursor.target_y;
364 void SetOverlayEnabled(boolean enabled)
366 overlay.enabled = enabled;
369 void SetOverlayActive(boolean active)
371 overlay.active = active;
374 boolean GetOverlayActive()
376 return overlay.active;
379 void SetDrawDeactivationMask(int draw_deactivation_mask)
381 gfx.draw_deactivation_mask = draw_deactivation_mask;
384 int GetDrawDeactivationMask()
386 return gfx.draw_deactivation_mask;
389 void SetDrawBackgroundMask(int draw_background_mask)
391 gfx.draw_background_mask = draw_background_mask;
394 void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
396 if (background_bitmap_tile != NULL)
397 gfx.background_bitmap_mask |= mask;
399 gfx.background_bitmap_mask &= ~mask;
401 if (background_bitmap_tile == NULL) /* empty background requested */
404 if (mask == REDRAW_ALL)
405 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
406 0, 0, video.width, video.height);
407 else if (mask == REDRAW_FIELD)
408 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
409 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
410 else if (mask == REDRAW_DOOR_1)
411 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
412 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
415 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
417 /* remove every mask before setting mask for window */
418 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
419 SetBackgroundBitmap(NULL, 0xffff); /* !!! FIX THIS !!! */
420 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
423 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
425 /* remove window area mask before setting mask for main area */
426 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
427 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
428 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
431 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
433 /* remove window area mask before setting mask for door area */
434 /* (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!) */
435 SetBackgroundBitmap(NULL, REDRAW_ALL); /* !!! FIX THIS !!! */
436 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
440 /* ========================================================================= */
441 /* video functions */
442 /* ========================================================================= */
444 inline static int GetRealDepth(int depth)
446 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
449 inline static void sysFillRectangle(Bitmap *bitmap, int x, int y,
450 int width, int height, Pixel color)
452 SDLFillRectangle(bitmap, x, y, width, height, color);
454 if (bitmap == backbuffer)
455 SetRedrawMaskFromArea(x, y, width, height);
458 inline static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
459 int src_x, int src_y, int width, int height,
460 int dst_x, int dst_y, int mask_mode)
462 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
463 dst_x, dst_y, mask_mode);
465 if (dst_bitmap == backbuffer)
466 SetRedrawMaskFromArea(dst_x, dst_y, width, height);
469 void LimitScreenUpdates(boolean enable)
471 SDLLimitScreenUpdates(enable);
474 void InitVideoDefaults(void)
476 video.default_depth = 32;
479 void InitVideoDisplay(void)
481 if (program.headless)
484 SDLInitVideoDisplay();
485 #if defined(TARGET_SDL2)
490 void CloseVideoDisplay(void)
492 KeyboardAutoRepeatOn();
494 SDL_QuitSubSystem(SDL_INIT_VIDEO);
497 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
500 video.height = height;
501 video.depth = GetRealDepth(depth);
503 video.screen_width = width;
504 video.screen_height = height;
505 video.screen_xoffset = 0;
506 video.screen_yoffset = 0;
508 video.fullscreen_available = FULLSCREEN_STATUS;
509 video.fullscreen_enabled = FALSE;
511 video.window_scaling_available = WINDOW_SCALING_STATUS;
513 video.frame_delay = 0;
514 video.frame_delay_value = GAME_FRAME_DELAY;
516 video.shifted_up = FALSE;
517 video.shifted_up_pos = 0;
518 video.shifted_up_pos_last = 0;
519 video.shifted_up_delay = 0;
520 video.shifted_up_delay_value = ONE_SECOND_DELAY / 4;
522 SDLInitVideoBuffer(fullscreen);
524 video.initialized = !program.headless;
529 inline static void FreeBitmapPointers(Bitmap *bitmap)
534 SDLFreeBitmapPointers(bitmap);
536 checked_free(bitmap->source_filename);
537 bitmap->source_filename = NULL;
540 inline static void TransferBitmapPointers(Bitmap *src_bitmap,
543 if (src_bitmap == NULL || dst_bitmap == NULL)
546 FreeBitmapPointers(dst_bitmap);
548 *dst_bitmap = *src_bitmap;
551 void FreeBitmap(Bitmap *bitmap)
556 FreeBitmapPointers(bitmap);
561 Bitmap *CreateBitmapStruct(void)
563 return checked_calloc(sizeof(Bitmap));
566 Bitmap *CreateBitmap(int width, int height, int depth)
568 Bitmap *new_bitmap = CreateBitmapStruct();
569 int real_width = MAX(1, width); /* prevent zero bitmap width */
570 int real_height = MAX(1, height); /* prevent zero bitmap height */
571 int real_depth = GetRealDepth(depth);
573 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
575 new_bitmap->width = real_width;
576 new_bitmap->height = real_height;
581 void ReCreateBitmap(Bitmap **bitmap, int width, int height)
585 /* if new bitmap size fits into old one, no need to re-create it */
586 if (width <= (*bitmap)->width &&
587 height <= (*bitmap)->height)
590 /* else adjust size so that old and new bitmap size fit into it */
591 width = MAX(width, (*bitmap)->width);
592 height = MAX(height, (*bitmap)->height);
595 Bitmap *new_bitmap = CreateBitmap(width, height, DEFAULT_DEPTH);
599 *bitmap = new_bitmap;
603 TransferBitmapPointers(new_bitmap, *bitmap);
608 void CloseWindow(DrawWindow *window)
612 void SetRedrawMaskFromArea(int x, int y, int width, int height)
616 int x2 = x + width - 1;
617 int y2 = y + height - 1;
619 if (width == 0 || height == 0)
622 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
623 redraw_mask |= REDRAW_FIELD;
624 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
625 redraw_mask |= REDRAW_DOOR_1;
626 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
627 redraw_mask |= REDRAW_DOOR_2;
628 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
629 redraw_mask |= REDRAW_DOOR_3;
631 redraw_mask = REDRAW_ALL;
634 inline static boolean CheckDrawingArea(int x, int y, int width, int height,
637 if (draw_mask == REDRAW_NONE)
640 if (draw_mask & REDRAW_ALL)
643 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
646 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
649 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
652 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
658 boolean DrawingDeactivatedField()
660 if (program.headless)
663 if (gfx.draw_deactivation_mask & REDRAW_FIELD)
669 boolean DrawingDeactivated(int x, int y, int width, int height)
671 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
674 boolean DrawingOnBackground(int x, int y)
676 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
677 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
680 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
681 int *width, int *height, boolean is_dest)
683 int clip_x, clip_y, clip_width, clip_height;
685 if (gfx.clipping_enabled && is_dest) /* only clip destination bitmap */
687 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
688 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
689 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
690 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
696 clip_width = bitmap->width;
697 clip_height = bitmap->height;
700 /* skip if rectangle completely outside bitmap */
702 if (*x + *width <= clip_x ||
703 *y + *height <= clip_y ||
704 *x >= clip_x + clip_width ||
705 *y >= clip_y + clip_height)
708 /* clip if rectangle overlaps bitmap */
712 *width -= clip_x - *x;
715 else if (*x + *width > clip_x + clip_width)
717 *width = clip_x + clip_width - *x;
722 *height -= clip_y - *y;
725 else if (*y + *height > clip_y + clip_height)
727 *height = clip_y + clip_height - *y;
733 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
734 int src_x, int src_y, int width, int height,
735 int dst_x, int dst_y)
737 int dst_x_unclipped = dst_x;
738 int dst_y_unclipped = dst_y;
740 if (program.headless)
743 if (src_bitmap == NULL || dst_bitmap == NULL)
746 if (DrawingDeactivated(dst_x, dst_y, width, height))
749 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
750 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
753 /* source x/y might need adjustment if destination x/y was clipped top/left */
754 src_x += dst_x - dst_x_unclipped;
755 src_y += dst_y - dst_y_unclipped;
757 #if defined(TARGET_SDL2)
758 /* !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!! */
759 /* !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!! */
760 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
761 but is already fixed in SVN and should therefore finally be fixed with
762 the next official SDL release, which is probably version 1.2.14.) */
763 /* !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!! */
765 if (src_bitmap == dst_bitmap)
767 /* needed when blitting directly to same bitmap -- should not be needed with
768 recent SDL libraries, but apparently does not work in 1.2.11 directly */
770 static Bitmap *tmp_bitmap = NULL;
771 static int tmp_bitmap_xsize = 0;
772 static int tmp_bitmap_ysize = 0;
774 /* start with largest static bitmaps for initial bitmap size ... */
775 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
777 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
778 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
781 /* ... and allow for later re-adjustments due to custom artwork bitmaps */
782 if (src_bitmap->width > tmp_bitmap_xsize ||
783 src_bitmap->height > tmp_bitmap_ysize)
785 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
786 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
788 FreeBitmap(tmp_bitmap);
793 if (tmp_bitmap == NULL)
794 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
797 sysCopyArea(src_bitmap, tmp_bitmap,
798 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
799 sysCopyArea(tmp_bitmap, dst_bitmap,
800 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
806 sysCopyArea(src_bitmap, dst_bitmap,
807 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
810 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
811 int src_x, int src_y, int src_width, int src_height,
812 int dst_x, int dst_y, int dst_width, int dst_height)
814 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
815 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
816 int dst_xsize = dst_width;
817 int dst_ysize = dst_height;
818 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
819 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
822 for (y = 0; y < src_ysteps; y++)
824 for (x = 0; x < src_xsteps; x++)
826 int draw_x = dst_x + x * src_xsize;
827 int draw_y = dst_y + y * src_ysize;
828 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
829 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
831 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
837 void FadeRectangle(int x, int y, int width, int height,
838 int fade_mode, int fade_delay, int post_delay,
839 void (*draw_border_function)(void))
841 /* (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined) */
842 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
845 SDLFadeRectangle(x, y, width, height,
846 fade_mode, fade_delay, post_delay, draw_border_function);
849 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
852 if (DrawingDeactivated(x, y, width, height))
855 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
858 sysFillRectangle(bitmap, x, y, width, height, color);
861 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
863 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
866 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
867 int width, int height)
869 if (DrawingOnBackground(x, y))
870 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
872 ClearRectangle(bitmap, x, y, width, height);
875 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
876 int src_x, int src_y, int width, int height,
877 int dst_x, int dst_y)
879 if (DrawingDeactivated(dst_x, dst_y, width, height))
882 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
883 dst_x, dst_y, BLIT_MASKED);
886 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
887 int src_x, int src_y, int width, int height,
888 int dst_x, int dst_y)
890 if (DrawingOnBackground(dst_x, dst_y))
892 /* draw background */
893 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
896 /* draw foreground */
897 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
901 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
905 void BlitTexture(Bitmap *bitmap,
906 int src_x, int src_y, int width, int height,
907 int dst_x, int dst_y)
912 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
916 void BlitTextureMasked(Bitmap *bitmap,
917 int src_x, int src_y, int width, int height,
918 int dst_x, int dst_y)
923 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
927 void BlitToScreen(Bitmap *bitmap,
928 int src_x, int src_y, int width, int height,
929 int dst_x, int dst_y)
934 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
935 BlitBitmap(bitmap, gfx.final_screen_bitmap, src_x, src_y,
936 width, height, dst_x, dst_y);
938 BlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y);
941 void BlitToScreenMasked(Bitmap *bitmap,
942 int src_x, int src_y, int width, int height,
943 int dst_x, int dst_y)
948 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
949 BlitBitmapMasked(bitmap, gfx.final_screen_bitmap, src_x, src_y,
950 width, height, dst_x, dst_y);
952 BlitTextureMasked(bitmap, src_x, src_y, width, height, dst_x, dst_y);
955 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
958 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
961 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
964 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
967 void DrawLine(Bitmap *bitmap, int from_x, int from_y,
968 int to_x, int to_y, Pixel pixel, int line_width)
972 if (program.headless)
975 for (x = 0; x < line_width; x++)
977 for (y = 0; y < line_width; y++)
979 int dx = x - line_width / 2;
980 int dy = y - line_width / 2;
982 if ((x == 0 && y == 0) ||
983 (x == 0 && y == line_width - 1) ||
984 (x == line_width - 1 && y == 0) ||
985 (x == line_width - 1 && y == line_width - 1))
989 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
994 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
999 for (i = 0; i < num_points - 1; i++)
1000 DrawLine(bitmap, points[i].x, points[i].y,
1001 points[i + 1].x, points[i + 1].y, pixel, line_width);
1004 SDLDrawLines(bitmap->surface, points, num_points, pixel);
1008 Pixel GetPixel(Bitmap *bitmap, int x, int y)
1010 if (program.headless)
1013 if (x < 0 || x >= bitmap->width ||
1014 y < 0 || y >= bitmap->height)
1017 return SDLGetPixel(bitmap, x, y);
1020 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
1021 unsigned int color_g, unsigned int color_b)
1023 if (program.headless)
1026 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
1029 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
1031 unsigned int color_r = (color >> 16) & 0xff;
1032 unsigned int color_g = (color >> 8) & 0xff;
1033 unsigned int color_b = (color >> 0) & 0xff;
1035 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
1038 void KeyboardAutoRepeatOn(void)
1040 #if defined(TARGET_SDL2)
1041 keyrepeat_status = TRUE;
1043 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
1044 SDL_DEFAULT_REPEAT_INTERVAL / 2);
1045 SDL_EnableUNICODE(1);
1049 void KeyboardAutoRepeatOff(void)
1051 #if defined(TARGET_SDL2)
1052 keyrepeat_status = FALSE;
1054 SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
1055 SDL_EnableUNICODE(0);
1059 boolean SetVideoMode(boolean fullscreen)
1061 return SDLSetVideoMode(fullscreen);
1064 void SetVideoFrameDelay(unsigned int frame_delay_value)
1066 video.frame_delay_value = frame_delay_value;
1069 unsigned int GetVideoFrameDelay()
1071 return video.frame_delay_value;
1074 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
1076 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
1077 (!fullscreen && video.fullscreen_enabled))
1078 fullscreen = SetVideoMode(fullscreen);
1083 Bitmap *LoadImage(char *filename)
1087 new_bitmap = SDLLoadImage(filename);
1090 new_bitmap->source_filename = getStringCopy(filename);
1095 Bitmap *LoadCustomImage(char *basename)
1097 char *filename = getCustomImageFilename(basename);
1100 if (filename == NULL)
1101 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
1103 if ((new_bitmap = LoadImage(filename)) == NULL)
1104 Error(ERR_EXIT, "LoadImage('%s') failed: %s", basename, GetError());
1109 void ReloadCustomImage(Bitmap *bitmap, char *basename)
1111 char *filename = getCustomImageFilename(basename);
1114 if (filename == NULL) /* (should never happen) */
1116 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
1120 if (strEqual(filename, bitmap->source_filename))
1122 /* The old and new image are the same (have the same filename and path).
1123 This usually means that this image does not exist in this graphic set
1124 and a fallback to the existing image is done. */
1129 if ((new_bitmap = LoadImage(filename)) == NULL)
1131 Error(ERR_WARN, "LoadImage('%s') failed: %s", basename, GetError());
1135 if (bitmap->width != new_bitmap->width ||
1136 bitmap->height != new_bitmap->height)
1138 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
1140 FreeBitmap(new_bitmap);
1144 TransferBitmapPointers(new_bitmap, bitmap);
1148 static Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
1150 return SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
1153 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
1155 if (bitmaps[IMG_BITMAP_CUSTOM])
1157 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
1159 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
1162 if (gfx.game_tile_size == gfx.standard_tile_size)
1164 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1169 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
1170 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
1171 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
1173 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
1175 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
1176 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
1179 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1180 int tile_size, boolean create_small_bitmaps)
1182 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1183 Bitmap *tmp_bitmap_final = NULL;
1184 Bitmap *tmp_bitmap_0 = NULL;
1185 Bitmap *tmp_bitmap_1 = NULL;
1186 Bitmap *tmp_bitmap_2 = NULL;
1187 Bitmap *tmp_bitmap_4 = NULL;
1188 Bitmap *tmp_bitmap_8 = NULL;
1189 Bitmap *tmp_bitmap_16 = NULL;
1190 Bitmap *tmp_bitmap_32 = NULL;
1191 int width_final, height_final;
1192 int width_0, height_0;
1193 int width_1, height_1;
1194 int width_2, height_2;
1195 int width_4, height_4;
1196 int width_8, height_8;
1197 int width_16, height_16;
1198 int width_32, height_32;
1199 int old_width, old_height;
1202 print_timestamp_init("CreateScaledBitmaps");
1204 old_width = old_bitmap->width;
1205 old_height = old_bitmap->height;
1207 /* calculate new image dimensions for final image size */
1208 width_final = old_width * zoom_factor;
1209 height_final = old_height * zoom_factor;
1211 /* get image with final size (this might require scaling up) */
1212 /* ("final" size may result in non-standard tile size image) */
1213 if (zoom_factor != 1)
1214 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1216 tmp_bitmap_final = old_bitmap;
1218 UPDATE_BUSY_STATE();
1220 width_0 = width_1 = width_final;
1221 height_0 = height_1 = height_final;
1223 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1225 if (create_small_bitmaps)
1227 /* check if we have a non-gameplay tile size image */
1228 if (tile_size != gfx.game_tile_size)
1230 /* get image with gameplay tile size */
1231 width_0 = width_final * gfx.game_tile_size / tile_size;
1232 height_0 = height_final * gfx.game_tile_size / tile_size;
1234 if (width_0 == old_width)
1235 tmp_bitmap_0 = old_bitmap;
1236 else if (width_0 == width_final)
1237 tmp_bitmap_0 = tmp_bitmap_final;
1239 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1241 UPDATE_BUSY_STATE();
1244 /* check if we have a non-standard tile size image */
1245 if (tile_size != gfx.standard_tile_size)
1247 /* get image with standard tile size */
1248 width_1 = width_final * gfx.standard_tile_size / tile_size;
1249 height_1 = height_final * gfx.standard_tile_size / tile_size;
1251 if (width_1 == old_width)
1252 tmp_bitmap_1 = old_bitmap;
1253 else if (width_1 == width_final)
1254 tmp_bitmap_1 = tmp_bitmap_final;
1255 else if (width_1 == width_0)
1256 tmp_bitmap_1 = tmp_bitmap_0;
1258 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1260 UPDATE_BUSY_STATE();
1263 /* calculate new image dimensions for small images */
1264 width_2 = width_1 / 2;
1265 height_2 = height_1 / 2;
1266 width_4 = width_1 / 4;
1267 height_4 = height_1 / 4;
1268 width_8 = width_1 / 8;
1269 height_8 = height_1 / 8;
1270 width_16 = width_1 / 16;
1271 height_16 = height_1 / 16;
1272 width_32 = width_1 / 32;
1273 height_32 = height_1 / 32;
1275 /* get image with 1/2 of normal size (for use in the level editor) */
1276 if (width_2 == old_width)
1277 tmp_bitmap_2 = old_bitmap;
1279 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1281 UPDATE_BUSY_STATE();
1283 /* get image with 1/4 of normal size (for use in the level editor) */
1284 if (width_4 == old_width)
1285 tmp_bitmap_4 = old_bitmap;
1287 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1289 UPDATE_BUSY_STATE();
1291 /* get image with 1/8 of normal size (for use on the preview screen) */
1292 if (width_8 == old_width)
1293 tmp_bitmap_8 = old_bitmap;
1295 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1297 UPDATE_BUSY_STATE();
1299 /* get image with 1/16 of normal size (for use on the preview screen) */
1300 if (width_16 == old_width)
1301 tmp_bitmap_16 = old_bitmap;
1303 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1305 UPDATE_BUSY_STATE();
1307 /* get image with 1/32 of normal size (for use on the preview screen) */
1308 if (width_32 == old_width)
1309 tmp_bitmap_32 = old_bitmap;
1311 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1313 UPDATE_BUSY_STATE();
1315 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1316 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1317 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1318 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1319 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1320 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1322 if (width_0 != width_1)
1323 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1325 if (bitmaps[IMG_BITMAP_CUSTOM])
1326 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1328 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1330 boolean free_old_bitmap = TRUE;
1332 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1333 if (bitmaps[i] == old_bitmap)
1334 free_old_bitmap = FALSE;
1336 if (free_old_bitmap)
1337 FreeBitmap(old_bitmap);
1341 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1344 UPDATE_BUSY_STATE();
1346 print_timestamp_done("CreateScaledBitmaps");
1349 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1352 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1355 void CreateBitmapTextures(Bitmap **bitmaps)
1357 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1360 void FreeBitmapTextures(Bitmap **bitmaps)
1362 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1365 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1367 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1371 /* ------------------------------------------------------------------------- */
1372 /* mouse pointer functions */
1373 /* ------------------------------------------------------------------------- */
1375 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1377 /* XPM image definitions */
1378 static const char *cursor_image_none[] =
1380 /* width height num_colors chars_per_pixel */
1410 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1411 static const char *cursor_image_dot[] =
1413 /* width height num_colors chars_per_pixel */
1442 static const char **cursor_image_playfield = cursor_image_dot;
1444 /* some people complained about a "white dot" on the screen and thought it
1445 was a graphical error... OK, let's just remove the whole pointer :-) */
1446 static const char **cursor_image_playfield = cursor_image_none;
1449 static const int cursor_bit_order = BIT_ORDER_MSB;
1451 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1453 struct MouseCursorInfo *cursor;
1454 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1455 int header_lines = 4;
1458 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1460 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1463 for (y = 0; y < cursor->width; y++)
1465 for (x = 0; x < cursor->height; x++)
1468 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1473 cursor->data[i] = cursor->mask[i] = 0;
1476 switch (image[header_lines + y][x])
1479 cursor->data[i] |= bit_mask;
1480 cursor->mask[i] |= bit_mask;
1484 cursor->mask[i] |= bit_mask;
1493 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1498 void SetMouseCursor(int mode)
1500 static struct MouseCursorInfo *cursor_none = NULL;
1501 static struct MouseCursorInfo *cursor_playfield = NULL;
1502 struct MouseCursorInfo *cursor_new;
1504 if (cursor_none == NULL)
1505 cursor_none = get_cursor_from_image(cursor_image_none);
1507 if (cursor_playfield == NULL)
1508 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1510 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1511 mode == CURSOR_NONE ? cursor_none :
1512 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1514 SDLSetMouseCursor(cursor_new);
1516 gfx.cursor_mode = mode;
1520 /* ========================================================================= */
1521 /* audio functions */
1522 /* ========================================================================= */
1524 void OpenAudio(void)
1526 /* always start with reliable default values */
1527 audio.sound_available = FALSE;
1528 audio.music_available = FALSE;
1529 audio.loops_available = FALSE;
1531 audio.sound_enabled = FALSE;
1532 audio.sound_deactivated = FALSE;
1534 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1535 audio.mixer_pid = 0;
1536 audio.device_name = NULL;
1537 audio.device_fd = -1;
1539 audio.num_channels = 0;
1540 audio.music_channel = 0;
1541 audio.first_sound_channel = 0;
1546 void CloseAudio(void)
1550 audio.sound_enabled = FALSE;
1553 void SetAudioMode(boolean enabled)
1555 if (!audio.sound_available)
1558 audio.sound_enabled = enabled;
1562 /* ========================================================================= */
1563 /* event functions */
1564 /* ========================================================================= */
1566 boolean PendingEvent(void)
1568 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1571 void WaitEvent(Event *event)
1573 SDLWaitEvent(event);
1576 void PeekEvent(Event *event)
1578 #if defined(TARGET_SDL2)
1579 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1581 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
1585 void CheckQuitEvent(void)
1587 if (SDL_QuitRequested())
1588 program.exit_function(0);
1591 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1593 #if defined(TARGET_SDL2)
1594 /* key up/down events in SDL2 do not return text characters anymore */
1595 return event->keysym.sym;
1598 #if ENABLE_UNUSED_CODE
1599 printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
1600 (int)event->keysym.unicode,
1601 (int)event->keysym.sym,
1602 (int)SDL_GetModState());
1605 if (with_modifiers &&
1606 event->keysym.unicode > 0x0000 &&
1607 event->keysym.unicode < 0x2000)
1608 return event->keysym.unicode;
1610 return event->keysym.sym;
1615 KeyMod HandleKeyModState(Key key, int key_status)
1617 static KeyMod current_modifiers = KMOD_None;
1619 if (key != KSYM_UNDEFINED) /* new key => check for modifier key change */
1621 KeyMod new_modifier = KMOD_None;
1626 new_modifier = KMOD_Shift_L;
1629 new_modifier = KMOD_Shift_R;
1631 case KSYM_Control_L:
1632 new_modifier = KMOD_Control_L;
1634 case KSYM_Control_R:
1635 new_modifier = KMOD_Control_R;
1638 new_modifier = KMOD_Meta_L;
1641 new_modifier = KMOD_Meta_R;
1644 new_modifier = KMOD_Alt_L;
1647 new_modifier = KMOD_Alt_R;
1653 if (key_status == KEY_PRESSED)
1654 current_modifiers |= new_modifier;
1656 current_modifiers &= ~new_modifier;
1659 return current_modifiers;
1662 KeyMod GetKeyModState()
1664 return (KeyMod)SDL_GetModState();
1667 KeyMod GetKeyModStateFromEvents()
1669 /* always use key modifier state as tracked from key events (this is needed
1670 if the modifier key event was injected into the event queue, but the key
1671 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1672 query the keys as held pressed on the keyboard) -- this case is currently
1673 only used to filter out clipboard insert events from "True X-Mouse" tool */
1675 return HandleKeyModState(KSYM_UNDEFINED, 0);
1678 void StartTextInput(int x, int y, int width, int height)
1680 #if defined(TARGET_SDL2)
1681 #if defined(HAS_SCREEN_KEYBOARD)
1682 SDL_StartTextInput();
1684 if (y + height > SCREEN_KEYBOARD_POS(video.height))
1686 video.shifted_up_pos = y + height - SCREEN_KEYBOARD_POS(video.height);
1687 video.shifted_up_delay = SDL_GetTicks();
1688 video.shifted_up = TRUE;
1694 void StopTextInput()
1696 #if defined(TARGET_SDL2)
1697 #if defined(HAS_SCREEN_KEYBOARD)
1698 SDL_StopTextInput();
1700 if (video.shifted_up)
1702 video.shifted_up_pos = 0;
1703 video.shifted_up_delay = SDL_GetTicks();
1704 video.shifted_up = FALSE;
1710 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1712 if (event->type != EVENT_CLIENTMESSAGE)
1715 return TRUE; /* the only possible message here is SDL_QUIT */
1719 /* ========================================================================= */
1720 /* joystick functions */
1721 /* ========================================================================= */
1723 void InitJoysticks()
1727 #if defined(NO_JOYSTICK)
1728 return; /* joysticks generally deactivated by compile-time directive */
1731 /* always start with reliable default values */
1732 joystick.status = JOYSTICK_NOT_AVAILABLE;
1733 for (i = 0; i < MAX_PLAYERS; i++)
1734 joystick.nr[i] = -1; /* no joystick configured */
1739 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1741 return SDLReadJoystick(nr, x, y, b1, b2);
1744 boolean CheckJoystickOpened(int nr)
1746 return SDLCheckJoystickOpened(nr);
1749 void ClearJoystickState()
1751 SDLClearJoystickState();