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;
320 gfx.cursor_mode_override = CURSOR_UNDEFINED;
321 gfx.cursor_mode_final = gfx.cursor_mode;
327 void InitTileCursorInfo(void)
329 tile_cursor.enabled = FALSE;
330 tile_cursor.active = FALSE;
331 tile_cursor.moving = FALSE;
333 tile_cursor.xpos = 0;
334 tile_cursor.ypos = 0;
337 tile_cursor.target_x = 0;
338 tile_cursor.target_y = 0;
344 void InitOverlayInfo(void)
346 int nr = GRID_ACTIVE_NR();
349 overlay.enabled = FALSE;
350 overlay.active = FALSE;
352 overlay.show_grid = FALSE;
354 overlay.grid_xsize = setup.touch.grid_xsize[nr];
355 overlay.grid_ysize = setup.touch.grid_ysize[nr];
357 for (x = 0; x < MAX_GRID_XSIZE; x++)
358 for (y = 0; y < MAX_GRID_YSIZE; y++)
359 overlay.grid_button[x][y] = setup.touch.grid_button[nr][x][y];
361 overlay.grid_button_highlight = CHAR_GRID_BUTTON_NONE;
362 overlay.grid_button_action = JOY_NO_ACTION;
364 #if defined(USE_TOUCH_INPUT_OVERLAY)
365 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
366 overlay.enabled = TRUE;
370 void SetTileCursorEnabled(boolean enabled)
372 tile_cursor.enabled = enabled;
375 void SetTileCursorActive(boolean active)
377 tile_cursor.active = active;
380 void SetTileCursorTargetXY(int x, int y)
382 // delayed placement of tile selection cursor at target position
383 // (tile cursor will be moved to target position step by step)
385 tile_cursor.xpos = x;
386 tile_cursor.ypos = y;
387 tile_cursor.target_x = tile_cursor.sx + x * gfx.game_tile_size;
388 tile_cursor.target_y = tile_cursor.sy + y * gfx.game_tile_size;
390 tile_cursor.moving = TRUE;
393 void SetTileCursorXY(int x, int y)
395 // immediate placement of tile selection cursor at target position
397 SetTileCursorTargetXY(x, y);
399 tile_cursor.x = tile_cursor.target_x;
400 tile_cursor.y = tile_cursor.target_y;
402 tile_cursor.moving = FALSE;
405 void SetTileCursorSXSY(int sx, int sy)
411 void SetOverlayEnabled(boolean enabled)
413 overlay.enabled = enabled;
416 void SetOverlayActive(boolean active)
418 overlay.active = active;
421 void SetOverlayShowGrid(boolean show_grid)
423 overlay.show_grid = show_grid;
425 SetOverlayActive(show_grid);
428 SetOverlayEnabled(TRUE);
431 boolean GetOverlayEnabled(void)
433 return overlay.enabled;
436 boolean GetOverlayActive(void)
438 return overlay.active;
441 void SetDrawDeactivationMask(int draw_deactivation_mask)
443 gfx.draw_deactivation_mask = draw_deactivation_mask;
446 int GetDrawDeactivationMask(void)
448 return gfx.draw_deactivation_mask;
451 void SetDrawBackgroundMask(int draw_background_mask)
453 gfx.draw_background_mask = draw_background_mask;
456 static void SetBackgroundBitmap(Bitmap *background_bitmap_tile, int mask)
458 if (background_bitmap_tile != NULL)
459 gfx.background_bitmap_mask |= mask;
461 gfx.background_bitmap_mask &= ~mask;
463 if (background_bitmap_tile == NULL) // empty background requested
466 if (mask == REDRAW_ALL)
467 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
468 0, 0, video.width, video.height);
469 else if (mask == REDRAW_FIELD)
470 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
471 gfx.real_sx, gfx.real_sy, gfx.full_sxsize, gfx.full_sysize);
472 else if (mask == REDRAW_DOOR_1)
473 BlitBitmapTiled(background_bitmap_tile, gfx.background_bitmap, 0, 0, 0, 0,
474 gfx.dx, gfx.dy, gfx.dxsize, gfx.dysize);
477 void SetWindowBackgroundBitmap(Bitmap *background_bitmap_tile)
479 // remove every mask before setting mask for window
480 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
481 SetBackgroundBitmap(NULL, 0xffff); // !!! FIX THIS !!!
482 SetBackgroundBitmap(background_bitmap_tile, REDRAW_ALL);
485 void SetMainBackgroundBitmap(Bitmap *background_bitmap_tile)
487 // remove window area mask before setting mask for main area
488 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
489 SetBackgroundBitmap(NULL, REDRAW_ALL); // !!! FIX THIS !!!
490 SetBackgroundBitmap(background_bitmap_tile, REDRAW_FIELD);
493 void SetDoorBackgroundBitmap(Bitmap *background_bitmap_tile)
495 // remove window area mask before setting mask for door area
496 // (!!! TO BE FIXED: The whole REDRAW_* system really sucks! !!!)
497 SetBackgroundBitmap(NULL, REDRAW_ALL); // !!! FIX THIS !!!
498 SetBackgroundBitmap(background_bitmap_tile, REDRAW_DOOR_1);
502 // ============================================================================
504 // ============================================================================
506 static int GetRealDepth(int depth)
508 return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
511 static void sysFillRectangle(Bitmap *bitmap, int x, int y,
512 int width, int height, Pixel color)
514 SDLFillRectangle(bitmap, x, y, width, height, color);
516 if (bitmap == backbuffer)
517 SetRedrawMaskFromArea(x, y, width, height);
520 static void sysCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
521 int src_x, int src_y, int width, int height,
522 int dst_x, int dst_y, int mask_mode)
524 SDLCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
525 dst_x, dst_y, mask_mode);
527 if (dst_bitmap == backbuffer)
528 SetRedrawMaskFromArea(dst_x, dst_y, width, height);
531 void LimitScreenUpdates(boolean enable)
533 SDLLimitScreenUpdates(enable);
536 void InitVideoDefaults(void)
538 video.default_depth = 32;
541 void InitVideoDisplay(void)
543 if (program.headless)
546 SDLInitVideoDisplay();
550 void CloseVideoDisplay(void)
552 KeyboardAutoRepeatOn();
554 SDL_QuitSubSystem(SDL_INIT_VIDEO);
557 void InitVideoBuffer(int width, int height, int depth, boolean fullscreen)
560 video.height = height;
561 video.depth = GetRealDepth(depth);
563 video.screen_width = width;
564 video.screen_height = height;
565 video.screen_xoffset = 0;
566 video.screen_yoffset = 0;
568 video.fullscreen_available = FULLSCREEN_STATUS;
569 video.fullscreen_enabled = FALSE;
571 video.window_scaling_available = WINDOW_SCALING_STATUS;
573 video.frame_delay = 0;
574 video.frame_delay_value = GAME_FRAME_DELAY;
576 video.shifted_up = FALSE;
577 video.shifted_up_pos = 0;
578 video.shifted_up_pos_last = 0;
579 video.shifted_up_delay = 0;
580 video.shifted_up_delay_value = ONE_SECOND_DELAY / 4;
582 SDLInitVideoBuffer(fullscreen);
584 video.initialized = !program.headless;
589 static void FreeBitmapPointers(Bitmap *bitmap)
594 SDLFreeBitmapPointers(bitmap);
596 checked_free(bitmap->source_filename);
597 bitmap->source_filename = NULL;
600 static void TransferBitmapPointers(Bitmap *src_bitmap,
603 if (src_bitmap == NULL || dst_bitmap == NULL)
606 FreeBitmapPointers(dst_bitmap);
608 *dst_bitmap = *src_bitmap;
611 void FreeBitmap(Bitmap *bitmap)
616 FreeBitmapPointers(bitmap);
621 Bitmap *CreateBitmapStruct(void)
623 return checked_calloc(sizeof(Bitmap));
626 Bitmap *CreateBitmap(int width, int height, int depth)
628 Bitmap *new_bitmap = CreateBitmapStruct();
629 int real_width = MAX(1, width); // prevent zero bitmap width
630 int real_height = MAX(1, height); // prevent zero bitmap height
631 int real_depth = GetRealDepth(depth);
633 SDLCreateBitmapContent(new_bitmap, real_width, real_height, real_depth);
635 new_bitmap->width = real_width;
636 new_bitmap->height = real_height;
641 void ReCreateBitmap(Bitmap **bitmap, int width, int height)
645 // if new bitmap size fits into old one, no need to re-create it
646 if (width <= (*bitmap)->width &&
647 height <= (*bitmap)->height)
650 // else adjust size so that old and new bitmap size fit into it
651 width = MAX(width, (*bitmap)->width);
652 height = MAX(height, (*bitmap)->height);
655 Bitmap *new_bitmap = CreateBitmap(width, height, DEFAULT_DEPTH);
659 *bitmap = new_bitmap;
663 TransferBitmapPointers(new_bitmap, *bitmap);
669 static void CloseWindow(DrawWindow *window)
674 void SetRedrawMaskFromArea(int x, int y, int width, int height)
678 int x2 = x + width - 1;
679 int y2 = y + height - 1;
681 if (width == 0 || height == 0)
684 if (IN_GFX_FIELD_FULL(x1, y1) && IN_GFX_FIELD_FULL(x2, y2))
685 redraw_mask |= REDRAW_FIELD;
686 else if (IN_GFX_DOOR_1(x1, y1) && IN_GFX_DOOR_1(x2, y2))
687 redraw_mask |= REDRAW_DOOR_1;
688 else if (IN_GFX_DOOR_2(x1, y1) && IN_GFX_DOOR_2(x2, y2))
689 redraw_mask |= REDRAW_DOOR_2;
690 else if (IN_GFX_DOOR_3(x1, y1) && IN_GFX_DOOR_3(x2, y2))
691 redraw_mask |= REDRAW_DOOR_3;
693 redraw_mask = REDRAW_ALL;
696 static boolean CheckDrawingArea(int x, int y, int width, int height,
699 if (draw_mask == REDRAW_NONE)
702 if (draw_mask & REDRAW_ALL)
705 if ((draw_mask & REDRAW_FIELD) && IN_GFX_FIELD_FULL(x, y))
708 if ((draw_mask & REDRAW_DOOR_1) && IN_GFX_DOOR_1(x, y))
711 if ((draw_mask & REDRAW_DOOR_2) && IN_GFX_DOOR_2(x, y))
714 if ((draw_mask & REDRAW_DOOR_3) && IN_GFX_DOOR_3(x, y))
720 boolean DrawingDeactivatedField(void)
722 if (program.headless)
725 if (gfx.draw_deactivation_mask & REDRAW_FIELD)
731 boolean DrawingDeactivated(int x, int y, int width, int height)
733 return CheckDrawingArea(x, y, width, height, gfx.draw_deactivation_mask);
736 boolean DrawingOnBackground(int x, int y)
738 return (CheckDrawingArea(x, y, 1, 1, gfx.background_bitmap_mask) &&
739 CheckDrawingArea(x, y, 1, 1, gfx.draw_background_mask));
742 static boolean InClippedRectangle(Bitmap *bitmap, int *x, int *y,
743 int *width, int *height, boolean is_dest)
745 int clip_x, clip_y, clip_width, clip_height;
747 if (gfx.clipping_enabled && is_dest) // only clip destination bitmap
749 clip_x = MIN(MAX(0, gfx.clip_x), bitmap->width);
750 clip_y = MIN(MAX(0, gfx.clip_y), bitmap->height);
751 clip_width = MIN(MAX(0, gfx.clip_width), bitmap->width - clip_x);
752 clip_height = MIN(MAX(0, gfx.clip_height), bitmap->height - clip_y);
758 clip_width = bitmap->width;
759 clip_height = bitmap->height;
762 // skip if rectangle completely outside bitmap
764 if (*x + *width <= clip_x ||
765 *y + *height <= clip_y ||
766 *x >= clip_x + clip_width ||
767 *y >= clip_y + clip_height)
770 // clip if rectangle overlaps bitmap
774 *width -= clip_x - *x;
777 else if (*x + *width > clip_x + clip_width)
779 *width = clip_x + clip_width - *x;
784 *height -= clip_y - *y;
787 else if (*y + *height > clip_y + clip_height)
789 *height = clip_y + clip_height - *y;
795 void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
796 int src_x, int src_y, int width, int height,
797 int dst_x, int dst_y)
799 int dst_x_unclipped = dst_x;
800 int dst_y_unclipped = dst_y;
802 if (program.headless)
805 if (src_bitmap == NULL || dst_bitmap == NULL)
808 if (DrawingDeactivated(dst_x, dst_y, width, height))
811 if (!InClippedRectangle(src_bitmap, &src_x, &src_y, &width, &height, FALSE) ||
812 !InClippedRectangle(dst_bitmap, &dst_x, &dst_y, &width, &height, TRUE))
815 // source x/y might need adjustment if destination x/y was clipped top/left
816 src_x += dst_x - dst_x_unclipped;
817 src_y += dst_y - dst_y_unclipped;
819 // !!! 2013-12-11: An "old friend" is back. Same bug in SDL2 2.0.1 !!!
820 // !!! 2009-03-30: Fixed by using self-compiled, patched SDL.dll !!!
821 /* (This bug still exists in the actual (as of 2009-06-15) version 1.2.13,
822 but is already fixed in SVN and should therefore finally be fixed with
823 the next official SDL release, which is probably version 1.2.14.) */
824 // !!! 2009-03-24: It seems that this problem still exists in 1.2.12 !!!
826 if (src_bitmap == dst_bitmap)
828 // needed when blitting directly to same bitmap -- should not be needed with
829 // recent SDL libraries, but apparently does not work in 1.2.11 directly
831 static Bitmap *tmp_bitmap = NULL;
832 static int tmp_bitmap_xsize = 0;
833 static int tmp_bitmap_ysize = 0;
835 // start with largest static bitmaps for initial bitmap size ...
836 if (tmp_bitmap_xsize == 0 && tmp_bitmap_ysize == 0)
838 tmp_bitmap_xsize = MAX(gfx.win_xsize, gfx.scrollbuffer_width);
839 tmp_bitmap_ysize = MAX(gfx.win_ysize, gfx.scrollbuffer_height);
842 // ... and allow for later re-adjustments due to custom artwork bitmaps
843 if (src_bitmap->width > tmp_bitmap_xsize ||
844 src_bitmap->height > tmp_bitmap_ysize)
846 tmp_bitmap_xsize = MAX(tmp_bitmap_xsize, src_bitmap->width);
847 tmp_bitmap_ysize = MAX(tmp_bitmap_ysize, src_bitmap->height);
849 FreeBitmap(tmp_bitmap);
854 if (tmp_bitmap == NULL)
855 tmp_bitmap = CreateBitmap(tmp_bitmap_xsize, tmp_bitmap_ysize,
858 sysCopyArea(src_bitmap, tmp_bitmap,
859 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
860 sysCopyArea(tmp_bitmap, dst_bitmap,
861 dst_x, dst_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
866 sysCopyArea(src_bitmap, dst_bitmap,
867 src_x, src_y, width, height, dst_x, dst_y, BLIT_OPAQUE);
870 void BlitBitmapTiled(Bitmap *src_bitmap, Bitmap *dst_bitmap,
871 int src_x, int src_y, int src_width, int src_height,
872 int dst_x, int dst_y, int dst_width, int dst_height)
874 int src_xsize = (src_width == 0 ? src_bitmap->width : src_width);
875 int src_ysize = (src_height == 0 ? src_bitmap->height : src_height);
876 int dst_xsize = dst_width;
877 int dst_ysize = dst_height;
878 int src_xsteps = (dst_xsize + src_xsize - 1) / src_xsize;
879 int src_ysteps = (dst_ysize + src_ysize - 1) / src_ysize;
882 for (y = 0; y < src_ysteps; y++)
884 for (x = 0; x < src_xsteps; x++)
886 int draw_x = dst_x + x * src_xsize;
887 int draw_y = dst_y + y * src_ysize;
888 int draw_xsize = MIN(src_xsize, dst_xsize - x * src_xsize);
889 int draw_ysize = MIN(src_ysize, dst_ysize - y * src_ysize);
891 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, draw_xsize, draw_ysize,
897 void FadeRectangle(int x, int y, int width, int height,
898 int fade_mode, int fade_delay, int post_delay,
899 void (*draw_border_function)(void))
901 // (use destination bitmap "backbuffer" -- "bitmap_cross" may be undefined)
902 if (!InClippedRectangle(backbuffer, &x, &y, &width, &height, TRUE))
905 SDLFadeRectangle(x, y, width, height,
906 fade_mode, fade_delay, post_delay, draw_border_function);
909 void FillRectangle(Bitmap *bitmap, int x, int y, int width, int height,
912 if (DrawingDeactivated(x, y, width, height))
915 if (!InClippedRectangle(bitmap, &x, &y, &width, &height, TRUE))
918 sysFillRectangle(bitmap, x, y, width, height, color);
921 void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
923 FillRectangle(bitmap, x, y, width, height, BLACK_PIXEL);
926 void ClearRectangleOnBackground(Bitmap *bitmap, int x, int y,
927 int width, int height)
929 if (DrawingOnBackground(x, y))
930 BlitBitmap(gfx.background_bitmap, bitmap, x, y, width, height, x, y);
932 ClearRectangle(bitmap, x, y, width, height);
935 void BlitBitmapMasked(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 (DrawingDeactivated(dst_x, dst_y, width, height))
942 sysCopyArea(src_bitmap, dst_bitmap, src_x, src_y, width, height,
943 dst_x, dst_y, BLIT_MASKED);
946 void BlitBitmapOnBackground(Bitmap *src_bitmap, Bitmap *dst_bitmap,
947 int src_x, int src_y, int width, int height,
948 int dst_x, int dst_y)
950 if (DrawingOnBackground(dst_x, dst_y))
953 BlitBitmap(gfx.background_bitmap, dst_bitmap, dst_x, dst_y, width, height,
957 BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y, width, height,
961 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, width, height,
965 void BlitTexture(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 BlitTextureMasked(Bitmap *bitmap,
977 int src_x, int src_y, int width, int height,
978 int dst_x, int dst_y)
983 SDLBlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y,
987 void BlitToScreen(Bitmap *bitmap,
988 int src_x, int src_y, int width, int height,
989 int dst_x, int dst_y)
994 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
995 BlitBitmap(bitmap, gfx.final_screen_bitmap, src_x, src_y,
996 width, height, dst_x, dst_y);
998 BlitTexture(bitmap, src_x, src_y, width, height, dst_x, dst_y);
1001 void BlitToScreenMasked(Bitmap *bitmap,
1002 int src_x, int src_y, int width, int height,
1003 int dst_x, int dst_y)
1008 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP)
1009 BlitBitmapMasked(bitmap, gfx.final_screen_bitmap, src_x, src_y,
1010 width, height, dst_x, dst_y);
1012 BlitTextureMasked(bitmap, src_x, src_y, width, height, dst_x, dst_y);
1015 void DrawSimpleBlackLine(Bitmap *bitmap, int from_x, int from_y,
1018 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, BLACK_PIXEL);
1021 void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
1024 SDLDrawSimpleLine(bitmap, from_x, from_y, to_x, to_y, WHITE_PIXEL);
1027 static void DrawLine(Bitmap *bitmap, int from_x, int from_y,
1028 int to_x, int to_y, Pixel pixel, int line_width)
1032 if (program.headless)
1035 for (x = 0; x < line_width; x++)
1037 for (y = 0; y < line_width; y++)
1039 int dx = x - line_width / 2;
1040 int dy = y - line_width / 2;
1042 if ((x == 0 && y == 0) ||
1043 (x == 0 && y == line_width - 1) ||
1044 (x == line_width - 1 && y == 0) ||
1045 (x == line_width - 1 && y == line_width - 1))
1049 from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
1054 void DrawLines(Bitmap *bitmap, struct XY *points, int num_points, Pixel pixel)
1059 for (i = 0; i < num_points - 1; i++)
1060 DrawLine(bitmap, points[i].x, points[i].y,
1061 points[i + 1].x, points[i + 1].y, pixel, line_width);
1064 SDLDrawLines(bitmap->surface, points, num_points, pixel);
1068 Pixel GetPixel(Bitmap *bitmap, int x, int y)
1070 if (program.headless)
1073 if (x < 0 || x >= bitmap->width ||
1074 y < 0 || y >= bitmap->height)
1077 return SDLGetPixel(bitmap, x, y);
1080 Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
1081 unsigned int color_g, unsigned int color_b)
1083 if (program.headless)
1086 return SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
1089 Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
1091 unsigned int color_r = (color >> 16) & 0xff;
1092 unsigned int color_g = (color >> 8) & 0xff;
1093 unsigned int color_b = (color >> 0) & 0xff;
1095 return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
1098 void KeyboardAutoRepeatOn(void)
1100 keyrepeat_status = TRUE;
1103 void KeyboardAutoRepeatOff(void)
1105 keyrepeat_status = FALSE;
1108 boolean SetVideoMode(boolean fullscreen)
1110 return SDLSetVideoMode(fullscreen);
1113 void SetVideoFrameDelay(unsigned int frame_delay_value)
1115 video.frame_delay_value = frame_delay_value;
1118 unsigned int GetVideoFrameDelay(void)
1120 return video.frame_delay_value;
1123 boolean ChangeVideoModeIfNeeded(boolean fullscreen)
1125 if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
1126 (!fullscreen && video.fullscreen_enabled))
1127 fullscreen = SetVideoMode(fullscreen);
1132 Bitmap *LoadImage(char *filename)
1136 new_bitmap = SDLLoadImage(filename);
1139 new_bitmap->source_filename = getStringCopy(filename);
1144 Bitmap *LoadCustomImage(char *basename)
1146 char *filename = getCustomImageFilename(basename);
1149 if (filename == NULL)
1150 Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
1152 if ((new_bitmap = LoadImage(filename)) == NULL)
1153 Error(ERR_EXIT, "LoadImage('%s') failed: %s", basename, GetError());
1158 void ReloadCustomImage(Bitmap *bitmap, char *basename)
1160 char *filename = getCustomImageFilename(basename);
1163 if (filename == NULL) // (should never happen)
1165 Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
1169 if (strEqual(filename, bitmap->source_filename))
1171 // The old and new image are the same (have the same filename and path).
1172 // This usually means that this image does not exist in this graphic set
1173 // and a fallback to the existing image is done.
1178 if ((new_bitmap = LoadImage(filename)) == NULL)
1180 Error(ERR_WARN, "LoadImage('%s') failed: %s", basename, GetError());
1184 if (bitmap->width != new_bitmap->width ||
1185 bitmap->height != new_bitmap->height)
1187 Error(ERR_WARN, "ReloadCustomImage: new image '%s' has wrong dimensions",
1189 FreeBitmap(new_bitmap);
1193 TransferBitmapPointers(new_bitmap, bitmap);
1197 static Bitmap *ZoomBitmap(Bitmap *src_bitmap, int zoom_width, int zoom_height)
1199 return SDLZoomBitmap(src_bitmap, zoom_width, zoom_height);
1202 void ReCreateGameTileSizeBitmap(Bitmap **bitmaps)
1204 if (bitmaps[IMG_BITMAP_CUSTOM])
1206 FreeBitmap(bitmaps[IMG_BITMAP_CUSTOM]);
1208 bitmaps[IMG_BITMAP_CUSTOM] = NULL;
1211 if (gfx.game_tile_size == gfx.standard_tile_size)
1213 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1218 Bitmap *bitmap = bitmaps[IMG_BITMAP_STANDARD];
1219 int width = bitmap->width * gfx.game_tile_size / gfx.standard_tile_size;;
1220 int height = bitmap->height * gfx.game_tile_size / gfx.standard_tile_size;;
1222 Bitmap *bitmap_new = ZoomBitmap(bitmap, width, height);
1224 bitmaps[IMG_BITMAP_CUSTOM] = bitmap_new;
1225 bitmaps[IMG_BITMAP_GAME] = bitmap_new;
1228 static void CreateScaledBitmaps(Bitmap **bitmaps, int zoom_factor,
1229 int tile_size, boolean create_small_bitmaps)
1231 Bitmap *old_bitmap = bitmaps[IMG_BITMAP_STANDARD];
1232 Bitmap *tmp_bitmap_final = NULL;
1233 Bitmap *tmp_bitmap_0 = NULL;
1234 Bitmap *tmp_bitmap_1 = NULL;
1235 Bitmap *tmp_bitmap_2 = NULL;
1236 Bitmap *tmp_bitmap_4 = NULL;
1237 Bitmap *tmp_bitmap_8 = NULL;
1238 Bitmap *tmp_bitmap_16 = NULL;
1239 Bitmap *tmp_bitmap_32 = NULL;
1240 int width_final, height_final;
1241 int width_0, height_0;
1242 int width_1, height_1;
1243 int width_2, height_2;
1244 int width_4, height_4;
1245 int width_8, height_8;
1246 int width_16, height_16;
1247 int width_32, height_32;
1248 int old_width, old_height;
1251 print_timestamp_init("CreateScaledBitmaps");
1253 old_width = old_bitmap->width;
1254 old_height = old_bitmap->height;
1256 // calculate new image dimensions for final image size
1257 width_final = old_width * zoom_factor;
1258 height_final = old_height * zoom_factor;
1260 // get image with final size (this might require scaling up)
1261 // ("final" size may result in non-standard tile size image)
1262 if (zoom_factor != 1)
1263 tmp_bitmap_final = ZoomBitmap(old_bitmap, width_final, height_final);
1265 tmp_bitmap_final = old_bitmap;
1267 UPDATE_BUSY_STATE();
1269 width_0 = width_1 = width_final;
1270 height_0 = height_1 = height_final;
1272 tmp_bitmap_0 = tmp_bitmap_1 = tmp_bitmap_final;
1274 if (create_small_bitmaps)
1276 // check if we have a non-gameplay tile size image
1277 if (tile_size != gfx.game_tile_size)
1279 // get image with gameplay tile size
1280 width_0 = width_final * gfx.game_tile_size / tile_size;
1281 height_0 = height_final * gfx.game_tile_size / tile_size;
1283 if (width_0 == old_width)
1284 tmp_bitmap_0 = old_bitmap;
1285 else if (width_0 == width_final)
1286 tmp_bitmap_0 = tmp_bitmap_final;
1288 tmp_bitmap_0 = ZoomBitmap(old_bitmap, width_0, height_0);
1290 UPDATE_BUSY_STATE();
1293 // check if we have a non-standard tile size image
1294 if (tile_size != gfx.standard_tile_size)
1296 // get image with standard tile size
1297 width_1 = width_final * gfx.standard_tile_size / tile_size;
1298 height_1 = height_final * gfx.standard_tile_size / tile_size;
1300 if (width_1 == old_width)
1301 tmp_bitmap_1 = old_bitmap;
1302 else if (width_1 == width_final)
1303 tmp_bitmap_1 = tmp_bitmap_final;
1304 else if (width_1 == width_0)
1305 tmp_bitmap_1 = tmp_bitmap_0;
1307 tmp_bitmap_1 = ZoomBitmap(old_bitmap, width_1, height_1);
1309 UPDATE_BUSY_STATE();
1312 // calculate new image dimensions for small images
1313 width_2 = width_1 / 2;
1314 height_2 = height_1 / 2;
1315 width_4 = width_1 / 4;
1316 height_4 = height_1 / 4;
1317 width_8 = width_1 / 8;
1318 height_8 = height_1 / 8;
1319 width_16 = width_1 / 16;
1320 height_16 = height_1 / 16;
1321 width_32 = width_1 / 32;
1322 height_32 = height_1 / 32;
1324 // get image with 1/2 of normal size (for use in the level editor)
1325 if (width_2 == old_width)
1326 tmp_bitmap_2 = old_bitmap;
1328 tmp_bitmap_2 = ZoomBitmap(tmp_bitmap_1, width_2, height_2);
1330 UPDATE_BUSY_STATE();
1332 // get image with 1/4 of normal size (for use in the level editor)
1333 if (width_4 == old_width)
1334 tmp_bitmap_4 = old_bitmap;
1336 tmp_bitmap_4 = ZoomBitmap(tmp_bitmap_2, width_4, height_4);
1338 UPDATE_BUSY_STATE();
1340 // get image with 1/8 of normal size (for use on the preview screen)
1341 if (width_8 == old_width)
1342 tmp_bitmap_8 = old_bitmap;
1344 tmp_bitmap_8 = ZoomBitmap(tmp_bitmap_4, width_8, height_8);
1346 UPDATE_BUSY_STATE();
1348 // get image with 1/16 of normal size (for use on the preview screen)
1349 if (width_16 == old_width)
1350 tmp_bitmap_16 = old_bitmap;
1352 tmp_bitmap_16 = ZoomBitmap(tmp_bitmap_8, width_16, height_16);
1354 UPDATE_BUSY_STATE();
1356 // get image with 1/32 of normal size (for use on the preview screen)
1357 if (width_32 == old_width)
1358 tmp_bitmap_32 = old_bitmap;
1360 tmp_bitmap_32 = ZoomBitmap(tmp_bitmap_16, width_32, height_32);
1362 UPDATE_BUSY_STATE();
1364 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1365 bitmaps[IMG_BITMAP_16x16] = tmp_bitmap_2;
1366 bitmaps[IMG_BITMAP_8x8] = tmp_bitmap_4;
1367 bitmaps[IMG_BITMAP_4x4] = tmp_bitmap_8;
1368 bitmaps[IMG_BITMAP_2x2] = tmp_bitmap_16;
1369 bitmaps[IMG_BITMAP_1x1] = tmp_bitmap_32;
1371 if (width_0 != width_1)
1372 bitmaps[IMG_BITMAP_CUSTOM] = tmp_bitmap_0;
1374 if (bitmaps[IMG_BITMAP_CUSTOM])
1375 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_CUSTOM];
1377 bitmaps[IMG_BITMAP_GAME] = bitmaps[IMG_BITMAP_STANDARD];
1379 boolean free_old_bitmap = TRUE;
1381 for (i = 0; i < NUM_IMG_BITMAPS; i++)
1382 if (bitmaps[i] == old_bitmap)
1383 free_old_bitmap = FALSE;
1385 if (free_old_bitmap)
1387 // copy image filename from old to new standard sized bitmap
1388 bitmaps[IMG_BITMAP_STANDARD]->source_filename =
1389 getStringCopy(old_bitmap->source_filename);
1391 FreeBitmap(old_bitmap);
1396 bitmaps[IMG_BITMAP_32x32] = tmp_bitmap_1;
1399 UPDATE_BUSY_STATE();
1401 print_timestamp_done("CreateScaledBitmaps");
1404 void CreateBitmapWithSmallBitmaps(Bitmap **bitmaps, int zoom_factor,
1407 CreateScaledBitmaps(bitmaps, zoom_factor, tile_size, TRUE);
1410 void CreateBitmapTextures(Bitmap **bitmaps)
1412 SDLCreateBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1415 void FreeBitmapTextures(Bitmap **bitmaps)
1417 SDLFreeBitmapTextures(bitmaps[IMG_BITMAP_STANDARD]);
1420 void ScaleBitmap(Bitmap **bitmaps, int zoom_factor)
1422 CreateScaledBitmaps(bitmaps, zoom_factor, 0, FALSE);
1426 // ----------------------------------------------------------------------------
1427 // mouse pointer functions
1428 // ----------------------------------------------------------------------------
1430 #define USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER 0
1432 // XPM image definitions
1433 static const char *cursor_image_none[] =
1435 // width height num_colors chars_per_pixel
1465 #if USE_ONE_PIXEL_PLAYFIELD_MOUSEPOINTER
1466 static const char *cursor_image_dot[] =
1468 // width height num_colors chars_per_pixel
1497 static const char **cursor_image_playfield = cursor_image_dot;
1499 // some people complained about a "white dot" on the screen and thought it
1500 // was a graphical error... OK, let's just remove the whole pointer :-)
1501 static const char **cursor_image_playfield = cursor_image_none;
1504 static const int cursor_bit_order = BIT_ORDER_MSB;
1506 static struct MouseCursorInfo *get_cursor_from_image(const char **image)
1508 struct MouseCursorInfo *cursor;
1509 boolean bit_order_msb = (cursor_bit_order == BIT_ORDER_MSB);
1510 int header_lines = 4;
1513 cursor = checked_calloc(sizeof(struct MouseCursorInfo));
1515 sscanf(image[0], " %d %d ", &cursor->width, &cursor->height);
1518 for (y = 0; y < cursor->width; y++)
1520 for (x = 0; x < cursor->height; x++)
1523 int bit_mask = 0x01 << (bit_order_msb ? 7 - bit_nr : bit_nr );
1528 cursor->data[i] = cursor->mask[i] = 0;
1531 switch (image[header_lines + y][x])
1534 cursor->data[i] |= bit_mask;
1535 cursor->mask[i] |= bit_mask;
1539 cursor->mask[i] |= bit_mask;
1548 sscanf(image[header_lines + y], "%d,%d", &cursor->hot_x, &cursor->hot_y);
1553 void SetMouseCursor(int mode)
1555 static struct MouseCursorInfo *cursor_none = NULL;
1556 static struct MouseCursorInfo *cursor_playfield = NULL;
1557 struct MouseCursorInfo *cursor_new;
1558 int mode_final = mode;
1560 if (cursor_none == NULL)
1561 cursor_none = get_cursor_from_image(cursor_image_none);
1563 if (cursor_playfield == NULL)
1564 cursor_playfield = get_cursor_from_image(cursor_image_playfield);
1566 if (gfx.cursor_mode_override != CURSOR_UNDEFINED)
1567 mode_final = gfx.cursor_mode_override;
1569 cursor_new = (mode_final == CURSOR_DEFAULT ? NULL :
1570 mode_final == CURSOR_NONE ? cursor_none :
1571 mode_final == CURSOR_PLAYFIELD ? cursor_playfield : NULL);
1573 SDLSetMouseCursor(cursor_new);
1575 gfx.cursor_mode = mode;
1576 gfx.cursor_mode_final = mode_final;
1580 // ============================================================================
1582 // ============================================================================
1584 void OpenAudio(void)
1586 // always start with reliable default values
1587 audio.sound_available = FALSE;
1588 audio.music_available = FALSE;
1589 audio.loops_available = FALSE;
1591 audio.sound_enabled = FALSE;
1592 audio.sound_deactivated = FALSE;
1594 audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
1595 audio.mixer_pid = 0;
1596 audio.device_name = NULL;
1597 audio.device_fd = -1;
1599 audio.num_channels = 0;
1600 audio.music_channel = 0;
1601 audio.first_sound_channel = 0;
1606 void CloseAudio(void)
1610 audio.sound_enabled = FALSE;
1613 void SetAudioMode(boolean enabled)
1615 if (!audio.sound_available)
1618 audio.sound_enabled = enabled;
1622 // ============================================================================
1624 // ============================================================================
1626 boolean PendingEvent(void)
1628 return (SDL_PollEvent(NULL) ? TRUE : FALSE);
1631 void WaitEvent(Event *event)
1633 SDLWaitEvent(event);
1636 void PeekEvent(Event *event)
1638 SDL_PeepEvents(event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
1641 void CheckQuitEvent(void)
1643 if (SDL_QuitRequested())
1644 program.exit_function(0);
1647 Key GetEventKey(KeyEvent *event, boolean with_modifiers)
1649 // key up/down events in SDL2 do not return text characters anymore
1650 return event->keysym.sym;
1653 KeyMod HandleKeyModState(Key key, int key_status)
1655 static KeyMod current_modifiers = KMOD_None;
1657 if (key != KSYM_UNDEFINED) // new key => check for modifier key change
1659 KeyMod new_modifier = KMOD_None;
1664 new_modifier = KMOD_Shift_L;
1667 new_modifier = KMOD_Shift_R;
1669 case KSYM_Control_L:
1670 new_modifier = KMOD_Control_L;
1672 case KSYM_Control_R:
1673 new_modifier = KMOD_Control_R;
1676 new_modifier = KMOD_Meta_L;
1679 new_modifier = KMOD_Meta_R;
1682 new_modifier = KMOD_Alt_L;
1685 new_modifier = KMOD_Alt_R;
1691 if (key_status == KEY_PRESSED)
1692 current_modifiers |= new_modifier;
1694 current_modifiers &= ~new_modifier;
1697 return current_modifiers;
1700 KeyMod GetKeyModState(void)
1702 return (KeyMod)SDL_GetModState();
1705 KeyMod GetKeyModStateFromEvents(void)
1707 /* always use key modifier state as tracked from key events (this is needed
1708 if the modifier key event was injected into the event queue, but the key
1709 was not really pressed on keyboard -- SDL_GetModState() seems to directly
1710 query the keys as held pressed on the keyboard) -- this case is currently
1711 only used to filter out clipboard insert events from "True X-Mouse" tool */
1713 return HandleKeyModState(KSYM_UNDEFINED, 0);
1716 void StartTextInput(int x, int y, int width, int height)
1718 #if defined(HAS_SCREEN_KEYBOARD)
1719 SDL_StartTextInput();
1721 if (y + height > SCREEN_KEYBOARD_POS(video.height))
1723 video.shifted_up_pos = y + height - SCREEN_KEYBOARD_POS(video.height);
1724 video.shifted_up_delay = SDL_GetTicks();
1725 video.shifted_up = TRUE;
1730 void StopTextInput(void)
1732 #if defined(HAS_SCREEN_KEYBOARD)
1733 SDL_StopTextInput();
1735 if (video.shifted_up)
1737 video.shifted_up_pos = 0;
1738 video.shifted_up_delay = SDL_GetTicks();
1739 video.shifted_up = FALSE;
1744 void PushUserEvent(int code, int value1, int value2)
1748 SDL_memset(&event, 0, sizeof(event));
1750 event.type = EVENT_USER;
1752 event.value1 = value1;
1753 event.value2 = value2;
1755 SDL_PushEvent((SDL_Event *)&event);
1759 // ============================================================================
1760 // joystick functions
1761 // ============================================================================
1763 void InitJoysticks(void)
1767 #if defined(NO_JOYSTICK)
1768 return; // joysticks generally deactivated by compile-time directive
1771 // always start with reliable default values
1772 joystick.status = JOYSTICK_NOT_AVAILABLE;
1773 for (i = 0; i < MAX_PLAYERS; i++)
1774 joystick.nr[i] = -1; // no joystick configured
1779 boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1781 return SDLReadJoystick(nr, x, y, b1, b2);
1784 boolean CheckJoystickOpened(int nr)
1786 return SDLCheckJoystickOpened(nr);
1789 void ClearJoystickState(void)
1791 SDLClearJoystickState();