X-Git-Url: https://git.artsoft.org/?p=rocksndiamonds.git;a=blobdiff_plain;f=src%2Flibgame%2Fsdl.c;h=3afecaf918c2e7bd991e3ec36e335cad334a65b3;hp=9ed0e49da6bc6367e88ae84be0c781780abd2fe1;hb=25c22434b11938e230719d6f73df4ed7813570be;hpb=67b32ebb347487d2d3a9d926ad8d1c4ed3d9aec6 diff --git a/src/libgame/sdl.c b/src/libgame/sdl.c index 9ed0e49d..3afecaf9 100644 --- a/src/libgame/sdl.c +++ b/src/libgame/sdl.c @@ -37,8 +37,10 @@ static boolean limit_screen_updates = FALSE; /* functions from SGE library */ void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32); +#if defined(USE_TOUCH_INPUT_OVERLAY) /* functions to draw overlay graphics for touch device input */ static void DrawTouchInputOverlay(); +#endif void SDLLimitScreenUpdates(boolean enable) { @@ -205,8 +207,11 @@ static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay) SDL_RenderCopy(sdl_renderer, sdl_texture_target, src_rect2, dst_rect2); } +#if defined(USE_TOUCH_INPUT_OVERLAY) // draw overlay graphics for touch device input, if needed DrawTouchInputOverlay(); +#endif + #endif // global synchronization point of the game to align video frame delay @@ -434,6 +439,9 @@ boolean SDLSetNativeSurface(SDL_Surface **surface) #if defined(TARGET_SDL2) static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface) { + if (program.headless) + return NULL; + SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface); if (texture == NULL) @@ -497,8 +505,11 @@ void SDLInitVideoDisplay(void) #endif } -void SDLInitVideoBuffer(boolean fullscreen) +inline static void SDLInitVideoBuffer_VideoBuffer(boolean fullscreen) { + if (program.headless) + return; + video.window_scaling_percent = setup.window_scaling_percent; video.window_scaling_quality = setup.window_scaling_quality; @@ -526,7 +537,10 @@ void SDLInitVideoBuffer(boolean fullscreen) #else SDL_WM_SetCaption(program.window_title, program.window_title); #endif +} +inline static void SDLInitVideoBuffer_DrawBuffer() +{ /* SDL cannot directly draw to the visible video framebuffer like X11, but always uses a backbuffer, which is then blitted to the visible video framebuffer with 'SDL_UpdateRect' (or replaced with the current @@ -542,6 +556,16 @@ void SDLInitVideoBuffer(boolean fullscreen) /* create additional (symbolic) buffer for double-buffering */ ReCreateBitmap(&window, video.width, video.height); + + /* create dummy drawing buffer for headless mode, if needed */ + if (program.headless) + ReCreateBitmap(&backbuffer, video.width, video.height); +} + +void SDLInitVideoBuffer(boolean fullscreen) +{ + SDLInitVideoBuffer_VideoBuffer(fullscreen); + SDLInitVideoBuffer_DrawBuffer(); } static boolean SDLCreateScreen(boolean fullscreen) @@ -565,10 +589,10 @@ static boolean SDLCreateScreen(boolean fullscreen) it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because it will try to use accelerated graphics and apparently fails miserably) */ int renderer_flags = SDL_RENDERER_SOFTWARE; -#endif #endif SDLSetScreenSizeAndOffsets(video.width, video.height); +#endif int width = video.width; int height = video.height; @@ -978,6 +1002,9 @@ void SDLRedrawWindow() void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height, int depth) { + if (program.headless) + return; + SDL_Surface *surface = SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0); @@ -2362,6 +2389,14 @@ Bitmap *SDLLoadImage(char *filename) Bitmap *new_bitmap = CreateBitmapStruct(); SDL_Surface *sdl_image_tmp; + if (program.headless) + { + /* prevent sanity check warnings at later stage */ + new_bitmap->width = new_bitmap->height = 1; + + return new_bitmap; + } + print_timestamp_init("SDLLoadImage"); print_timestamp_time(getBaseNamePtr(filename)); @@ -2451,6 +2486,9 @@ void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info) void SDLOpenAudio(void) { + if (program.headless) + return; + #if !defined(TARGET_SDL2) if (!strEqual(setup.system.sdl_audiodriver, ARG_DEFAULT)) SDL_putenv(getStringCat2("SDL_AUDIODRIVER=", setup.system.sdl_audiodriver)); @@ -2553,31 +2591,83 @@ void SDLHandleWindowManagerEvent(Event *event) /* joystick functions */ /* ========================================================================= */ -static SDL_Joystick *sdl_joystick[MAX_PLAYERS] = { NULL, NULL, NULL, NULL }; -static int sdl_js_axis[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} }; -static int sdl_js_button[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} }; +#if defined(TARGET_SDL2) +static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick +#else +static SDL_Joystick *sdl_joystick[MAX_PLAYERS]; // only joysticks supported +#endif +static int sdl_js_axis_raw[MAX_PLAYERS][2]; +static int sdl_js_axis[MAX_PLAYERS][2]; +static int sdl_js_button[MAX_PLAYERS][2]; +static boolean sdl_is_controller[MAX_PLAYERS]; -static boolean SDLOpenJoystick(int nr) +void SDLClearJoystickState() { - if (nr < 0 || nr > MAX_PLAYERS) + int i, j; + + for (i = 0; i < MAX_PLAYERS; i++) + { + for (j = 0; j < 2; j++) + { + sdl_js_axis_raw[i][j] = -1; + sdl_js_axis[i][j] = 0; + sdl_js_button[i][j] = 0; + } + } +} + +boolean SDLOpenJoystick(int nr) +{ + if (nr < 0 || nr >= MAX_PLAYERS) return FALSE; - return ((sdl_joystick[nr] = SDL_JoystickOpen(nr)) == NULL ? FALSE : TRUE); +#if defined(TARGET_SDL2) + sdl_is_controller[nr] = SDL_IsGameController(nr); +#else + sdl_is_controller[nr] = FALSE; +#endif + +#if 1 + Error(ERR_DEBUG, "opening joystick %d (%s)", + nr, (sdl_is_controller[nr] ? "game controller" : "joystick")); +#endif + +#if defined(TARGET_SDL2) + if (sdl_is_controller[nr]) + sdl_joystick[nr] = SDL_GameControllerOpen(nr); + else + sdl_joystick[nr] = SDL_JoystickOpen(nr); +#else + sdl_joystick[nr] = SDL_JoystickOpen(nr); +#endif + + return (sdl_joystick[nr] != NULL); } -static void SDLCloseJoystick(int nr) +void SDLCloseJoystick(int nr) { - if (nr < 0 || nr > MAX_PLAYERS) + if (nr < 0 || nr >= MAX_PLAYERS) return; +#if 1 + Error(ERR_DEBUG, "closing joystick %d", nr); +#endif + +#if defined(TARGET_SDL2) + if (sdl_is_controller[nr]) + SDL_GameControllerClose(sdl_joystick[nr]); + else + SDL_JoystickClose(sdl_joystick[nr]); +#else SDL_JoystickClose(sdl_joystick[nr]); +#endif sdl_joystick[nr] = NULL; } -static boolean SDLCheckJoystickOpened(int nr) +boolean SDLCheckJoystickOpened(int nr) { - if (nr < 0 || nr > MAX_PLAYERS) + if (nr < 0 || nr >= MAX_PLAYERS) return FALSE; #if defined(TARGET_SDL2) @@ -2587,23 +2677,164 @@ static boolean SDLCheckJoystickOpened(int nr) #endif } +static void setJoystickAxis(int nr, int axis_id_raw, int axis_value) +{ +#if defined(TARGET_SDL2) + int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX || + axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 : + axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY || + axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1); +#else + int axis_id = axis_id_raw % 2; +#endif + + if (nr < 0 || nr >= MAX_PLAYERS) + return; + + if (axis_id == -1) + return; + + // prevent (slightly jittering, but centered) axis A from resetting axis B + if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 && + axis_id_raw != sdl_js_axis_raw[nr][axis_id]) + return; + + sdl_js_axis[nr][axis_id] = axis_value; + sdl_js_axis_raw[nr][axis_id] = axis_id_raw; +} + +static void setJoystickButton(int nr, int button_id_raw, int button_state) +{ +#if defined(TARGET_SDL2) + int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A || + button_id_raw == SDL_CONTROLLER_BUTTON_X || + button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER || + button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK || + button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 : + button_id_raw == SDL_CONTROLLER_BUTTON_B || + button_id_raw == SDL_CONTROLLER_BUTTON_Y || + button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 : + -1); + + if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT) + sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT; + else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) + sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT; + else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP) + sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER; + else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN) + sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER; + + if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT || + button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT || + button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP || + button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN) + sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1; +#else + int button_id = button_id_raw % 2; +#endif + + if (nr < 0 || nr >= MAX_PLAYERS) + return; + + if (button_id == -1) + return; + + sdl_js_button[nr][button_id] = button_state; +} + void HandleJoystickEvent(Event *event) { switch(event->type) { +#if defined(TARGET_SDL2) + case SDL_CONTROLLERDEVICEADDED: +#if 1 + Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEADDED: device %d added", + event->cdevice.which); +#endif + InitJoysticks(); + break; + + case SDL_CONTROLLERDEVICEREMOVED: +#if 1 + Error(ERR_DEBUG, "SDL_CONTROLLERDEVICEREMOVED: device %d removed", + event->cdevice.which); +#endif + InitJoysticks(); + break; + + case SDL_CONTROLLERAXISMOTION: +#if 1 + Error(ERR_DEBUG, "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d", + event->caxis.which, event->caxis.axis, event->caxis.value); +#endif + setJoystickAxis(event->caxis.which, + event->caxis.axis, + event->caxis.value); + break; + + case SDL_CONTROLLERBUTTONDOWN: +#if 1 + Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONDOWN: device %d, button %d", + event->cbutton.which, event->cbutton.button); +#endif + setJoystickButton(event->cbutton.which, + event->cbutton.button, + TRUE); + break; + + case SDL_CONTROLLERBUTTONUP: +#if 1 + Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONUP: device %d, button %d", + event->cbutton.which, event->cbutton.button); +#endif + setJoystickButton(event->cbutton.which, + event->cbutton.button, + FALSE); + break; +#endif + case SDL_JOYAXISMOTION: - if (event->jaxis.axis < 2) - sdl_js_axis[event->jaxis.which][event->jaxis.axis]= event->jaxis.value; + if (sdl_is_controller[event->jaxis.which]) + break; + +#if 1 + Error(ERR_DEBUG, "SDL_JOYAXISMOTION: device %d, axis %d: %d", + event->jaxis.which, event->jaxis.axis, event->jaxis.value); +#endif + if (event->jaxis.axis < 4) + setJoystickAxis(event->jaxis.which, + event->jaxis.axis, + event->jaxis.value); break; case SDL_JOYBUTTONDOWN: - if (event->jbutton.button < 2) - sdl_js_button[event->jbutton.which][event->jbutton.button] = TRUE; + if (sdl_is_controller[event->jaxis.which]) + break; + +#if 1 + Error(ERR_DEBUG, "SDL_JOYBUTTONDOWN: device %d, button %d", + event->jbutton.which, event->jbutton.button); +#endif + if (event->jbutton.button < 4) + setJoystickButton(event->jbutton.which, + event->jbutton.button, + TRUE); break; case SDL_JOYBUTTONUP: - if (event->jbutton.button < 2) - sdl_js_button[event->jbutton.which][event->jbutton.button] = FALSE; + if (sdl_is_controller[event->jaxis.which]) + break; + +#if 1 + Error(ERR_DEBUG, "SDL_JOYBUTTONUP: device %d, button %d", + event->jbutton.which, event->jbutton.button); +#endif + if (event->jbutton.button < 4) + setJoystickButton(event->jbutton.which, + event->jbutton.button, + FALSE); break; default: @@ -2615,19 +2846,73 @@ void SDLInitJoysticks() { static boolean sdl_joystick_subsystem_initialized = FALSE; boolean print_warning = !sdl_joystick_subsystem_initialized; +#if defined(TARGET_SDL2) + char *mappings_file_base = getPath2(options.conf_directory, + GAMECONTROLLER_BASENAME); + char *mappings_file_user = getPath2(getUserGameDataDir(), + GAMECONTROLLER_BASENAME); + int num_mappings; +#endif int i; if (!sdl_joystick_subsystem_initialized) { sdl_joystick_subsystem_initialized = TRUE; +#if defined(TARGET_SDL2) + SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0"); + + if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0) +#else if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0) +#endif { Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError()); return; } + +#if defined(TARGET_SDL2) + num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_base); + + if (num_mappings != -1) + Error(ERR_INFO, "%d game controller base mapping(s) added", num_mappings); + else + Error(ERR_WARN, "no game controller base mappings found"); + + num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file_user); + + if (num_mappings != -1) + Error(ERR_INFO, "%d game controller user mapping(s) added", num_mappings); + else + Error(ERR_WARN, "no game controller user mappings found"); + + Error(ERR_INFO, "%d joystick(s) found:", SDL_NumJoysticks()); + + checked_free(mappings_file_base); + checked_free(mappings_file_user); + + for (i = 0; i < SDL_NumJoysticks(); i++) + { + const char *name, *type; + + if (SDL_IsGameController(i)) + { + name = SDL_GameControllerNameForIndex(i); + type = "game controller"; + } + else + { + name = SDL_JoystickNameForIndex(i); + type = "joystick"; + } + + Error(ERR_INFO, "- joystick %d (%s): '%s'", + i, type, (name ? name : "(Unknown)")); + } +#endif } + /* assign joysticks from configured to connected joystick for all players */ for (i = 0; i < MAX_PLAYERS; i++) { /* get configured joystick for this player */ @@ -2642,29 +2927,24 @@ void SDLInitJoysticks() joystick_nr = -1; } - /* misuse joystick file descriptor variable to store joystick number */ - joystick.fd[i] = joystick_nr; - - if (joystick_nr == -1) - continue; + /* store configured joystick number for each player */ + joystick.nr[i] = joystick_nr; + } + /* now open all connected joysticks (regardless if configured or not) */ + for (i = 0; i < SDL_NumJoysticks(); i++) + { /* this allows subsequent calls to 'InitJoysticks' for re-initialization */ - if (SDLCheckJoystickOpened(joystick_nr)) - SDLCloseJoystick(joystick_nr); + if (SDLCheckJoystickOpened(i)) + SDLCloseJoystick(i); - if (!setup.input[i].use_joystick) - continue; - - if (!SDLOpenJoystick(joystick_nr)) - { - if (print_warning) - Error(ERR_WARN, "cannot open joystick %d", joystick_nr); - - continue; - } - - joystick.status = JOYSTICK_ACTIVATED; + if (SDLOpenJoystick(i)) + joystick.status = JOYSTICK_ACTIVATED; + else if (print_warning) + Error(ERR_WARN, "cannot open joystick %d", i); } + + SDLClearJoystickState(); } boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2) @@ -2685,9 +2965,14 @@ boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2) return TRUE; } + +/* ========================================================================= */ +/* touch input overlay functions */ +/* ========================================================================= */ + +#if defined(USE_TOUCH_INPUT_OVERLAY) static void DrawTouchInputOverlay() { -#if defined(USE_TOUCH_INPUT_OVERLAY) static SDL_Texture *texture = NULL; static boolean initialized = FALSE; static boolean deactivated = TRUE; @@ -2696,11 +2981,12 @@ static void DrawTouchInputOverlay() static int alpha_step = 5; static int alpha_last = 0; static int alpha = 0; + boolean active = (overlay.enabled && overlay.active); - if (!overlay.active && deactivated) + if (!active && deactivated) return; - if (overlay.active) + if (active) { if (alpha < alpha_max) alpha = MIN(alpha + alpha_step, alpha_max); @@ -2753,9 +3039,29 @@ static void DrawTouchInputOverlay() alpha_last = alpha; + float ratio_overlay = (float) width / height; + float ratio_screen = (float) video.screen_width / video.screen_height; + int width_scaled, height_scaled; + int xpos, ypos; + + if (ratio_overlay > ratio_screen) + { + width_scaled = video.screen_width; + height_scaled = video.screen_height * ratio_screen / ratio_overlay; + xpos = 0; + ypos = video.screen_height - height_scaled; + } + else + { + width_scaled = video.screen_width * ratio_overlay / ratio_screen; + height_scaled = video.screen_height; + xpos = (video.screen_width - width_scaled) / 2; + ypos = 0; + } + SDL_Rect src_rect = { 0, 0, width, height }; - SDL_Rect dst_rect = { 0, 0, video.screen_width, video.screen_height }; + SDL_Rect dst_rect = { xpos, ypos, width_scaled, height_scaled }; SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect); -#endif } +#endif