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;
324 void InitTileCursorInfo(void)
326 tile_cursor.enabled = FALSE;
327 tile_cursor.active = FALSE;
328 tile_cursor.moving = FALSE;
330 tile_cursor.xpos = 0;
331 tile_cursor.ypos = 0;
334 tile_cursor.target_x = 0;
335 tile_cursor.target_y = 0;
341 void InitOverlayInfo(void)
343 int nr = GRID_ACTIVE_NR();
346 overlay.enabled = FALSE;
347 overlay.active = FALSE;
349 overlay.show_grid = FALSE;
351 overlay.grid_xsize = setup.touch.grid_xsize[nr];
352 overlay.grid_ysize = setup.touch.grid_ysize[nr];
354 for (x = 0; x < MAX_GRID_XSIZE; x++)
355 for (y = 0; y < MAX_GRID_YSIZE; y++)
356 overlay.grid_button[x][y] = setup.touch.grid_button[nr][x][y];
358 overlay.grid_button_highlight = CHAR_GRID_BUTTON_NONE;
359 overlay.grid_button_action = JOY_NO_ACTION;
361 #if defined(USE_TOUCH_INPUT_OVERLAY)
362 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
363 overlay.enabled = TRUE;
367 void SetTileCursorEnabled(boolean enabled)
369 tile_cursor.enabled = enabled;
372 void SetTileCursorActive(boolean active)
374 tile_cursor.active = active;
377 void SetTileCursorTargetXY(int x, int y)
379 // delayed placement of tile selection cursor at target position
380 // (tile cursor will be moved to target position step by step)
382 tile_cursor.xpos = x;
383 tile_cursor.ypos = y;
384 tile_cursor.target_x = tile_cursor.sx + x * gfx.game_tile_size;
385 tile_cursor.target_y = tile_cursor.sy + y * gfx.game_tile_size;
387 tile_cursor.moving = TRUE;
390 void SetTileCursorXY(int x, int y)
392 // immediate placement of tile selection cursor at target position
394 SetTileCursorTargetXY(x, y);
396 tile_cursor.x = tile_cursor.target_x;
397 tile_cursor.y = tile_cursor.target_y;
399 tile_cursor.moving = FALSE;
402 void SetTileCursorSXSY(int sx, int sy)
408 void SetOverlayEnabled(boolean enabled)
410 overlay.enabled = enabled;
413 void SetOverlayActive(boolean active)
415 overlay.active = active;
418 void SetOverlayShowGrid(boolean show_grid)
420 overlay.show_grid = show_grid;
422 SetOverlayActive(show_grid);
425 SetOverlayEnabled(TRUE);
428 boolean GetOverlayEnabled(void)
430 return overlay.enabled;
433 boolean GetOverlayActive(void)
435 return overlay.active;
438 void SetDrawDeactivationMask(int draw_deactivation_mask)
440 gfx.draw_deactivation_mask = draw_deactivation_mask;
443 int GetDrawDeactivationMask(void)
445 return gfx.draw_deactivation_mask;
448 void SetDrawBackgroundMask(int draw_background_mask)
450 gfx.draw_background_mask = draw_background_mask;
453 static void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
455 if (background_bitmap_tile != NULL)
456 gfx.background_bitmap_mask |= mask;
458 gfx.background_bitmap_mask &= ~mask;
460 if (background_bitmap_tile == NULL) // empty background requested
463 if (mask == REDRAW_ALL)
464 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
465 0, 0, video.width, video.height);
466 else if (mask == REDRAW_FIELD)
467 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
468 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
469 else if (mask == REDRAW_DOOR_1)
470 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
471 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
474 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
476 // remove every mask before setting mask for window
477 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
478 SetBackgroundBitmap(NULL, 0xffff); // !!! FIX THIS !!!
479 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
482 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
484 // remove window area mask before setting mask for main area
485 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
486 SetBackgroundBitmap(NULL, REDRAW_ALL); // !!! FIX THIS !!!
487 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
490 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
492 // remove window area mask before setting mask for door area
493 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
494 SetBackgroundBitmap(NULL, REDRAW_ALL); // !!! FIX THIS !!!
495 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
499 // ============================================================================
501 // ============================================================================
503 static int GetRealDepth(int depth)
505 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
508 static void sysFillRectangle(Bitmap *bitmap, int x, int y,
509 int width, int height, Pixel color)
511 SDLFillRectangle(bitmap, x, y, width, height, color);
513 if (bitmap == backbuffer)
514 SetRedrawMaskFromArea(x, y, width, height);
517 static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
518 int src_x, int src_y, int width, int height,
519 int dst_x, int dst_y, int mask_mode)
521 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
522 dst_x, dst_y, mask_mode);
524 if (dst_bitmap == backbuffer)
525 SetRedrawMaskFromArea(dst_x, dst_y, width, height);
528 void LimitScreenUpdates(boolean enable)
530 SDLLimitScreenUpdates(enable);
533 void InitVideoDefaults(void)
535 video.default_depth = 32;
538 void InitVideoDisplay(void)
540 if (program.headless)
543 SDLInitVideoDisplay();
547 void CloseVideoDisplay(void)
549 KeyboardAutoRepeatOn();
551 SDL_QuitSubSystem(SDL_INIT_VIDEO);
554 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
557 video.height = height;
558 video.depth = GetRealDepth(depth);
560 video.screen_width = width;
561 video.screen_height = height;
562 video.screen_xoffset = 0;
563 video.screen_yoffset = 0;
565 video.fullscreen_available = FULLSCREEN_STATUS;
566 video.fullscreen_enabled = FALSE;
568 video.window_scaling_available = WINDOW_SCALING_STATUS;
570 video.frame_delay = 0;
571 video.frame_delay_value = GAME_FRAME_DELAY;
573 video.shifted_up = FALSE;
574 video.shifted_up_pos = 0;
575 video.shifted_up_pos_last = 0;
576 video.shifted_up_delay = 0;
577 video.shifted_up_delay_value = ONE_SECOND_DELAY / 4;
579 SDLInitVideoBuffer(fullscreen);
581 video.initialized = !program.headless;
586 static void FreeBitmapPointers(Bitmap *bitmap)
591 SDLFreeBitmapPointers(bitmap);
593 checked_free(bitmap->source_filename);
594 bitmap->source_filename = NULL;
597 static void TransferBitmapPointers(Bitmap *src_bitmap,
600 if (src_bitmap == NULL || dst_bitmap == NULL)
603 FreeBitmapPointers(dst_bitmap);
605 *dst_bitmap = *src_bitmap;
608 void FreeBitmap(Bitmap *bitmap)
613 FreeBitmapPointers(bitmap);
618 Bitmap *CreateBitmapStruct(void)
620 return checked_calloc(sizeof(Bitmap));
623 Bitmap *CreateBitmap(int width, int height, int depth)
625 Bitmap *new_bitmap = CreateBitmapStruct();
626 int real_width = MAX(1, width); // prevent zero bitmap width
627 int real_height = MAX(1, height); // prevent zero bitmap height
628 int real_depth = GetRealDepth(depth);
630 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
632 new_bitmap->width = real_width;
633 new_bitmap->height = real_height;
638 void ReCreateBitmap(Bitmap **bitmap, int width, int height)
642 // if new bitmap size fits into old one, no need to re-create it
643 if (width <= (*bitmap)->width &&
644 height <= (*bitmap)->height)
647 // else adjust size so that old and new bitmap size fit into it
648 width = MAX(width, (*bitmap)->width);
649 height = MAX(height, (*bitmap)->height);
652 Bitmap *new_bitmap = CreateBitmap(width, height, DEFAULT_DEPTH);
656 *bitmap = new_bitmap;
660 TransferBitmapPointers(new_bitmap, *bitmap);
666 static void CloseWindow(DrawWindow *window)
671 void SetRedrawMaskFromArea(int x, int y, int width, int height)
675 int x2 = x + width - 1;
676 int y2 = y + height - 1;
678 if (width == 0 || height == 0)
681 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
682 redraw_mask |= REDRAW_FIELD;
683 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
684 redraw_mask |= REDRAW_DOOR_1;
685 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
686 redraw_mask |= REDRAW_DOOR_2;
687 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
688 redraw_mask |= REDRAW_DOOR_3;
690 redraw_mask = REDRAW_ALL;
693 static boolean CheckDrawingArea(int x, int y, int width, int height,
696 if (draw_mask == REDRAW_NONE)
699 if (draw_mask & REDRAW_ALL)
702 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
705 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
708 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
711 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
717 boolean DrawingDeactivatedField(void)
719 if (program.headless)
722 if (gfx.draw_deactivation_mask & REDRAW_FIELD)
728 boolean DrawingDeactivated(int x, int y, int width, int height)
730 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
733 boolean DrawingOnBackground(int x, int y)
735 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
736 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
739 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
740 int *width, int *height, boolean is_dest)
742 int clip_x, clip_y, clip_width, clip_height;
744 if (gfx.clipping_enabled && is_dest) // only clip destination bitmap
746 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
747 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
748 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
749 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
755 clip_width = bitmap->width;
756 clip_height = bitmap->height;
759 // skip if rectangle completely outside bitmap
761 if (*x + *width <= clip_x ||
762 *y + *height <= clip_y ||
763 *x >= clip_x + clip_width ||
764 *y >= clip_y + clip_height)
767 // clip if rectangle overlaps bitmap
771 *width -= clip_x - *x;
774 else if (*x + *width > clip_x + clip_width)
776 *width = clip_x + clip_width - *x;
781 *height -= clip_y - *y;
784 else if (*y + *height > clip_y + clip_height)
786 *height = clip_y + clip_height - *y;
792 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
793 int src_x, int src_y, int width, int height,
794 int dst_x, int dst_y)
796 int dst_x_unclipped = dst_x;
797 int dst_y_unclipped = dst_y;
799 if (program.headless)
802 if (src_bitmap == NULL || dst_bitmap == NULL)
805 if (DrawingDeactivated(dst_x, dst_y, width, height))
808 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
809 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
812 // source x/y might need adjustment if destination x/y was clipped top/left
813 src_x += dst_x - dst_x_unclipped;
814 src_y += dst_y - dst_y_unclipped;
816 // !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!!
817 // !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!!
818 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
819 but is already fixed in SVN and should therefore finally be fixed with
820 the next official SDL release, which is probably version 1.2.14.) */
821 // !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!!
823 if (src_bitmap == dst_bitmap)
825 // needed when blitting directly to same bitmap -- should not be needed with
826 // recent SDL libraries, but apparently does not work in 1.2.11 directly
828 static Bitmap *tmp_bitmap = NULL;
829 static int tmp_bitmap_xsize = 0;
830 static int tmp_bitmap_ysize = 0;
832 // start with largest static bitmaps for initial bitmap size ...
833 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
835 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
836 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
839 // ... and allow for later re-adjustments due to custom artwork bitmaps
840 if (src_bitmap->width > tmp_bitmap_xsize ||
841 src_bitmap->height > tmp_bitmap_ysize)
843 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
844 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
846 FreeBitmap(tmp_bitmap);
851 if (tmp_bitmap == NULL)
852 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
855 sysCopyArea(src_bitmap, tmp_bitmap,
856 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
857 sysCopyArea(tmp_bitmap, dst_bitmap,
858 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
863 sysCopyArea(src_bitmap, dst_bitmap,
864 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
867 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
868 int src_x, int src_y, int src_width, int src_height,
869 int dst_x, int dst_y, int dst_width, int dst_height)
871 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
872 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
873 int dst_xsize = dst_width;
874 int dst_ysize = dst_height;
875 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
876 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
879 for (y = 0; y < src_ysteps; y++)
881 for (x = 0; x < src_xsteps; x++)
883 int draw_x = dst_x + x * src_xsize;
884 int draw_y = dst_y + y * src_ysize;
885 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
886 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
888 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
894 void FadeRectangle(int x, int y, int width, int height,
895 int fade_mode, int fade_delay, int post_delay,
896 void (*draw_border_function)(void))
898 // (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined)
899 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
902 SDLFadeRectangle(x, y, width, height,
903 fade_mode, fade_delay, post_delay, draw_border_function);
906 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
909 if (DrawingDeactivated(x, y, width, height))
912 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
915 sysFillRectangle(bitmap, x, y, width, height, color);
918 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
920 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
923 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
924 int width, int height)
926 if (DrawingOnBackground(x, y))
927 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
929 ClearRectangle(bitmap, x, y, width, height);
932 void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
933 int src_x, int src_y, int width, int height,
934 int dst_x, int dst_y)
936 if (DrawingDeactivated(dst_x, dst_y, width, height))
939 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
940 dst_x, dst_y, BLIT_MASKED);
943 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
944 int src_x, int src_y, int width, int height,
945 int dst_x, int dst_y)
947 if (DrawingOnBackground(dst_x, dst_y))
950 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
954 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
958 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
962 void BlitTexture(Bitmap *bitmap,
963 int src_x, int src_y, int width, int height,
964 int dst_x, int dst_y)
969 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
973 void BlitTextureMasked(Bitmap *bitmap,
974 int src_x, int src_y, int width, int height,
975 int dst_x, int dst_y)
980 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
984 void BlitToScreen(Bitmap *bitmap,
985 int src_x, int src_y, int width, int height,
986 int dst_x, int dst_y)
991 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
992 BlitBitmap(bitmap, gfx.final_screen_bitmap, src_x, src_y,
993 width, height, dst_x, dst_y);
995 BlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y);
998 void BlitToScreenMasked(Bitmap *bitmap,
999 int src_x, int src_y, int width, int height,
1000 int dst_x, int dst_y)
1005 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
1006 BlitBitmapMasked(bitmap, gfx.final_screen_bitmap, src_x, src_y,
1007 width, height, dst_x, dst_y);
1009 BlitTextureMasked(bitmap, src_x, src_y, width, height, dst_x, dst_y);
1012 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
1015 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
1018 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
1021 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
1024 static void DrawLine(Bitmap *bitmap, int from_x, int from_y,
1025 int to_x, int to_y, Pixel pixel, int line_width)
1029 if (program.headless)
1032 for (x = 0; x < line_width; x++)
1034 for (y = 0; y < line_width; y++)
1036 int dx = x - line_width / 2;
1037 int dy = y - line_width / 2;
1039 if ((x == 0 && y == 0) ||
1040 (x == 0 && y == line_width - 1) ||
1041 (x == line_width - 1 && y == 0) ||
1042 (x == line_width - 1 && y == line_width - 1))
1046 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
1051 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
1056 for (i = 0; i < num_points - 1; i++)
1057 DrawLine(bitmap, points[i].x, points[i].y,
1058 points[i + 1].x, points[i + 1].y, pixel, line_width);
1061 SDLDrawLines(bitmap->surface, points, num_points, pixel);
1065 Pixel GetPixel(Bitmap *bitmap, int x, int y)
1067 if (program.headless)
1070 if (x < 0 || x >= bitmap->width ||
1071 y < 0 || y >= bitmap->height)
1074 return SDLGetPixel(bitmap, x, y);
1077 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
1078 unsigned int color_g, unsigned int color_b)
1080 if (program.headless)
1083 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
1086 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
1088 unsigned int color_r = (color >> 16) & 0xff;
1089 unsigned int color_g = (color >> 8) & 0xff;
1090 unsigned int color_b = (color >> 0) & 0xff;
1092 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
1095 void KeyboardAutoRepeatOn(void)
1097 keyrepeat_status = TRUE;
1100 void KeyboardAutoRepeatOff(void)
1102 keyrepeat_status = FALSE;
1105 boolean SetVideoMode(boolean fullscreen)
1107 return SDLSetVideoMode(fullscreen);
1110 void SetVideoFrameDelay(unsigned int frame_delay_value)
1112 video.frame_delay_value = frame_delay_value;
1115 unsigned int GetVideoFrameDelay(void)
1117 return video.frame_delay_value;
1120 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
1122 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
1123 (!fullscreen && video.fullscreen_enabled))
1124 fullscreen = SetVideoMode(fullscreen);
1129 Bitmap *LoadImage(char *filename)
1133 new_bitmap = SDLLoadImage(filename);
1136 new_bitmap->source_filename = getStringCopy(filename);
1141 Bitmap *LoadCustomImage(char *basename)
1143 char *filename = getCustomImageFilename(basename);
1146 if (filename == NULL)
1147 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
1149 if ((new_bitmap = LoadImage(filename)) == NULL)
1150 Error(ERR_EXIT, "LoadImage('%s') failed: %s", basename, GetError());
1155 void ReloadCustomImage(Bitmap *bitmap, char *basename)
1157 char *filename = getCustomImageFilename(basename);
1160 if (filename == NULL) // (should never happen)
1162 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
1166 if (strEqual(filename, bitmap->source_filename))
1168 // The old and new image are the same (have the same filename and path).
1169 // This usually means that this image does not exist in this graphic set
1170 // and a fallback to the existing image is done.
1175 if ((new_bitmap = LoadImage(filename)) == NULL)
1177 Error(ERR_WARN, "LoadImage('%s') failed: %s", basename, GetError());
1181 if (bitmap->width != new_bitmap->width ||
1182 bitmap->height != new_bitmap->height)
1184 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
1186 FreeBitmap(new_bitmap);
1190 TransferBitmapPointers(new_bitmap, bitmap);
1194 static Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
1196 return SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
1199 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
1201 if (bitmaps[IMG_BITMAP_CUSTOM])
1203 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
1205 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
1208 if (gfx.game_tile_size == gfx.standard_tile_size)
1210 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1215 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
1216 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
1217 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
1219 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
1221 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
1222 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
1225 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1226 int tile_size, boolean create_small_bitmaps)
1228 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1229 Bitmap *tmp_bitmap_final = NULL;
1230 Bitmap *tmp_bitmap_0 = NULL;
1231 Bitmap *tmp_bitmap_1 = NULL;
1232 Bitmap *tmp_bitmap_2 = NULL;
1233 Bitmap *tmp_bitmap_4 = NULL;
1234 Bitmap *tmp_bitmap_8 = NULL;
1235 Bitmap *tmp_bitmap_16 = NULL;
1236 Bitmap *tmp_bitmap_32 = NULL;
1237 int width_final, height_final;
1238 int width_0, height_0;
1239 int width_1, height_1;
1240 int width_2, height_2;
1241 int width_4, height_4;
1242 int width_8, height_8;
1243 int width_16, height_16;
1244 int width_32, height_32;
1245 int old_width, old_height;
1248 print_timestamp_init("CreateScaledBitmaps");
1250 old_width = old_bitmap->width;
1251 old_height = old_bitmap->height;
1253 // calculate new image dimensions for final image size
1254 width_final = old_width * zoom_factor;
1255 height_final = old_height * zoom_factor;
1257 // get image with final size (this might require scaling up)
1258 // ("final" size may result in non-standard tile size image)
1259 if (zoom_factor != 1)
1260 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1262 tmp_bitmap_final = old_bitmap;
1264 UPDATE_BUSY_STATE();
1266 width_0 = width_1 = width_final;
1267 height_0 = height_1 = height_final;
1269 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1271 if (create_small_bitmaps)
1273 // check if we have a non-gameplay tile size image
1274 if (tile_size != gfx.game_tile_size)
1276 // get image with gameplay tile size
1277 width_0 = width_final * gfx.game_tile_size / tile_size;
1278 height_0 = height_final * gfx.game_tile_size / tile_size;
1280 if (width_0 == old_width)
1281 tmp_bitmap_0 = old_bitmap;
1282 else if (width_0 == width_final)
1283 tmp_bitmap_0 = tmp_bitmap_final;
1285 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1287 UPDATE_BUSY_STATE();
1290 // check if we have a non-standard tile size image
1291 if (tile_size != gfx.standard_tile_size)
1293 // get image with standard tile size
1294 width_1 = width_final * gfx.standard_tile_size / tile_size;
1295 height_1 = height_final * gfx.standard_tile_size / tile_size;
1297 if (width_1 == old_width)
1298 tmp_bitmap_1 = old_bitmap;
1299 else if (width_1 == width_final)
1300 tmp_bitmap_1 = tmp_bitmap_final;
1301 else if (width_1 == width_0)
1302 tmp_bitmap_1 = tmp_bitmap_0;
1304 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1306 UPDATE_BUSY_STATE();
1309 // calculate new image dimensions for small images
1310 width_2 = width_1 / 2;
1311 height_2 = height_1 / 2;
1312 width_4 = width_1 / 4;
1313 height_4 = height_1 / 4;
1314 width_8 = width_1 / 8;
1315 height_8 = height_1 / 8;
1316 width_16 = width_1 / 16;
1317 height_16 = height_1 / 16;
1318 width_32 = width_1 / 32;
1319 height_32 = height_1 / 32;
1321 // get image with 1/2 of normal size (for use in the level editor)
1322 if (width_2 == old_width)
1323 tmp_bitmap_2 = old_bitmap;
1325 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1327 UPDATE_BUSY_STATE();
1329 // get image with 1/4 of normal size (for use in the level editor)
1330 if (width_4 == old_width)
1331 tmp_bitmap_4 = old_bitmap;
1333 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1335 UPDATE_BUSY_STATE();
1337 // get image with 1/8 of normal size (for use on the preview screen)
1338 if (width_8 == old_width)
1339 tmp_bitmap_8 = old_bitmap;
1341 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1343 UPDATE_BUSY_STATE();
1345 // get image with 1/16 of normal size (for use on the preview screen)
1346 if (width_16 == old_width)
1347 tmp_bitmap_16 = old_bitmap;
1349 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1351 UPDATE_BUSY_STATE();
1353 // get image with 1/32 of normal size (for use on the preview screen)
1354 if (width_32 == old_width)
1355 tmp_bitmap_32 = old_bitmap;
1357 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1359 UPDATE_BUSY_STATE();
1361 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1362 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1363 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1364 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1365 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1366 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1368 if (width_0 != width_1)
1369 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1371 if (bitmaps[IMG_BITMAP_CUSTOM])
1372 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1374 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1376 boolean free_old_bitmap = TRUE;
1378 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1379 if (bitmaps[i] == old_bitmap)
1380 free_old_bitmap = FALSE;
1382 if (free_old_bitmap)
1384 // copy image filename from old to new standard sized bitmap
1385 bitmaps[IMG_BITMAP_STANDARD]->source_filename =
1386 getStringCopy(old_bitmap->source_filename);
1388 FreeBitmap(old_bitmap);
1393 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1396 UPDATE_BUSY_STATE();
1398 print_timestamp_done("CreateScaledBitmaps");
1401 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1404 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1407 void CreateBitmapTextures(Bitmap **bitmaps)
1409 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1412 void FreeBitmapTextures(Bitmap **bitmaps)
1414 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1417 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1419 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1423 // ----------------------------------------------------------------------------
1424 // mouse pointer functions
1425 // ----------------------------------------------------------------------------
1427 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1429 // XPM image definitions
1430 static const char *cursor_image_none[] =
1432 // width height num_colors chars_per_pixel
1462 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1463 static const char *cursor_image_dot[] =
1465 // width height num_colors chars_per_pixel
1494 static const char **cursor_image_playfield = cursor_image_dot;
1496 // some people complained about a "white dot" on the screen and thought it
1497 // was a graphical error... OK, let's just remove the whole pointer :-)
1498 static const char **cursor_image_playfield = cursor_image_none;
1501 static const int cursor_bit_order = BIT_ORDER_MSB;
1503 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1505 struct MouseCursorInfo *cursor;
1506 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1507 int header_lines = 4;
1510 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1512 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1515 for (y = 0; y < cursor->width; y++)
1517 for (x = 0; x < cursor->height; x++)
1520 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1525 cursor->data[i] = cursor->mask[i] = 0;
1528 switch (image[header_lines + y][x])
1531 cursor->data[i] |= bit_mask;
1532 cursor->mask[i] |= bit_mask;
1536 cursor->mask[i] |= bit_mask;
1545 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1550 void SetMouseCursor(int mode)
1552 static struct MouseCursorInfo *cursor_none = NULL;
1553 static struct MouseCursorInfo *cursor_playfield = NULL;
1554 struct MouseCursorInfo *cursor_new;
1556 if (cursor_none == NULL)
1557 cursor_none = get_cursor_from_image(cursor_image_none);
1559 if (cursor_playfield == NULL)
1560 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1562 cursor_new = (mode == CURSOR_DEFAULT ? NULL :
1563 mode == CURSOR_NONE ? cursor_none :
1564 mode == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1566 SDLSetMouseCursor(cursor_new);
1568 gfx.cursor_mode = mode;
1572 // ============================================================================
1574 // ============================================================================
1576 void OpenAudio(void)
1578 // always start with reliable default values
1579 audio.sound_available = FALSE;
1580 audio.music_available = FALSE;
1581 audio.loops_available = FALSE;
1583 audio.sound_enabled = FALSE;
1584 audio.sound_deactivated = FALSE;
1586 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1587 audio.mixer_pid = 0;
1588 audio.device_name = NULL;
1589 audio.device_fd = -1;
1591 audio.num_channels = 0;
1592 audio.music_channel = 0;
1593 audio.first_sound_channel = 0;
1598 void CloseAudio(void)
1602 audio.sound_enabled = FALSE;
1605 void SetAudioMode(boolean enabled)
1607 if (!audio.sound_available)
1610 audio.sound_enabled = enabled;
1614 // ============================================================================
1616 // ============================================================================
1618 boolean PendingEvent(void)
1620 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1623 void WaitEvent(Event *event)
1625 SDLWaitEvent(event);
1628 void PeekEvent(Event *event)
1630 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1633 void CheckQuitEvent(void)
1635 if (SDL_QuitRequested())
1636 program.exit_function(0);
1639 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1641 // key up/down events in SDL2 do not return text characters anymore
1642 return event->keysym.sym;
1645 KeyMod HandleKeyModState(Key key, int key_status)
1647 static KeyMod current_modifiers = KMOD_None;
1649 if (key != KSYM_UNDEFINED) // new key => check for modifier key change
1651 KeyMod new_modifier = KMOD_None;
1656 new_modifier = KMOD_Shift_L;
1659 new_modifier = KMOD_Shift_R;
1661 case KSYM_Control_L:
1662 new_modifier = KMOD_Control_L;
1664 case KSYM_Control_R:
1665 new_modifier = KMOD_Control_R;
1668 new_modifier = KMOD_Meta_L;
1671 new_modifier = KMOD_Meta_R;
1674 new_modifier = KMOD_Alt_L;
1677 new_modifier = KMOD_Alt_R;
1683 if (key_status == KEY_PRESSED)
1684 current_modifiers |= new_modifier;
1686 current_modifiers &= ~new_modifier;
1689 return current_modifiers;
1692 KeyMod GetKeyModState(void)
1694 return (KeyMod)SDL_GetModState();
1697 KeyMod GetKeyModStateFromEvents(void)
1699 /* always use key modifier state as tracked from key events (this is needed
1700 if the modifier key event was injected into the event queue, but the key
1701 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1702 query the keys as held pressed on the keyboard) -- this case is currently
1703 only used to filter out clipboard insert events from "True X-Mouse" tool */
1705 return HandleKeyModState(KSYM_UNDEFINED, 0);
1708 void StartTextInput(int x, int y, int width, int height)
1710 #if defined(HAS_SCREEN_KEYBOARD)
1711 SDL_StartTextInput();
1713 if (y + height > SCREEN_KEYBOARD_POS(video.height))
1715 video.shifted_up_pos = y + height - SCREEN_KEYBOARD_POS(video.height);
1716 video.shifted_up_delay = SDL_GetTicks();
1717 video.shifted_up = TRUE;
1722 void StopTextInput(void)
1724 #if defined(HAS_SCREEN_KEYBOARD)
1725 SDL_StopTextInput();
1727 if (video.shifted_up)
1729 video.shifted_up_pos = 0;
1730 video.shifted_up_delay = SDL_GetTicks();
1731 video.shifted_up = FALSE;
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();