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 OptionInfo options;
34 struct VideoSystemInfo video;
35 struct AudioSystemInfo audio;
37 struct TileCursorInfo tile_cursor;
38 struct OverlayInfo overlay;
39 struct ArtworkInfo artwork;
40 struct JoystickInfo joystick;
41 struct SetupInfo setup;
43 LevelDirTree *leveldir_first_all = NULL;
44 LevelDirTree *leveldir_first = NULL;
45 LevelDirTree *leveldir_current = NULL;
48 struct LevelSetInfo levelset;
49 struct LevelStats level_stats[MAX_LEVELS];
51 DrawWindow *window = NULL;
52 DrawBuffer *backbuffer = NULL;
53 DrawBuffer *drawto = NULL;
55 int button_status = MB_NOT_PRESSED;
56 boolean motion_status = FALSE;
57 int wheel_steps = DEFAULT_WHEEL_STEPS;
58 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_super = VERSION_SUPER(program_version);
91 program.version_major = VERSION_MAJOR(program_version);
92 program.version_minor = VERSION_MINOR(program_version);
93 program.version_patch = VERSION_PATCH(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 InitNetworkInfo(boolean enabled, boolean connected, boolean serveronly,
107 char *server_host, int server_port)
109 network.enabled = enabled;
110 network.connected = connected;
111 network.serveronly = serveronly;
113 network.server_host = server_host;
114 network.server_port = server_port;
117 void InitScoresInfo(void)
119 char *global_scores_dir = getPath2(getCommonDataDir(), SCORES_DIRECTORY);
121 program.global_scores = directoryExists(global_scores_dir);
122 program.many_scores_per_name = !program.global_scores;
127 if (program.global_scores)
129 Error(ERR_DEBUG, "Using global, multi-user scores directory '%s'.",
131 Error(ERR_DEBUG, "Remove to enable single-user scores directory.");
132 Error(ERR_DEBUG, "(This enables multipe score entries per user.)");
136 Error(ERR_DEBUG, "Using private, single-user scores directory.");
141 free(global_scores_dir);
144 void SetWindowTitle(void)
146 program.window_title = program.window_title_function();
151 void InitWindowTitleFunction(char *(*window_title_function)(void))
153 program.window_title_function = window_title_function;
156 void InitExitMessageFunction(void (*exit_message_function)(char *, va_list))
158 program.exit_message_function = exit_message_function;
161 void InitExitFunction(void (*exit_function)(int))
163 program.exit_function = exit_function;
165 // set signal handlers to custom exit function
166 // signal(SIGINT, exit_function);
167 signal(SIGTERM, exit_function);
169 // set exit function to automatically cleanup SDL stuff after exit()
173 void InitPlatformDependentStuff(void)
175 // this is initialized in GetOptions(), but may already be used before
176 options.verbose = TRUE;
180 int sdl_init_flags = SDL_INIT_EVENTS | SDL_INIT_NOPARACHUTE;
182 if (SDL_Init(sdl_init_flags) < 0)
183 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
188 void ClosePlatformDependentStuff(void)
193 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
194 int real_sx, int real_sy,
195 int full_sxsize, int full_sysize,
196 Bitmap *field_save_buffer)
202 gfx.real_sx = real_sx;
203 gfx.real_sy = real_sy;
204 gfx.full_sxsize = full_sxsize;
205 gfx.full_sysize = full_sysize;
207 gfx.field_save_buffer = field_save_buffer;
209 SetDrawDeactivationMask(REDRAW_NONE); // do not deactivate drawing
210 SetDrawBackgroundMask(REDRAW_NONE); // deactivate masked drawing
213 void InitGfxTileSizeInfo(int game_tile_size, int standard_tile_size)
215 gfx.game_tile_size = game_tile_size;
216 gfx.standard_tile_size = standard_tile_size;
219 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
227 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
235 void InitGfxDoor3Info(int ex, int ey, int exsize, int eysize)
243 void InitGfxWindowInfo(int win_xsize, int win_ysize)
245 if (win_xsize != gfx.win_xsize || win_ysize != gfx.win_ysize)
247 ReCreateBitmap(&gfx.background_bitmap, win_xsize, win_ysize);
249 ReCreateBitmap(&gfx.final_screen_bitmap, win_xsize, win_ysize);
251 ReCreateBitmap(&gfx.fade_bitmap_backup, win_xsize, win_ysize);
252 ReCreateBitmap(&gfx.fade_bitmap_source, win_xsize, win_ysize);
253 ReCreateBitmap(&gfx.fade_bitmap_target, win_xsize, win_ysize);
254 ReCreateBitmap(&gfx.fade_bitmap_black, win_xsize, win_ysize);
256 ClearRectangle(gfx.fade_bitmap_black, 0, 0, win_xsize, win_ysize);
259 gfx.win_xsize = win_xsize;
260 gfx.win_ysize = win_ysize;
262 gfx.background_bitmap_mask = REDRAW_NONE;
265 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
267 // currently only used by MSDOS code to alloc VRAM buffer, if available
268 // 2009-03-24: also (temporarily?) used for overlapping blit workaround
269 gfx.scrollbuffer_width = scrollbuffer_width;
270 gfx.scrollbuffer_height = scrollbuffer_height;
273 void InitGfxClipRegion(boolean enabled, int x, int y, int width, int height)
275 gfx.clipping_enabled = enabled;
278 gfx.clip_width = width;
279 gfx.clip_height = height;
282 void InitGfxDrawBusyAnimFunction(void (*draw_busy_anim_function)(void))
284 gfx.draw_busy_anim_function = draw_busy_anim_function;
287 void InitGfxDrawGlobalAnimFunction(void (*draw_global_anim_function)(int, int))
289 gfx.draw_global_anim_function = draw_global_anim_function;
292 void InitGfxDrawGlobalBorderFunction(void (*draw_global_border_function)(int))
294 gfx.draw_global_border_function = draw_global_border_function;
297 void InitGfxDrawTileCursorFunction(void (*draw_tile_cursor_function)(int))
299 gfx.draw_tile_cursor_function = draw_tile_cursor_function;
302 void InitGfxCustomArtworkInfo(void)
304 gfx.override_level_graphics = FALSE;
305 gfx.override_level_sounds = FALSE;
306 gfx.override_level_music = FALSE;
308 gfx.draw_init_text = TRUE;
311 void InitGfxOtherSettings(void)
313 gfx.cursor_mode = CURSOR_DEFAULT;
316 void InitTileCursorInfo(void)
318 tile_cursor.enabled = FALSE;
319 tile_cursor.active = FALSE;
320 tile_cursor.moving = FALSE;
322 tile_cursor.xpos = 0;
323 tile_cursor.ypos = 0;
326 tile_cursor.target_x = 0;
327 tile_cursor.target_y = 0;
333 void InitOverlayInfo(void)
335 int nr = GRID_ACTIVE_NR();
338 overlay.enabled = FALSE;
339 overlay.active = FALSE;
341 overlay.show_grid = FALSE;
343 overlay.grid_xsize = setup.touch.grid_xsize[nr];
344 overlay.grid_ysize = setup.touch.grid_ysize[nr];
346 for (x = 0; x < MAX_GRID_XSIZE; x++)
347 for (y = 0; y < MAX_GRID_YSIZE; y++)
348 overlay.grid_button[x][y] = setup.touch.grid_button[nr][x][y];
350 overlay.grid_button_highlight = CHAR_GRID_BUTTON_NONE;
351 overlay.grid_button_action = JOY_NO_ACTION;
353 #if defined(USE_TOUCH_INPUT_OVERLAY)
354 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
355 overlay.enabled = TRUE;
359 void SetTileCursorEnabled(boolean enabled)
361 tile_cursor.enabled = enabled;
364 void SetTileCursorActive(boolean active)
366 tile_cursor.active = active;
369 void SetTileCursorTargetXY(int x, int y)
371 // delayed placement of tile selection cursor at target position
372 // (tile cursor will be moved to target position step by step)
374 tile_cursor.xpos = x;
375 tile_cursor.ypos = y;
376 tile_cursor.target_x = tile_cursor.sx + x * gfx.game_tile_size;
377 tile_cursor.target_y = tile_cursor.sy + y * gfx.game_tile_size;
379 tile_cursor.moving = TRUE;
382 void SetTileCursorXY(int x, int y)
384 // immediate placement of tile selection cursor at target position
386 SetTileCursorTargetXY(x, y);
388 tile_cursor.x = tile_cursor.target_x;
389 tile_cursor.y = tile_cursor.target_y;
391 tile_cursor.moving = FALSE;
394 void SetTileCursorSXSY(int sx, int sy)
400 void SetOverlayEnabled(boolean enabled)
402 overlay.enabled = enabled;
405 void SetOverlayActive(boolean active)
407 overlay.active = active;
410 void SetOverlayShowGrid(boolean show_grid)
412 overlay.show_grid = show_grid;
414 SetOverlayActive(show_grid);
417 SetOverlayEnabled(TRUE);
420 boolean GetOverlayEnabled(void)
422 return overlay.enabled;
425 boolean GetOverlayActive(void)
427 return overlay.active;
430 void SetDrawDeactivationMask(int draw_deactivation_mask)
432 gfx.draw_deactivation_mask = draw_deactivation_mask;
435 int GetDrawDeactivationMask(void)
437 return gfx.draw_deactivation_mask;
440 void SetDrawBackgroundMask(int draw_background_mask)
442 gfx.draw_background_mask = draw_background_mask;
445 static void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
447 if (background_bitmap_tile != NULL)
448 gfx.background_bitmap_mask |= mask;
450 gfx.background_bitmap_mask &= ~mask;
452 if (background_bitmap_tile == NULL) // empty background requested
455 if (mask == REDRAW_ALL)
456 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
457 0, 0, video.width, video.height);
458 else if (mask == REDRAW_FIELD)
459 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
460 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
461 else if (mask == REDRAW_DOOR_1)
462 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
463 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
466 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
468 // remove every mask before setting mask for window
469 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
470 SetBackgroundBitmap(NULL, 0xffff); // !!! FIX THIS !!!
471 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
474 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
476 // remove window area mask before setting mask for main area
477 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
478 SetBackgroundBitmap(NULL, REDRAW_ALL); // !!! FIX THIS !!!
479 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
482 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
484 // remove window area mask before setting mask for door area
485 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
486 SetBackgroundBitmap(NULL, REDRAW_ALL); // !!! FIX THIS !!!
487 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
491 // ============================================================================
493 // ============================================================================
495 static int GetRealDepth(int depth)
497 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
500 static void sysFillRectangle(Bitmap *bitmap, int x, int y,
501 int width, int height, Pixel color)
503 SDLFillRectangle(bitmap, x, y, width, height, color);
505 if (bitmap == backbuffer)
506 SetRedrawMaskFromArea(x, y, width, height);
509 static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
510 int src_x, int src_y, int width, int height,
511 int dst_x, int dst_y, int mask_mode)
513 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
514 dst_x, dst_y, mask_mode);
516 if (dst_bitmap == backbuffer)
517 SetRedrawMaskFromArea(dst_x, dst_y, width, height);
520 void LimitScreenUpdates(boolean enable)
522 SDLLimitScreenUpdates(enable);
525 void InitVideoDefaults(void)
527 video.default_depth = 32;
530 void InitVideoDisplay(void)
532 if (program.headless)
535 SDLInitVideoDisplay();
539 void CloseVideoDisplay(void)
541 KeyboardAutoRepeatOn();
543 SDL_QuitSubSystem(SDL_INIT_VIDEO);
546 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
549 video.height = height;
550 video.depth = GetRealDepth(depth);
552 video.screen_width = width;
553 video.screen_height = height;
554 video.screen_xoffset = 0;
555 video.screen_yoffset = 0;
557 video.fullscreen_available = FULLSCREEN_STATUS;
558 video.fullscreen_enabled = FALSE;
560 video.window_scaling_available = WINDOW_SCALING_STATUS;
562 video.frame_delay = 0;
563 video.frame_delay_value = GAME_FRAME_DELAY;
565 video.shifted_up = FALSE;
566 video.shifted_up_pos = 0;
567 video.shifted_up_pos_last = 0;
568 video.shifted_up_delay = 0;
569 video.shifted_up_delay_value = ONE_SECOND_DELAY / 4;
571 SDLInitVideoBuffer(fullscreen);
573 video.initialized = !program.headless;
578 static void FreeBitmapPointers(Bitmap *bitmap)
583 SDLFreeBitmapPointers(bitmap);
585 checked_free(bitmap->source_filename);
586 bitmap->source_filename = NULL;
589 static void TransferBitmapPointers(Bitmap *src_bitmap,
592 if (src_bitmap == NULL || dst_bitmap == NULL)
595 FreeBitmapPointers(dst_bitmap);
597 *dst_bitmap = *src_bitmap;
600 void FreeBitmap(Bitmap *bitmap)
605 FreeBitmapPointers(bitmap);
610 Bitmap *CreateBitmapStruct(void)
612 return checked_calloc(sizeof(Bitmap));
615 Bitmap *CreateBitmap(int width, int height, int depth)
617 Bitmap *new_bitmap = CreateBitmapStruct();
618 int real_width = MAX(1, width); // prevent zero bitmap width
619 int real_height = MAX(1, height); // prevent zero bitmap height
620 int real_depth = GetRealDepth(depth);
622 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
624 new_bitmap->width = real_width;
625 new_bitmap->height = real_height;
630 void ReCreateBitmap(Bitmap **bitmap, int width, int height)
634 // if new bitmap size fits into old one, no need to re-create it
635 if (width <= (*bitmap)->width &&
636 height <= (*bitmap)->height)
639 // else adjust size so that old and new bitmap size fit into it
640 width = MAX(width, (*bitmap)->width);
641 height = MAX(height, (*bitmap)->height);
644 Bitmap *new_bitmap = CreateBitmap(width, height, DEFAULT_DEPTH);
648 *bitmap = new_bitmap;
652 TransferBitmapPointers(new_bitmap, *bitmap);
658 static void CloseWindow(DrawWindow *window)
663 void SetRedrawMaskFromArea(int x, int y, int width, int height)
667 int x2 = x + width - 1;
668 int y2 = y + height - 1;
670 if (width == 0 || height == 0)
673 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
674 redraw_mask |= REDRAW_FIELD;
675 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
676 redraw_mask |= REDRAW_DOOR_1;
677 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
678 redraw_mask |= REDRAW_DOOR_2;
679 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
680 redraw_mask |= REDRAW_DOOR_3;
682 redraw_mask = REDRAW_ALL;
685 static boolean CheckDrawingArea(int x, int y, int width, int height,
688 if (draw_mask == REDRAW_NONE)
691 if (draw_mask & REDRAW_ALL)
694 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
697 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
700 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
703 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
709 boolean DrawingDeactivatedField(void)
711 if (program.headless)
714 if (gfx.draw_deactivation_mask & REDRAW_FIELD)
720 boolean DrawingDeactivated(int x, int y, int width, int height)
722 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
725 boolean DrawingOnBackground(int x, int y)
727 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
728 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
731 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
732 int *width, int *height, boolean is_dest)
734 int clip_x, clip_y, clip_width, clip_height;
736 if (gfx.clipping_enabled && is_dest) // only clip destination bitmap
738 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
739 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
740 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
741 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
747 clip_width = bitmap->width;
748 clip_height = bitmap->height;
751 // skip if rectangle completely outside bitmap
753 if (*x + *width <= clip_x ||
754 *y + *height <= clip_y ||
755 *x >= clip_x + clip_width ||
756 *y >= clip_y + clip_height)
759 // clip if rectangle overlaps bitmap
763 *width -= clip_x - *x;
766 else if (*x + *width > clip_x + clip_width)
768 *width = clip_x + clip_width - *x;
773 *height -= clip_y - *y;
776 else if (*y + *height > clip_y + clip_height)
778 *height = clip_y + clip_height - *y;
784 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
785 int src_x, int src_y, int width, int height,
786 int dst_x, int dst_y)
788 int dst_x_unclipped = dst_x;
789 int dst_y_unclipped = dst_y;
791 if (program.headless)
794 if (src_bitmap == NULL || dst_bitmap == NULL)
797 if (DrawingDeactivated(dst_x, dst_y, width, height))
800 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
801 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
804 // source x/y might need adjustment if destination x/y was clipped top/left
805 src_x += dst_x - dst_x_unclipped;
806 src_y += dst_y - dst_y_unclipped;
808 // !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!!
809 // !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!!
810 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
811 but is already fixed in SVN and should therefore finally be fixed with
812 the next official SDL release, which is probably version 1.2.14.) */
813 // !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!!
815 if (src_bitmap == dst_bitmap)
817 // needed when blitting directly to same bitmap -- should not be needed with
818 // recent SDL libraries, but apparently does not work in 1.2.11 directly
820 static Bitmap *tmp_bitmap = NULL;
821 static int tmp_bitmap_xsize = 0;
822 static int tmp_bitmap_ysize = 0;
824 // start with largest static bitmaps for initial bitmap size ...
825 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
827 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
828 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
831 // ... and allow for later re-adjustments due to custom artwork bitmaps
832 if (src_bitmap->width > tmp_bitmap_xsize ||
833 src_bitmap->height > tmp_bitmap_ysize)
835 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
836 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
838 FreeBitmap(tmp_bitmap);
843 if (tmp_bitmap == NULL)
844 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
847 sysCopyArea(src_bitmap, tmp_bitmap,
848 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
849 sysCopyArea(tmp_bitmap, dst_bitmap,
850 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
855 sysCopyArea(src_bitmap, dst_bitmap,
856 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
859 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
860 int src_x, int src_y, int src_width, int src_height,
861 int dst_x, int dst_y, int dst_width, int dst_height)
863 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
864 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
865 int dst_xsize = dst_width;
866 int dst_ysize = dst_height;
867 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
868 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
871 for (y = 0; y < src_ysteps; y++)
873 for (x = 0; x < src_xsteps; x++)
875 int draw_x = dst_x + x * src_xsize;
876 int draw_y = dst_y + y * src_ysize;
877 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
878 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
880 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
886 void FadeRectangle(int x, int y, int width, int height,
887 int fade_mode, int fade_delay, int post_delay,
888 void (*draw_border_function)(void))
890 // (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined)
891 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
894 SDLFadeRectangle(x, y, width, height,
895 fade_mode, fade_delay, post_delay, draw_border_function);
898 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
901 if (DrawingDeactivated(x, y, width, height))
904 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
907 sysFillRectangle(bitmap, x, y, width, height, color);
910 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
912 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
915 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
916 int width, int height)
918 if (DrawingOnBackground(x, y))
919 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
921 ClearRectangle(bitmap, x, y, width, height);
924 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
925 int src_x, int src_y, int width, int height,
926 int dst_x, int dst_y)
928 if (DrawingDeactivated(dst_x, dst_y, width, height))
931 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
932 dst_x, dst_y, BLIT_MASKED);
935 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
936 int src_x, int src_y, int width, int height,
937 int dst_x, int dst_y)
939 if (DrawingOnBackground(dst_x, dst_y))
942 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
946 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
950 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
954 void BlitTexture(Bitmap *bitmap,
955 int src_x, int src_y, int width, int height,
956 int dst_x, int dst_y)
961 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
965 void BlitTextureMasked(Bitmap *bitmap,
966 int src_x, int src_y, int width, int height,
967 int dst_x, int dst_y)
972 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
976 void BlitToScreen(Bitmap *bitmap,
977 int src_x, int src_y, int width, int height,
978 int dst_x, int dst_y)
983 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
984 BlitBitmap(bitmap, gfx.final_screen_bitmap, src_x, src_y,
985 width, height, dst_x, dst_y);
987 BlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y);
990 void BlitToScreenMasked(Bitmap *bitmap,
991 int src_x, int src_y, int width, int height,
992 int dst_x, int dst_y)
997 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
998 BlitBitmapMasked(bitmap, gfx.final_screen_bitmap, src_x, src_y,
999 width, height, dst_x, dst_y);
1001 BlitTextureMasked(bitmap, src_x, src_y, width, height, dst_x, dst_y);
1004 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
1007 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
1010 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
1013 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
1016 static void DrawLine(Bitmap *bitmap, int from_x, int from_y,
1017 int to_x, int to_y, Pixel pixel, int line_width)
1021 if (program.headless)
1024 for (x = 0; x < line_width; x++)
1026 for (y = 0; y < line_width; y++)
1028 int dx = x - line_width / 2;
1029 int dy = y - line_width / 2;
1031 if ((x == 0 && y == 0) ||
1032 (x == 0 && y == line_width - 1) ||
1033 (x == line_width - 1 && y == 0) ||
1034 (x == line_width - 1 && y == line_width - 1))
1038 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
1043 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
1048 for (i = 0; i < num_points - 1; i++)
1049 DrawLine(bitmap, points[i].x, points[i].y,
1050 points[i + 1].x, points[i + 1].y, pixel, line_width);
1053 SDLDrawLines(bitmap->surface, points, num_points, pixel);
1057 Pixel GetPixel(Bitmap *bitmap, int x, int y)
1059 if (program.headless)
1062 if (x < 0 || x >= bitmap->width ||
1063 y < 0 || y >= bitmap->height)
1066 return SDLGetPixel(bitmap, x, y);
1069 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
1070 unsigned int color_g, unsigned int color_b)
1072 if (program.headless)
1075 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
1078 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
1080 unsigned int color_r = (color >> 16) & 0xff;
1081 unsigned int color_g = (color >> 8) & 0xff;
1082 unsigned int color_b = (color >> 0) & 0xff;
1084 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
1087 void KeyboardAutoRepeatOn(void)
1089 keyrepeat_status = TRUE;
1092 void KeyboardAutoRepeatOff(void)
1094 keyrepeat_status = FALSE;
1097 boolean SetVideoMode(boolean fullscreen)
1099 return SDLSetVideoMode(fullscreen);
1102 void SetVideoFrameDelay(unsigned int frame_delay_value)
1104 video.frame_delay_value = frame_delay_value;
1107 unsigned int GetVideoFrameDelay(void)
1109 return video.frame_delay_value;
1112 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
1114 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
1115 (!fullscreen && video.fullscreen_enabled))
1116 fullscreen = SetVideoMode(fullscreen);
1121 Bitmap *LoadImage(char *filename)
1125 new_bitmap = SDLLoadImage(filename);
1128 new_bitmap->source_filename = getStringCopy(filename);
1133 Bitmap *LoadCustomImage(char *basename)
1135 char *filename = getCustomImageFilename(basename);
1138 if (filename == NULL)
1139 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
1141 if ((new_bitmap = LoadImage(filename)) == NULL)
1142 Error(ERR_EXIT, "LoadImage('%s') failed: %s", basename, GetError());
1147 void ReloadCustomImage(Bitmap *bitmap, char *basename)
1149 char *filename = getCustomImageFilename(basename);
1152 if (filename == NULL) // (should never happen)
1154 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
1158 if (strEqual(filename, bitmap->source_filename))
1160 // The old and new image are the same (have the same filename and path).
1161 // This usually means that this image does not exist in this graphic set
1162 // and a fallback to the existing image is done.
1167 if ((new_bitmap = LoadImage(filename)) == NULL)
1169 Error(ERR_WARN, "LoadImage('%s') failed: %s", basename, GetError());
1173 if (bitmap->width != new_bitmap->width ||
1174 bitmap->height != new_bitmap->height)
1176 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
1178 FreeBitmap(new_bitmap);
1182 TransferBitmapPointers(new_bitmap, bitmap);
1186 static Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
1188 return SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
1191 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
1193 if (bitmaps[IMG_BITMAP_CUSTOM])
1195 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
1197 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
1200 if (gfx.game_tile_size == gfx.standard_tile_size)
1202 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1207 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
1208 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
1209 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
1211 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
1213 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
1214 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
1217 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1218 int tile_size, boolean create_small_bitmaps)
1220 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1221 Bitmap *tmp_bitmap_final = NULL;
1222 Bitmap *tmp_bitmap_0 = NULL;
1223 Bitmap *tmp_bitmap_1 = NULL;
1224 Bitmap *tmp_bitmap_2 = NULL;
1225 Bitmap *tmp_bitmap_4 = NULL;
1226 Bitmap *tmp_bitmap_8 = NULL;
1227 Bitmap *tmp_bitmap_16 = NULL;
1228 Bitmap *tmp_bitmap_32 = NULL;
1229 int width_final, height_final;
1230 int width_0, height_0;
1231 int width_1, height_1;
1232 int width_2, height_2;
1233 int width_4, height_4;
1234 int width_8, height_8;
1235 int width_16, height_16;
1236 int width_32, height_32;
1237 int old_width, old_height;
1240 print_timestamp_init("CreateScaledBitmaps");
1242 old_width = old_bitmap->width;
1243 old_height = old_bitmap->height;
1245 // calculate new image dimensions for final image size
1246 width_final = old_width * zoom_factor;
1247 height_final = old_height * zoom_factor;
1249 // get image with final size (this might require scaling up)
1250 // ("final" size may result in non-standard tile size image)
1251 if (zoom_factor != 1)
1252 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1254 tmp_bitmap_final = old_bitmap;
1256 UPDATE_BUSY_STATE();
1258 width_0 = width_1 = width_final;
1259 height_0 = height_1 = height_final;
1261 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1263 if (create_small_bitmaps)
1265 // check if we have a non-gameplay tile size image
1266 if (tile_size != gfx.game_tile_size)
1268 // get image with gameplay tile size
1269 width_0 = width_final * gfx.game_tile_size / tile_size;
1270 height_0 = height_final * gfx.game_tile_size / tile_size;
1272 if (width_0 == old_width)
1273 tmp_bitmap_0 = old_bitmap;
1274 else if (width_0 == width_final)
1275 tmp_bitmap_0 = tmp_bitmap_final;
1277 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1279 UPDATE_BUSY_STATE();
1282 // check if we have a non-standard tile size image
1283 if (tile_size != gfx.standard_tile_size)
1285 // get image with standard tile size
1286 width_1 = width_final * gfx.standard_tile_size / tile_size;
1287 height_1 = height_final * gfx.standard_tile_size / tile_size;
1289 if (width_1 == old_width)
1290 tmp_bitmap_1 = old_bitmap;
1291 else if (width_1 == width_final)
1292 tmp_bitmap_1 = tmp_bitmap_final;
1293 else if (width_1 == width_0)
1294 tmp_bitmap_1 = tmp_bitmap_0;
1296 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1298 UPDATE_BUSY_STATE();
1301 // calculate new image dimensions for small images
1302 width_2 = width_1 / 2;
1303 height_2 = height_1 / 2;
1304 width_4 = width_1 / 4;
1305 height_4 = height_1 / 4;
1306 width_8 = width_1 / 8;
1307 height_8 = height_1 / 8;
1308 width_16 = width_1 / 16;
1309 height_16 = height_1 / 16;
1310 width_32 = width_1 / 32;
1311 height_32 = height_1 / 32;
1313 // get image with 1/2 of normal size (for use in the level editor)
1314 if (width_2 == old_width)
1315 tmp_bitmap_2 = old_bitmap;
1317 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1319 UPDATE_BUSY_STATE();
1321 // get image with 1/4 of normal size (for use in the level editor)
1322 if (width_4 == old_width)
1323 tmp_bitmap_4 = old_bitmap;
1325 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1327 UPDATE_BUSY_STATE();
1329 // get image with 1/8 of normal size (for use on the preview screen)
1330 if (width_8 == old_width)
1331 tmp_bitmap_8 = old_bitmap;
1333 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1335 UPDATE_BUSY_STATE();
1337 // get image with 1/16 of normal size (for use on the preview screen)
1338 if (width_16 == old_width)
1339 tmp_bitmap_16 = old_bitmap;
1341 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1343 UPDATE_BUSY_STATE();
1345 // get image with 1/32 of normal size (for use on the preview screen)
1346 if (width_32 == old_width)
1347 tmp_bitmap_32 = old_bitmap;
1349 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1351 UPDATE_BUSY_STATE();
1353 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1354 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1355 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1356 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1357 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1358 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1360 if (width_0 != width_1)
1361 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1363 if (bitmaps[IMG_BITMAP_CUSTOM])
1364 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1366 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1368 boolean free_old_bitmap = TRUE;
1370 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1371 if (bitmaps[i] == old_bitmap)
1372 free_old_bitmap = FALSE;
1374 if (free_old_bitmap)
1376 // copy image filename from old to new standard sized bitmap
1377 bitmaps[IMG_BITMAP_STANDARD]->source_filename =
1378 getStringCopy(old_bitmap->source_filename);
1380 FreeBitmap(old_bitmap);
1385 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1388 UPDATE_BUSY_STATE();
1390 print_timestamp_done("CreateScaledBitmaps");
1393 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1396 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1399 void CreateBitmapTextures(Bitmap **bitmaps)
1401 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1404 void FreeBitmapTextures(Bitmap **bitmaps)
1406 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1409 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1411 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1415 // ----------------------------------------------------------------------------
1416 // mouse pointer functions
1417 // ----------------------------------------------------------------------------
1419 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1421 // XPM image definitions
1422 static const char *cursor_image_none[] =
1424 // width height num_colors chars_per_pixel
1454 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1455 static const char *cursor_image_dot[] =
1457 // width height num_colors chars_per_pixel
1486 static const char **cursor_image_playfield = cursor_image_dot;
1488 // some people complained about a "white dot" on the screen and thought it
1489 // was a graphical error... OK, let's just remove the whole pointer :-)
1490 static const char **cursor_image_playfield = cursor_image_none;
1493 static const int cursor_bit_order = BIT_ORDER_MSB;
1495 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1497 struct MouseCursorInfo *cursor;
1498 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1499 int header_lines = 4;
1502 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1504 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1507 for (y = 0; y < cursor->width; y++)
1509 for (x = 0; x < cursor->height; x++)
1512 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1517 cursor->data[i] = cursor->mask[i] = 0;
1520 switch (image[header_lines + y][x])
1523 cursor->data[i] |= bit_mask;
1524 cursor->mask[i] |= bit_mask;
1528 cursor->mask[i] |= bit_mask;
1537 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1542 void SetMouseCursor(int mode)
1544 static struct MouseCursorInfo *cursor_none = NULL;
1545 static struct MouseCursorInfo *cursor_playfield = NULL;
1546 struct MouseCursorInfo *cursor_new;
1548 if (cursor_none == NULL)
1549 cursor_none = get_cursor_from_image(cursor_image_none);
1551 if (cursor_playfield == NULL)
1552 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1554 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1555 mode == CURSOR_NONE ? cursor_none :
1556 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1558 SDLSetMouseCursor(cursor_new);
1560 gfx.cursor_mode = mode;
1564 // ============================================================================
1566 // ============================================================================
1568 void OpenAudio(void)
1570 // always start with reliable default values
1571 audio.sound_available = FALSE;
1572 audio.music_available = FALSE;
1573 audio.loops_available = FALSE;
1575 audio.sound_enabled = FALSE;
1576 audio.sound_deactivated = FALSE;
1578 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1579 audio.mixer_pid = 0;
1580 audio.device_name = NULL;
1581 audio.device_fd = -1;
1583 audio.num_channels = 0;
1584 audio.music_channel = 0;
1585 audio.first_sound_channel = 0;
1590 void CloseAudio(void)
1594 audio.sound_enabled = FALSE;
1597 void SetAudioMode(boolean enabled)
1599 if (!audio.sound_available)
1602 audio.sound_enabled = enabled;
1606 // ============================================================================
1608 // ============================================================================
1610 boolean PendingEvent(void)
1612 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1615 void WaitEvent(Event *event)
1617 SDLWaitEvent(event);
1620 void PeekEvent(Event *event)
1622 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1625 void CheckQuitEvent(void)
1627 if (SDL_QuitRequested())
1628 program.exit_function(0);
1631 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1633 // key up/down events in SDL2 do not return text characters anymore
1634 return event->keysym.sym;
1637 KeyMod HandleKeyModState(Key key, int key_status)
1639 static KeyMod current_modifiers = KMOD_None;
1641 if (key != KSYM_UNDEFINED) // new key => check for modifier key change
1643 KeyMod new_modifier = KMOD_None;
1648 new_modifier = KMOD_Shift_L;
1651 new_modifier = KMOD_Shift_R;
1653 case KSYM_Control_L:
1654 new_modifier = KMOD_Control_L;
1656 case KSYM_Control_R:
1657 new_modifier = KMOD_Control_R;
1660 new_modifier = KMOD_Meta_L;
1663 new_modifier = KMOD_Meta_R;
1666 new_modifier = KMOD_Alt_L;
1669 new_modifier = KMOD_Alt_R;
1675 if (key_status == KEY_PRESSED)
1676 current_modifiers |= new_modifier;
1678 current_modifiers &= ~new_modifier;
1681 return current_modifiers;
1684 KeyMod GetKeyModState(void)
1686 return (KeyMod)SDL_GetModState();
1689 KeyMod GetKeyModStateFromEvents(void)
1691 /* always use key modifier state as tracked from key events (this is needed
1692 if the modifier key event was injected into the event queue, but the key
1693 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1694 query the keys as held pressed on the keyboard) -- this case is currently
1695 only used to filter out clipboard insert events from "True X-Mouse" tool */
1697 return HandleKeyModState(KSYM_UNDEFINED, 0);
1700 void StartTextInput(int x, int y, int width, int height)
1702 #if defined(HAS_SCREEN_KEYBOARD)
1703 SDL_StartTextInput();
1705 if (y + height > SCREEN_KEYBOARD_POS(video.height))
1707 video.shifted_up_pos = y + height - SCREEN_KEYBOARD_POS(video.height);
1708 video.shifted_up_delay = SDL_GetTicks();
1709 video.shifted_up = TRUE;
1714 void StopTextInput(void)
1716 #if defined(HAS_SCREEN_KEYBOARD)
1717 SDL_StopTextInput();
1719 if (video.shifted_up)
1721 video.shifted_up_pos = 0;
1722 video.shifted_up_delay = SDL_GetTicks();
1723 video.shifted_up = FALSE;
1728 boolean CheckCloseWindowEvent(ClientMessageEvent *event)
1730 if (event->type != EVENT_CLIENTMESSAGE)
1733 return TRUE; // the only possible message here is SDL_QUIT
1737 // ============================================================================
1738 // joystick functions
1739 // ============================================================================
1741 void InitJoysticks(void)
1745 #if defined(NO_JOYSTICK)
1746 return; // joysticks generally deactivated by compile-time directive
1749 // always start with reliable default values
1750 joystick.status = JOYSTICK_NOT_AVAILABLE;
1751 for (i = 0; i < MAX_PLAYERS; i++)
1752 joystick.nr[i] = -1; // no joystick configured
1757 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1759 return SDLReadJoystick(nr, x, y, b1, b2);
1762 boolean CheckJoystickOpened(int nr)
1764 return SDLCheckJoystickOpened(nr);
1767 void ClearJoystickState(void)
1769 SDLClearJoystickState();