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 // ============================================================================
29 // ============================================================================
31 struct ProgramInfo program;
32 struct NetworkInfo network;
33 struct RuntimeInfo runtime;
34 struct OptionInfo options;
35 struct VideoSystemInfo video;
36 struct AudioSystemInfo audio;
38 struct TileCursorInfo tile_cursor;
39 struct OverlayInfo overlay;
40 struct ArtworkInfo artwork;
41 struct JoystickInfo joystick;
42 struct SetupInfo setup;
44 LevelDirTree *leveldir_first_all = NULL;
45 LevelDirTree *leveldir_first = NULL;
46 LevelDirTree *leveldir_current = NULL;
49 struct LevelSetInfo levelset;
50 struct LevelStats level_stats[MAX_LEVELS];
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 int wheel_steps = DEFAULT_WHEEL_STEPS;
59 boolean keyrepeat_status = TRUE;
61 int redraw_mask = REDRAW_NONE;
66 // ============================================================================
67 // init/close functions
68 // ============================================================================
70 void InitProgramInfo(char *argv0, char *config_filename, char *userdata_subdir,
71 char *program_title, char *icon_title,
72 char *icon_filename, char *cookie_prefix,
73 char *program_version_string, int program_version)
75 program.command_basepath = getBasePath(argv0);
76 program.command_basename = getBaseName(argv0);
78 program.config_filename = config_filename;
80 program.userdata_subdir = userdata_subdir;
81 program.userdata_path = getUserGameDataDir();
83 program.program_title = program_title;
84 program.window_title = "(undefined)";
85 program.icon_title = icon_title;
87 program.icon_filename = icon_filename;
89 program.cookie_prefix = cookie_prefix;
91 program.version_super = VERSION_SUPER(program_version);
92 program.version_major = VERSION_MAJOR(program_version);
93 program.version_minor = VERSION_MINOR(program_version);
94 program.version_patch = VERSION_PATCH(program_version);
95 program.version_ident = program_version;
97 program.version_string = program_version_string;
99 program.log_filename[LOG_OUT_ID] = getLogFilename(LOG_OUT_BASENAME);
100 program.log_filename[LOG_ERR_ID] = getLogFilename(LOG_ERR_BASENAME);
101 program.log_file[LOG_OUT_ID] = program.log_file_default[LOG_OUT_ID] = stdout;
102 program.log_file[LOG_ERR_ID] = program.log_file_default[LOG_ERR_ID] = stderr;
104 program.headless = FALSE;
107 void InitNetworkInfo(boolean enabled, boolean connected, boolean serveronly,
108 char *server_host, int server_port)
110 network.enabled = enabled;
111 network.connected = connected;
112 network.serveronly = serveronly;
114 network.server_host = server_host;
115 network.server_port = server_port;
118 void InitRuntimeInfo()
120 runtime.uses_touch_device = FALSE;
123 void InitScoresInfo(void)
125 char *global_scores_dir = getPath2(getCommonDataDir(), SCORES_DIRECTORY);
127 program.global_scores = directoryExists(global_scores_dir);
128 program.many_scores_per_name = !program.global_scores;
133 if (program.global_scores)
135 Error(ERR_DEBUG, "Using global, multi-user scores directory '%s'.",
137 Error(ERR_DEBUG, "Remove to enable single-user scores directory.");
138 Error(ERR_DEBUG, "(This enables multipe score entries per user.)");
142 Error(ERR_DEBUG, "Using private, single-user scores directory.");
147 free(global_scores_dir);
150 void SetWindowTitle(void)
152 program.window_title = program.window_title_function();
157 void InitWindowTitleFunction(char *(*window_title_function)(void))
159 program.window_title_function = window_title_function;
162 void InitExitMessageFunction(void (*exit_message_function)(char *, va_list))
164 program.exit_message_function = exit_message_function;
167 void InitExitFunction(void (*exit_function)(int))
169 program.exit_function = exit_function;
171 // set signal handlers to custom exit function
172 // signal(SIGINT, exit_function);
173 signal(SIGTERM, exit_function);
175 // set exit function to automatically cleanup SDL stuff after exit()
179 void InitPlatformDependentStuff(void)
181 // this is initialized in GetOptions(), but may already be used before
182 options.verbose = TRUE;
186 int sdl_init_flags = SDL_INIT_EVENTS | SDL_INIT_NOPARACHUTE;
188 if (SDL_Init(sdl_init_flags) < 0)
189 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
194 void ClosePlatformDependentStuff(void)
199 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
200 int real_sx, int real_sy,
201 int full_sxsize, int full_sysize,
202 Bitmap *field_save_buffer)
208 gfx.real_sx = real_sx;
209 gfx.real_sy = real_sy;
210 gfx.full_sxsize = full_sxsize;
211 gfx.full_sysize = full_sysize;
213 gfx.field_save_buffer = field_save_buffer;
215 SetDrawDeactivationMask(REDRAW_NONE); // do not deactivate drawing
216 SetDrawBackgroundMask(REDRAW_NONE); // deactivate masked drawing
219 void InitGfxTileSizeInfo(int game_tile_size, int standard_tile_size)
221 gfx.game_tile_size = game_tile_size;
222 gfx.standard_tile_size = standard_tile_size;
225 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
233 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
241 void InitGfxDoor3Info(int ex, int ey, int exsize, int eysize)
249 void InitGfxWindowInfo(int win_xsize, int win_ysize)
251 if (win_xsize != gfx.win_xsize || win_ysize != gfx.win_ysize)
253 ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize);
255 ReCreateBitmap(&gfx.final_screen_bitmap, win_xsize, win_ysize);
257 ReCreateBitmap(&gfx.fade_bitmap_backup, win_xsize, win_ysize);
258 ReCreateBitmap(&gfx.fade_bitmap_source, win_xsize, win_ysize);
259 ReCreateBitmap(&gfx.fade_bitmap_target, win_xsize, win_ysize);
260 ReCreateBitmap(&gfx.fade_bitmap_black, win_xsize, win_ysize);
262 ClearRectangle(gfx.fade_bitmap_black, 0, 0, win_xsize, win_ysize);
265 gfx.win_xsize = win_xsize;
266 gfx.win_ysize = win_ysize;
268 gfx.background_bitmap_mask = REDRAW_NONE;
271 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
273 // currently only used by MSDOS code to alloc VRAM buffer, if available
274 // 2009-03-24: also (temporarily?) used for overlapping blit workaround
275 gfx.scrollbuffer_width = scrollbuffer_width;
276 gfx.scrollbuffer_height = scrollbuffer_height;
279 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
281 gfx.clipping_enabled = enabled;
284 gfx.clip_width = width;
285 gfx.clip_height = height;
288 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
290 gfx.draw_busy_anim_function = draw_busy_anim_function;
293 void InitGfxDrawGlobalAnimFunction(void (*draw_global_anim_function)(int, int))
295 gfx.draw_global_anim_function = draw_global_anim_function;
298 void InitGfxDrawGlobalBorderFunction(void (*draw_global_border_function)(int))
300 gfx.draw_global_border_function = draw_global_border_function;
303 void InitGfxDrawTileCursorFunction(void (*draw_tile_cursor_function)(int))
305 gfx.draw_tile_cursor_function = draw_tile_cursor_function;
308 void InitGfxCustomArtworkInfo(void)
310 gfx.override_level_graphics = FALSE;
311 gfx.override_level_sounds = FALSE;
312 gfx.override_level_music = FALSE;
314 gfx.draw_init_text = TRUE;
317 void InitGfxOtherSettings(void)
319 gfx.cursor_mode = CURSOR_DEFAULT;
322 void InitTileCursorInfo(void)
324 tile_cursor.enabled = FALSE;
325 tile_cursor.active = FALSE;
326 tile_cursor.moving = FALSE;
328 tile_cursor.xpos = 0;
329 tile_cursor.ypos = 0;
332 tile_cursor.target_x = 0;
333 tile_cursor.target_y = 0;
339 void InitOverlayInfo(void)
341 int nr = GRID_ACTIVE_NR();
344 overlay.enabled = FALSE;
345 overlay.active = FALSE;
347 overlay.show_grid = FALSE;
349 overlay.grid_xsize = setup.touch.grid_xsize[nr];
350 overlay.grid_ysize = setup.touch.grid_ysize[nr];
352 for (x = 0; x < MAX_GRID_XSIZE; x++)
353 for (y = 0; y < MAX_GRID_YSIZE; y++)
354 overlay.grid_button[x][y] = setup.touch.grid_button[nr][x][y];
356 overlay.grid_button_highlight = CHAR_GRID_BUTTON_NONE;
357 overlay.grid_button_action = JOY_NO_ACTION;
359 #if defined(USE_TOUCH_INPUT_OVERLAY)
360 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
361 overlay.enabled = TRUE;
365 void SetTileCursorEnabled(boolean enabled)
367 tile_cursor.enabled = enabled;
370 void SetTileCursorActive(boolean active)
372 tile_cursor.active = active;
375 void SetTileCursorTargetXY(int x, int y)
377 // delayed placement of tile selection cursor at target position
378 // (tile cursor will be moved to target position step by step)
380 tile_cursor.xpos = x;
381 tile_cursor.ypos = y;
382 tile_cursor.target_x = tile_cursor.sx + x * gfx.game_tile_size;
383 tile_cursor.target_y = tile_cursor.sy + y * gfx.game_tile_size;
385 tile_cursor.moving = TRUE;
388 void SetTileCursorXY(int x, int y)
390 // immediate placement of tile selection cursor at target position
392 SetTileCursorTargetXY(x, y);
394 tile_cursor.x = tile_cursor.target_x;
395 tile_cursor.y = tile_cursor.target_y;
397 tile_cursor.moving = FALSE;
400 void SetTileCursorSXSY(int sx, int sy)
406 void SetOverlayEnabled(boolean enabled)
408 overlay.enabled = enabled;
411 void SetOverlayActive(boolean active)
413 overlay.active = active;
416 void SetOverlayShowGrid(boolean show_grid)
418 overlay.show_grid = show_grid;
420 SetOverlayActive(show_grid);
423 SetOverlayEnabled(TRUE);
426 boolean GetOverlayEnabled(void)
428 return overlay.enabled;
431 boolean GetOverlayActive(void)
433 return overlay.active;
436 void SetDrawDeactivationMask(int draw_deactivation_mask)
438 gfx.draw_deactivation_mask = draw_deactivation_mask;
441 int GetDrawDeactivationMask(void)
443 return gfx.draw_deactivation_mask;
446 void SetDrawBackgroundMask(int draw_background_mask)
448 gfx.draw_background_mask = draw_background_mask;
451 static void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
453 if (background_bitmap_tile != NULL)
454 gfx.background_bitmap_mask |= mask;
456 gfx.background_bitmap_mask &= ~mask;
458 if (background_bitmap_tile == NULL) // empty background requested
461 if (mask == REDRAW_ALL)
462 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
463 0, 0, video.width, video.height);
464 else if (mask == REDRAW_FIELD)
465 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
466 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
467 else if (mask == REDRAW_DOOR_1)
468 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
469 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
472 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
474 // remove every mask before setting mask for window
475 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
476 SetBackgroundBitmap(NULL, 0xffff); // !!! FIX THIS !!!
477 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
480 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
482 // remove window area mask before setting mask for main area
483 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
484 SetBackgroundBitmap(NULL, REDRAW_ALL); // !!! FIX THIS !!!
485 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
488 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
490 // remove window area mask before setting mask for door area
491 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
492 SetBackgroundBitmap(NULL, REDRAW_ALL); // !!! FIX THIS !!!
493 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
497 // ============================================================================
499 // ============================================================================
501 static int GetRealDepth(int depth)
503 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
506 static void sysFillRectangle(Bitmap *bitmap, int x, int y,
507 int width, int height, Pixel color)
509 SDLFillRectangle(bitmap, x, y, width, height, color);
511 if (bitmap == backbuffer)
512 SetRedrawMaskFromArea(x, y, width, height);
515 static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
516 int src_x, int src_y, int width, int height,
517 int dst_x, int dst_y, int mask_mode)
519 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
520 dst_x, dst_y, mask_mode);
522 if (dst_bitmap == backbuffer)
523 SetRedrawMaskFromArea(dst_x, dst_y, width, height);
526 void LimitScreenUpdates(boolean enable)
528 SDLLimitScreenUpdates(enable);
531 void InitVideoDefaults(void)
533 video.default_depth = 32;
536 void InitVideoDisplay(void)
538 if (program.headless)
541 SDLInitVideoDisplay();
545 void CloseVideoDisplay(void)
547 KeyboardAutoRepeatOn();
549 SDL_QuitSubSystem(SDL_INIT_VIDEO);
552 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
555 video.height = height;
556 video.depth = GetRealDepth(depth);
558 video.screen_width = width;
559 video.screen_height = height;
560 video.screen_xoffset = 0;
561 video.screen_yoffset = 0;
563 video.fullscreen_available = FULLSCREEN_STATUS;
564 video.fullscreen_enabled = FALSE;
566 video.window_scaling_available = WINDOW_SCALING_STATUS;
568 video.frame_delay = 0;
569 video.frame_delay_value = GAME_FRAME_DELAY;
571 video.shifted_up = FALSE;
572 video.shifted_up_pos = 0;
573 video.shifted_up_pos_last = 0;
574 video.shifted_up_delay = 0;
575 video.shifted_up_delay_value = ONE_SECOND_DELAY / 4;
577 SDLInitVideoBuffer(fullscreen);
579 video.initialized = !program.headless;
584 static void FreeBitmapPointers(Bitmap *bitmap)
589 SDLFreeBitmapPointers(bitmap);
591 checked_free(bitmap->source_filename);
592 bitmap->source_filename = NULL;
595 static void TransferBitmapPointers(Bitmap *src_bitmap,
598 if (src_bitmap == NULL || dst_bitmap == NULL)
601 FreeBitmapPointers(dst_bitmap);
603 *dst_bitmap = *src_bitmap;
606 void FreeBitmap(Bitmap *bitmap)
611 FreeBitmapPointers(bitmap);
616 Bitmap *CreateBitmapStruct(void)
618 return checked_calloc(sizeof(Bitmap));
621 Bitmap *CreateBitmap(int width, int height, int depth)
623 Bitmap *new_bitmap = CreateBitmapStruct();
624 int real_width = MAX(1, width); // prevent zero bitmap width
625 int real_height = MAX(1, height); // prevent zero bitmap height
626 int real_depth = GetRealDepth(depth);
628 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
630 new_bitmap->width = real_width;
631 new_bitmap->height = real_height;
636 void ReCreateBitmap(Bitmap **bitmap, int width, int height)
640 // if new bitmap size fits into old one, no need to re-create it
641 if (width <= (*bitmap)->width &&
642 height <= (*bitmap)->height)
645 // else adjust size so that old and new bitmap size fit into it
646 width = MAX(width, (*bitmap)->width);
647 height = MAX(height, (*bitmap)->height);
650 Bitmap *new_bitmap = CreateBitmap(width, height, DEFAULT_DEPTH);
654 *bitmap = new_bitmap;
658 TransferBitmapPointers(new_bitmap, *bitmap);
664 static void CloseWindow(DrawWindow *window)
669 void SetRedrawMaskFromArea(int x, int y, int width, int height)
673 int x2 = x + width - 1;
674 int y2 = y + height - 1;
676 if (width == 0 || height == 0)
679 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
680 redraw_mask |= REDRAW_FIELD;
681 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
682 redraw_mask |= REDRAW_DOOR_1;
683 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
684 redraw_mask |= REDRAW_DOOR_2;
685 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
686 redraw_mask |= REDRAW_DOOR_3;
688 redraw_mask = REDRAW_ALL;
691 static boolean CheckDrawingArea(int x, int y, int width, int height,
694 if (draw_mask == REDRAW_NONE)
697 if (draw_mask & REDRAW_ALL)
700 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
703 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
706 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
709 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
715 boolean DrawingDeactivatedField(void)
717 if (program.headless)
720 if (gfx.draw_deactivation_mask & REDRAW_FIELD)
726 boolean DrawingDeactivated(int x, int y, int width, int height)
728 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
731 boolean DrawingOnBackground(int x, int y)
733 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
734 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
737 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
738 int *width, int *height, boolean is_dest)
740 int clip_x, clip_y, clip_width, clip_height;
742 if (gfx.clipping_enabled && is_dest) // only clip destination bitmap
744 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
745 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
746 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
747 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
753 clip_width = bitmap->width;
754 clip_height = bitmap->height;
757 // skip if rectangle completely outside bitmap
759 if (*x + *width <= clip_x ||
760 *y + *height <= clip_y ||
761 *x >= clip_x + clip_width ||
762 *y >= clip_y + clip_height)
765 // clip if rectangle overlaps bitmap
769 *width -= clip_x - *x;
772 else if (*x + *width > clip_x + clip_width)
774 *width = clip_x + clip_width - *x;
779 *height -= clip_y - *y;
782 else if (*y + *height > clip_y + clip_height)
784 *height = clip_y + clip_height - *y;
790 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
791 int src_x, int src_y, int width, int height,
792 int dst_x, int dst_y)
794 int dst_x_unclipped = dst_x;
795 int dst_y_unclipped = dst_y;
797 if (program.headless)
800 if (src_bitmap == NULL || dst_bitmap == NULL)
803 if (DrawingDeactivated(dst_x, dst_y, width, height))
806 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
807 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
810 // source x/y might need adjustment if destination x/y was clipped top/left
811 src_x += dst_x - dst_x_unclipped;
812 src_y += dst_y - dst_y_unclipped;
814 // !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!!
815 // !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!!
816 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
817 but is already fixed in SVN and should therefore finally be fixed with
818 the next official SDL release, which is probably version 1.2.14.) */
819 // !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!!
821 if (src_bitmap == dst_bitmap)
823 // needed when blitting directly to same bitmap -- should not be needed with
824 // recent SDL libraries, but apparently does not work in 1.2.11 directly
826 static Bitmap *tmp_bitmap = NULL;
827 static int tmp_bitmap_xsize = 0;
828 static int tmp_bitmap_ysize = 0;
830 // start with largest static bitmaps for initial bitmap size ...
831 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
833 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
834 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
837 // ... and allow for later re-adjustments due to custom artwork bitmaps
838 if (src_bitmap->width > tmp_bitmap_xsize ||
839 src_bitmap->height > tmp_bitmap_ysize)
841 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
842 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
844 FreeBitmap(tmp_bitmap);
849 if (tmp_bitmap == NULL)
850 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
853 sysCopyArea(src_bitmap, tmp_bitmap,
854 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
855 sysCopyArea(tmp_bitmap, dst_bitmap,
856 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
861 sysCopyArea(src_bitmap, dst_bitmap,
862 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
865 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
866 int src_x, int src_y, int src_width, int src_height,
867 int dst_x, int dst_y, int dst_width, int dst_height)
869 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
870 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
871 int dst_xsize = dst_width;
872 int dst_ysize = dst_height;
873 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
874 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
877 for (y = 0; y < src_ysteps; y++)
879 for (x = 0; x < src_xsteps; x++)
881 int draw_x = dst_x + x * src_xsize;
882 int draw_y = dst_y + y * src_ysize;
883 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
884 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
886 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
892 void FadeRectangle(int x, int y, int width, int height,
893 int fade_mode, int fade_delay, int post_delay,
894 void (*draw_border_function)(void))
896 // (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined)
897 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
900 SDLFadeRectangle(x, y, width, height,
901 fade_mode, fade_delay, post_delay, draw_border_function);
904 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
907 if (DrawingDeactivated(x, y, width, height))
910 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
913 sysFillRectangle(bitmap, x, y, width, height, color);
916 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
918 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
921 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
922 int width, int height)
924 if (DrawingOnBackground(x, y))
925 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
927 ClearRectangle(bitmap, x, y, width, height);
930 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
931 int src_x, int src_y, int width, int height,
932 int dst_x, int dst_y)
934 if (DrawingDeactivated(dst_x, dst_y, width, height))
937 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
938 dst_x, dst_y, BLIT_MASKED);
941 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
942 int src_x, int src_y, int width, int height,
943 int dst_x, int dst_y)
945 if (DrawingOnBackground(dst_x, dst_y))
948 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
952 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
956 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
960 void BlitTexture(Bitmap *bitmap,
961 int src_x, int src_y, int width, int height,
962 int dst_x, int dst_y)
967 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
971 void BlitTextureMasked(Bitmap *bitmap,
972 int src_x, int src_y, int width, int height,
973 int dst_x, int dst_y)
978 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
982 void BlitToScreen(Bitmap *bitmap,
983 int src_x, int src_y, int width, int height,
984 int dst_x, int dst_y)
989 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
990 BlitBitmap(bitmap, gfx.final_screen_bitmap, src_x, src_y,
991 width, height, dst_x, dst_y);
993 BlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y);
996 void BlitToScreenMasked(Bitmap *bitmap,
997 int src_x, int src_y, int width, int height,
998 int dst_x, int dst_y)
1003 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
1004 BlitBitmapMasked(bitmap, gfx.final_screen_bitmap, src_x, src_y,
1005 width, height, dst_x, dst_y);
1007 BlitTextureMasked(bitmap, src_x, src_y, width, height, dst_x, dst_y);
1010 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
1013 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
1016 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
1019 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
1022 static void DrawLine(Bitmap *bitmap, int from_x, int from_y,
1023 int to_x, int to_y, Pixel pixel, int line_width)
1027 if (program.headless)
1030 for (x = 0; x < line_width; x++)
1032 for (y = 0; y < line_width; y++)
1034 int dx = x - line_width / 2;
1035 int dy = y - line_width / 2;
1037 if ((x == 0 && y == 0) ||
1038 (x == 0 && y == line_width - 1) ||
1039 (x == line_width - 1 && y == 0) ||
1040 (x == line_width - 1 && y == line_width - 1))
1044 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
1049 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
1054 for (i = 0; i < num_points - 1; i++)
1055 DrawLine(bitmap, points[i].x, points[i].y,
1056 points[i + 1].x, points[i + 1].y, pixel, line_width);
1059 SDLDrawLines(bitmap->surface, points, num_points, pixel);
1063 Pixel GetPixel(Bitmap *bitmap, int x, int y)
1065 if (program.headless)
1068 if (x < 0 || x >= bitmap->width ||
1069 y < 0 || y >= bitmap->height)
1072 return SDLGetPixel(bitmap, x, y);
1075 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
1076 unsigned int color_g, unsigned int color_b)
1078 if (program.headless)
1081 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
1084 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
1086 unsigned int color_r = (color >> 16) & 0xff;
1087 unsigned int color_g = (color >> 8) & 0xff;
1088 unsigned int color_b = (color >> 0) & 0xff;
1090 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
1093 void KeyboardAutoRepeatOn(void)
1095 keyrepeat_status = TRUE;
1098 void KeyboardAutoRepeatOff(void)
1100 keyrepeat_status = FALSE;
1103 boolean SetVideoMode(boolean fullscreen)
1105 return SDLSetVideoMode(fullscreen);
1108 void SetVideoFrameDelay(unsigned int frame_delay_value)
1110 video.frame_delay_value = frame_delay_value;
1113 unsigned int GetVideoFrameDelay(void)
1115 return video.frame_delay_value;
1118 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
1120 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
1121 (!fullscreen && video.fullscreen_enabled))
1122 fullscreen = SetVideoMode(fullscreen);
1127 Bitmap *LoadImage(char *filename)
1131 new_bitmap = SDLLoadImage(filename);
1134 new_bitmap->source_filename = getStringCopy(filename);
1139 Bitmap *LoadCustomImage(char *basename)
1141 char *filename = getCustomImageFilename(basename);
1144 if (filename == NULL)
1145 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
1147 if ((new_bitmap = LoadImage(filename)) == NULL)
1148 Error(ERR_EXIT, "LoadImage('%s') failed: %s", basename, GetError());
1153 void ReloadCustomImage(Bitmap *bitmap, char *basename)
1155 char *filename = getCustomImageFilename(basename);
1158 if (filename == NULL) // (should never happen)
1160 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
1164 if (strEqual(filename, bitmap->source_filename))
1166 // The old and new image are the same (have the same filename and path).
1167 // This usually means that this image does not exist in this graphic set
1168 // and a fallback to the existing image is done.
1173 if ((new_bitmap = LoadImage(filename)) == NULL)
1175 Error(ERR_WARN, "LoadImage('%s') failed: %s", basename, GetError());
1179 if (bitmap->width != new_bitmap->width ||
1180 bitmap->height != new_bitmap->height)
1182 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
1184 FreeBitmap(new_bitmap);
1188 TransferBitmapPointers(new_bitmap, bitmap);
1192 static Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
1194 return SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
1197 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
1199 if (bitmaps[IMG_BITMAP_CUSTOM])
1201 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
1203 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
1206 if (gfx.game_tile_size == gfx.standard_tile_size)
1208 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1213 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
1214 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
1215 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
1217 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
1219 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
1220 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
1223 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1224 int tile_size, boolean create_small_bitmaps)
1226 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1227 Bitmap *tmp_bitmap_final = NULL;
1228 Bitmap *tmp_bitmap_0 = NULL;
1229 Bitmap *tmp_bitmap_1 = NULL;
1230 Bitmap *tmp_bitmap_2 = NULL;
1231 Bitmap *tmp_bitmap_4 = NULL;
1232 Bitmap *tmp_bitmap_8 = NULL;
1233 Bitmap *tmp_bitmap_16 = NULL;
1234 Bitmap *tmp_bitmap_32 = NULL;
1235 int width_final, height_final;
1236 int width_0, height_0;
1237 int width_1, height_1;
1238 int width_2, height_2;
1239 int width_4, height_4;
1240 int width_8, height_8;
1241 int width_16, height_16;
1242 int width_32, height_32;
1243 int old_width, old_height;
1246 print_timestamp_init("CreateScaledBitmaps");
1248 old_width = old_bitmap->width;
1249 old_height = old_bitmap->height;
1251 // calculate new image dimensions for final image size
1252 width_final = old_width * zoom_factor;
1253 height_final = old_height * zoom_factor;
1255 // get image with final size (this might require scaling up)
1256 // ("final" size may result in non-standard tile size image)
1257 if (zoom_factor != 1)
1258 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1260 tmp_bitmap_final = old_bitmap;
1262 UPDATE_BUSY_STATE();
1264 width_0 = width_1 = width_final;
1265 height_0 = height_1 = height_final;
1267 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1269 if (create_small_bitmaps)
1271 // check if we have a non-gameplay tile size image
1272 if (tile_size != gfx.game_tile_size)
1274 // get image with gameplay tile size
1275 width_0 = width_final * gfx.game_tile_size / tile_size;
1276 height_0 = height_final * gfx.game_tile_size / tile_size;
1278 if (width_0 == old_width)
1279 tmp_bitmap_0 = old_bitmap;
1280 else if (width_0 == width_final)
1281 tmp_bitmap_0 = tmp_bitmap_final;
1283 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1285 UPDATE_BUSY_STATE();
1288 // check if we have a non-standard tile size image
1289 if (tile_size != gfx.standard_tile_size)
1291 // get image with standard tile size
1292 width_1 = width_final * gfx.standard_tile_size / tile_size;
1293 height_1 = height_final * gfx.standard_tile_size / tile_size;
1295 if (width_1 == old_width)
1296 tmp_bitmap_1 = old_bitmap;
1297 else if (width_1 == width_final)
1298 tmp_bitmap_1 = tmp_bitmap_final;
1299 else if (width_1 == width_0)
1300 tmp_bitmap_1 = tmp_bitmap_0;
1302 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1304 UPDATE_BUSY_STATE();
1307 // calculate new image dimensions for small images
1308 width_2 = width_1 / 2;
1309 height_2 = height_1 / 2;
1310 width_4 = width_1 / 4;
1311 height_4 = height_1 / 4;
1312 width_8 = width_1 / 8;
1313 height_8 = height_1 / 8;
1314 width_16 = width_1 / 16;
1315 height_16 = height_1 / 16;
1316 width_32 = width_1 / 32;
1317 height_32 = height_1 / 32;
1319 // get image with 1/2 of normal size (for use in the level editor)
1320 if (width_2 == old_width)
1321 tmp_bitmap_2 = old_bitmap;
1323 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1325 UPDATE_BUSY_STATE();
1327 // get image with 1/4 of normal size (for use in the level editor)
1328 if (width_4 == old_width)
1329 tmp_bitmap_4 = old_bitmap;
1331 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1333 UPDATE_BUSY_STATE();
1335 // get image with 1/8 of normal size (for use on the preview screen)
1336 if (width_8 == old_width)
1337 tmp_bitmap_8 = old_bitmap;
1339 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1341 UPDATE_BUSY_STATE();
1343 // get image with 1/16 of normal size (for use on the preview screen)
1344 if (width_16 == old_width)
1345 tmp_bitmap_16 = old_bitmap;
1347 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1349 UPDATE_BUSY_STATE();
1351 // get image with 1/32 of normal size (for use on the preview screen)
1352 if (width_32 == old_width)
1353 tmp_bitmap_32 = old_bitmap;
1355 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1357 UPDATE_BUSY_STATE();
1359 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1360 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1361 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1362 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1363 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1364 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1366 if (width_0 != width_1)
1367 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1369 if (bitmaps[IMG_BITMAP_CUSTOM])
1370 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1372 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1374 boolean free_old_bitmap = TRUE;
1376 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1377 if (bitmaps[i] == old_bitmap)
1378 free_old_bitmap = FALSE;
1380 if (free_old_bitmap)
1382 // copy image filename from old to new standard sized bitmap
1383 bitmaps[IMG_BITMAP_STANDARD]->source_filename =
1384 getStringCopy(old_bitmap->source_filename);
1386 FreeBitmap(old_bitmap);
1391 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1394 UPDATE_BUSY_STATE();
1396 print_timestamp_done("CreateScaledBitmaps");
1399 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1402 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1405 void CreateBitmapTextures(Bitmap **bitmaps)
1407 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1410 void FreeBitmapTextures(Bitmap **bitmaps)
1412 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1415 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1417 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1421 // ----------------------------------------------------------------------------
1422 // mouse pointer functions
1423 // ----------------------------------------------------------------------------
1425 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1427 // XPM image definitions
1428 static const char *cursor_image_none[] =
1430 // width height num_colors chars_per_pixel
1460 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1461 static const char *cursor_image_dot[] =
1463 // width height num_colors chars_per_pixel
1492 static const char **cursor_image_playfield = cursor_image_dot;
1494 // some people complained about a "white dot" on the screen and thought it
1495 // was a graphical error... OK, let's just remove the whole pointer :-)
1496 static const char **cursor_image_playfield = cursor_image_none;
1499 static const int cursor_bit_order = BIT_ORDER_MSB;
1501 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1503 struct MouseCursorInfo *cursor;
1504 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1505 int header_lines = 4;
1508 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1510 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1513 for (y = 0; y < cursor->width; y++)
1515 for (x = 0; x < cursor->height; x++)
1518 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1523 cursor->data[i] = cursor->mask[i] = 0;
1526 switch (image[header_lines + y][x])
1529 cursor->data[i] |= bit_mask;
1530 cursor->mask[i] |= bit_mask;
1534 cursor->mask[i] |= bit_mask;
1543 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1548 void SetMouseCursor(int mode)
1550 static struct MouseCursorInfo *cursor_none = NULL;
1551 static struct MouseCursorInfo *cursor_playfield = NULL;
1552 struct MouseCursorInfo *cursor_new;
1554 if (cursor_none == NULL)
1555 cursor_none = get_cursor_from_image(cursor_image_none);
1557 if (cursor_playfield == NULL)
1558 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1560 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1561 mode == CURSOR_NONE ? cursor_none :
1562 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1564 SDLSetMouseCursor(cursor_new);
1566 gfx.cursor_mode = mode;
1570 // ============================================================================
1572 // ============================================================================
1574 void OpenAudio(void)
1576 // always start with reliable default values
1577 audio.sound_available = FALSE;
1578 audio.music_available = FALSE;
1579 audio.loops_available = FALSE;
1581 audio.sound_enabled = FALSE;
1582 audio.sound_deactivated = FALSE;
1584 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1585 audio.mixer_pid = 0;
1586 audio.device_name = NULL;
1587 audio.device_fd = -1;
1589 audio.num_channels = 0;
1590 audio.music_channel = 0;
1591 audio.first_sound_channel = 0;
1596 void CloseAudio(void)
1600 audio.sound_enabled = FALSE;
1603 void SetAudioMode(boolean enabled)
1605 if (!audio.sound_available)
1608 audio.sound_enabled = enabled;
1612 // ============================================================================
1614 // ============================================================================
1616 boolean PendingEvent(void)
1618 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1621 void WaitEvent(Event *event)
1623 SDLWaitEvent(event);
1626 void PeekEvent(Event *event)
1628 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1631 void CheckQuitEvent(void)
1633 if (SDL_QuitRequested())
1634 program.exit_function(0);
1637 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1639 // key up/down events in SDL2 do not return text characters anymore
1640 return event->keysym.sym;
1643 KeyMod HandleKeyModState(Key key, int key_status)
1645 static KeyMod current_modifiers = KMOD_None;
1647 if (key != KSYM_UNDEFINED) // new key => check for modifier key change
1649 KeyMod new_modifier = KMOD_None;
1654 new_modifier = KMOD_Shift_L;
1657 new_modifier = KMOD_Shift_R;
1659 case KSYM_Control_L:
1660 new_modifier = KMOD_Control_L;
1662 case KSYM_Control_R:
1663 new_modifier = KMOD_Control_R;
1666 new_modifier = KMOD_Meta_L;
1669 new_modifier = KMOD_Meta_R;
1672 new_modifier = KMOD_Alt_L;
1675 new_modifier = KMOD_Alt_R;
1681 if (key_status == KEY_PRESSED)
1682 current_modifiers |= new_modifier;
1684 current_modifiers &= ~new_modifier;
1687 return current_modifiers;
1690 KeyMod GetKeyModState(void)
1692 return (KeyMod)SDL_GetModState();
1695 KeyMod GetKeyModStateFromEvents(void)
1697 /* always use key modifier state as tracked from key events (this is needed
1698 if the modifier key event was injected into the event queue, but the key
1699 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1700 query the keys as held pressed on the keyboard) -- this case is currently
1701 only used to filter out clipboard insert events from "True X-Mouse" tool */
1703 return HandleKeyModState(KSYM_UNDEFINED, 0);
1706 void StartTextInput(int x, int y, int width, int height)
1708 #if defined(HAS_SCREEN_KEYBOARD)
1709 SDL_StartTextInput();
1711 if (y + height > SCREEN_KEYBOARD_POS(video.height))
1713 video.shifted_up_pos = y + height - SCREEN_KEYBOARD_POS(video.height);
1714 video.shifted_up_delay = SDL_GetTicks();
1715 video.shifted_up = TRUE;
1720 void StopTextInput(void)
1722 #if defined(HAS_SCREEN_KEYBOARD)
1723 SDL_StopTextInput();
1725 if (video.shifted_up)
1727 video.shifted_up_pos = 0;
1728 video.shifted_up_delay = SDL_GetTicks();
1729 video.shifted_up = FALSE;
1734 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1736 if (event->type != EVENT_CLIENTMESSAGE)
1739 return TRUE; // the only possible message here is SDL_QUIT
1743 // ============================================================================
1744 // joystick functions
1745 // ============================================================================
1747 void InitJoysticks(void)
1751 #if defined(NO_JOYSTICK)
1752 return; // joysticks generally deactivated by compile-time directive
1755 // always start with reliable default values
1756 joystick.status = JOYSTICK_NOT_AVAILABLE;
1757 for (i = 0; i < MAX_PLAYERS; i++)
1758 joystick.nr[i] = -1; // no joystick configured
1763 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1765 return SDLReadJoystick(nr, x, y, b1, b2);
1768 boolean CheckJoystickOpened(int nr)
1770 return SDLCheckJoystickOpened(nr);
1773 void ClearJoystickState(void)
1775 SDLClearJoystickState();