diff --git a/gamedir/ui/resource/language/bugfixedhl_english.txt b/gamedir/ui/resource/language/bugfixedhl_english.txt index 7ff395c3..481f1f0c 100644 Binary files a/gamedir/ui/resource/language/bugfixedhl_english.txt and b/gamedir/ui/resource/language/bugfixedhl_english.txt differ diff --git a/gamedir/ui/resource/language/bugfixedhl_russian.txt b/gamedir/ui/resource/language/bugfixedhl_russian.txt index 8f270b37..1a0dfb79 100644 Binary files a/gamedir/ui/resource/language/bugfixedhl_russian.txt and b/gamedir/ui/resource/language/bugfixedhl_russian.txt differ diff --git a/src/game/client/gameui/options/options_general.cpp b/src/game/client/gameui/options/options_general.cpp index eac2a309..87bb9e36 100644 --- a/src/game/client/gameui/options/options_general.cpp +++ b/src/game/client/gameui/options/options_general.cpp @@ -10,6 +10,7 @@ #include "hud.h" #include "cl_util.h" #include "engine_patches.h" +#include "sdl_rt.h" extern ConVar m_input; @@ -27,7 +28,7 @@ CGeneralSubOptions::CGeneralSubOptions(vgui2::Panel *parent) m_pRawInputLabel = new vgui2::Label(this, "RawInputLabel", "#BHL_AdvOptions_General_Input"); m_pInputMethodBox = new vgui2::ComboBox(this, "InputMethodBox", 3, false); - m_InputMethodItems[0] = m_pInputMethodBox->AddItem("#BHL_AdvOptions_General_InputEng", new KeyValues("Item", "value", 0)); + m_InputMethodItems[0] = m_pInputMethodBox->AddItem("#BHL_AdvOptions_General_InputWin", new KeyValues("Item", "value", 0)); m_InputMethodItems[1] = m_pInputMethodBox->AddItem(GetItemText("BHL_AdvOptions_General_InputDX", IsWindows()), new KeyValues("Item", "value", 1)); m_InputMethodItems[2] = m_pInputMethodBox->AddItem(GetItemText("BHL_AdvOptions_General_InputSDL", !IsWindows()), new KeyValues("Item", "value", 2)); @@ -51,11 +52,9 @@ CGeneralSubOptions::CGeneralSubOptions(vgui2::Panel *parent) LoadControlSettings(VGUI2_ROOT_DIR "resource/options/GeneralSubOptions.res"); // Disable unsupported input methods - if (!IsWindows()) - m_pInputMethodBox->SetItemEnabled(m_InputMethodItems[1], false); - - if (!CEnginePatches::Get().IsSDLEngine()) - m_pInputMethodBox->SetItemEnabled(m_InputMethodItems[2], false); + m_pInputMethodBox->SetItemEnabled(m_InputMethodItems[0], IsWindows()); // WindowsCursor + m_pInputMethodBox->SetItemEnabled(m_InputMethodItems[1], IsWindows()); // DirectInput + m_pInputMethodBox->SetItemEnabled(m_InputMethodItems[2], GetSDL()->IsGood()); // RawInput // Disable HTML MOTD if SteamAPI not available if (!SteamAPI_IsAvailable()) diff --git a/src/game/client/hud_msg.cpp b/src/game/client/hud_msg.cpp index 07a86c7e..809bc058 100644 --- a/src/game/client/hud_msg.cpp +++ b/src/game/client/hud_msg.cpp @@ -41,12 +41,12 @@ void ClearEventList(void); #endif extern ConVar zoom_sensitivity_ratio; -extern cvar_t *sensitivity; extern float g_lastFOV; cvar_t *cl_lw = nullptr; void CAM_ToFirstPerson(void); +float IN_GetMouseSensitivity(); /// USER-DEFINED SERVER MESSAGE HANDLERS @@ -177,7 +177,7 @@ int CHud::MsgFunc_SetFOV(const char *pszName, int iSize, void *pbuf) else { // set a new sensitivity that is proportional to the change from the FOV default - m_flMouseSensitivity = sensitivity->value * ((float)newfov / (float)def_fov) * zoom_sensitivity_ratio.GetFloat(); + m_flMouseSensitivity = IN_GetMouseSensitivity() * ((float)newfov / (float)def_fov) * zoom_sensitivity_ratio.GetFloat(); } // Update crosshair after zoom change diff --git a/src/game/client/hud_redraw.cpp b/src/game/client/hud_redraw.cpp index cd2f8087..753840a0 100644 --- a/src/game/client/hud_redraw.cpp +++ b/src/game/client/hud_redraw.cpp @@ -34,8 +34,8 @@ int grgLogoFrame[MAX_LOGO_FRAMES] = { }; float HUD_GetFOV(void); +float IN_GetMouseSensitivity(); -extern cvar_t *sensitivity; extern ConVar zoom_sensitivity_ratio; ConVar hud_colortext("hud_colortext", "1", FCVAR_BHL_ARCHIVE); @@ -82,7 +82,7 @@ void CHud::Think(void) else { // set a new sensitivity that is proportional to the change from the FOV default - m_flMouseSensitivity = sensitivity->value * ((float)newfov / (float)default_fov.GetInt()) * zoom_sensitivity_ratio.GetFloat(); + m_flMouseSensitivity = IN_GetMouseSensitivity() * ((float)newfov / (float)default_fov.GetInt()) * zoom_sensitivity_ratio.GetFloat(); } // think about default fov diff --git a/src/game/client/inputw32.cpp b/src/game/client/inputw32.cpp index 983c6b93..f66305db 100644 --- a/src/game/client/inputw32.cpp +++ b/src/game/client/inputw32.cpp @@ -38,7 +38,7 @@ enum class MouseMode { Auto = -1, - Engine = 0, + WindowsCursor = 0, DirectInput = 1, RawInput = 2, }; @@ -66,8 +66,6 @@ extern cvar_t *cl_forwardspeed; extern cvar_t *cl_pitchspeed; extern cvar_t *cl_movespeedkey; -static double s_flRawInputUpdateTime = 0.0f; -static bool m_bRawInput = false; static bool m_bMouseThread = false; extern globalvars_t *gpGlobals; @@ -78,10 +76,10 @@ cvar_t *sensitivity; static cvar_t *m_rawinput = nullptr; ConVar m_input("m_input", "-1", FCVAR_BHL_ARCHIVE, "Mouse input mode\n" - "- 0: Engine\n" + "- 0: Windows cursor position (Windows-only)\n" "- 1: DirectInput (Windows-only)\n" "- 2: Raw Input (Steam-only)"); -static MouseMode m_mode = MouseMode::Engine; +static MouseMode m_mode = MouseMode::Auto; // Custom mouse acceleration (0 disable, 1 to enable, 2 enable with separate yaw/pitch rescale) static cvar_t *m_customaccel; @@ -156,6 +154,7 @@ cvar_t *joy_advaxisz; cvar_t *joy_advaxisr; cvar_t *joy_advaxisu; cvar_t *joy_advaxisv; +cvar_t *joy_supported; cvar_t *joy_forwardthreshold; cvar_t *joy_sidethreshold; cvar_t *joy_pitchthreshold; @@ -198,7 +197,7 @@ void IN_UpdateMouseMode() { MouseMode newMode = (MouseMode)clamp(m_input.GetInt(), -1, 2); - if (newMode != m_mode) + if (newMode == MouseMode::Auto || newMode != m_mode) { if (newMode == MouseMode::Auto) { @@ -212,9 +211,13 @@ void IN_UpdateMouseMode() // Prefer raw input on SteamPipe engine newMode = MouseMode::RawInput; } + else if (IsWindows()) + { + newMode = MouseMode::WindowsCursor; + } else { - newMode = MouseMode::Engine; + HUD_FatalError("No mouse mode is supported. This can't happen."); } } @@ -228,9 +231,9 @@ void IN_UpdateMouseMode() switch (newMode) { - case MouseMode::Engine: + case MouseMode::WindowsCursor: { - ConPrintf("Set mouse input mode to engine input.\n"); + ConPrintf("Set mouse input mode to Windows Cursor.\n"); if (rawinput.IsValid()) rawinput.SetValue(0); break; @@ -264,12 +267,16 @@ void IN_UpdateMouseMode() ConPrintf("Set mouse input mode to Raw Input.\n"); rawinput.SetValue(1); } - else + else if (IsWindows()) { ConPrintf(ConColor::Cyan, "Raw Input is not supported on your client.\n"); ConPrintf(ConColor::Cyan, "Setting to DirectInput instead.\n"); newMode = MouseMode::DirectInput; } + else + { + HUD_FatalError("Raw input is not supported and not Windows. This can't happen."); + } break; } @@ -288,6 +295,7 @@ void IN_UpdateMouseMode() #ifndef PLATFORM_WINDOWS // DirectInput is Windows-only Assert(m_mode != MouseMode::DirectInput); + Assert(m_mode != MouseMode::WindowsCursor); #endif } @@ -308,18 +316,34 @@ void IN_RunFrame() if (rawinput.IsValid() && GetSDL()->IsGood()) { - if (rawinput.GetBool() && m_mode != MouseMode::RawInput) + if (rawinput.GetBool()) { - m_input.SetValue((int)MouseMode::RawInput); - IN_UpdateMouseMode(); + if (m_mode != MouseMode::RawInput) + { + m_input.SetValue((int)MouseMode::RawInput); + IN_UpdateMouseMode(); - if (IsWindows()) - ConPrintf(ConColor::Cyan, "NOTE: It is recommended to use `m_input 1` on Windows.\n"); + if (IsWindows()) + ConPrintf(ConColor::Cyan, "NOTE: It is recommended to use `m_input 1` on Windows.\n"); + } } - else if (!rawinput.GetBool() && m_mode == MouseMode::RawInput) + else { - m_input.SetValue((int)MouseMode::Engine); - IN_UpdateMouseMode(); + if (IsWindows()) + { + if (m_mode == MouseMode::RawInput) + { + m_input.SetValue((int)MouseMode::WindowsCursor); + IN_UpdateMouseMode(); + } + } + else + { + m_input.SetValue((int)MouseMode::RawInput); + rawinput.SetValue(true); + IN_UpdateMouseMode(); + ConPrintf(ConColor::Cyan, "Linux only supports Raw Input mode.\n"); + } } } } @@ -611,6 +635,28 @@ void IN_GetMousePos(int *mx, int *my) gEngfuncs.GetMousePosition(mx, my); } +/* +=========== +IN_GetMouseSensitivity + +Get mouse sensitivity with sanitization +=========== +*/ +float IN_GetMouseSensitivity() +{ + // Absurdly high sensitivity values can cause the game to hang, so clamp + if (sensitivity->value > 10000.0) + { + gEngfuncs.Cvar_SetValue(sensitivity->name, 10000.0); + } + else if (sensitivity->value < 0.01) + { + gEngfuncs.Cvar_SetValue(sensitivity->name, 0.01); + } + + return sensitivity->value; +} + /* =========== IN_ResetMouse @@ -622,19 +668,12 @@ void IN_ResetMouse(void) { // no work to do in SDL #ifdef _WIN32 - if (!m_bRawInput && mouseactive && gEngfuncs.GetWindowCenterX && gEngfuncs.GetWindowCenterY) + if (m_mode == MouseMode::WindowsCursor && mouseactive && gEngfuncs.GetWindowCenterX && gEngfuncs.GetWindowCenterY) { SetCursorPos(gEngfuncs.GetWindowCenterX(), gEngfuncs.GetWindowCenterY()); ThreadInterlockedExchange(&old_mouse_pos.x, gEngfuncs.GetWindowCenterX()); ThreadInterlockedExchange(&old_mouse_pos.y, gEngfuncs.GetWindowCenterY()); } - - if (gpGlobals && gpGlobals->time - s_flRawInputUpdateTime > 1.0f) - { - s_flRawInputUpdateTime = gpGlobals->time; - if (m_bRawInput) - m_bRawInput = m_rawinput->value != 0; - } #endif } @@ -690,7 +729,7 @@ void IN_ScaleMouse(float *x, float *y) float my = *y; // This is the default sensitivity - float mouse_senstivity = (gHUD.GetSensitivity() != 0) ? gHUD.GetSensitivity() : sensitivity->value; + float mouse_senstivity = (gHUD.GetSensitivity() != 0) ? gHUD.GetSensitivity() : IN_GetMouseSensitivity(); // Using special accleration values if (m_customaccel->value != 0) @@ -747,10 +786,11 @@ void IN_MouseMove(float frametime, usercmd_t *cmd) // move the camera, or if the mouse cursor is visible or if we're in intermission if (!iMouseInUse && !gHUD.m_iIntermission && !g_pVGuiSurface->IsCursorVisible()) { - int deltaX, deltaY; -#ifdef _WIN32 - if (m_mode == MouseMode::Engine) + int deltaX = 0, deltaY = 0; + + if (m_mode == MouseMode::WindowsCursor) { +#ifdef _WIN32 if (m_bMouseThread) { ThreadInterlockedExchange(¤t_pos.x, s_mouseDeltaX); @@ -762,9 +802,13 @@ void IN_MouseMove(float frametime, usercmd_t *cmd) { GetCursorPos(¤t_pos); } +#else + Assert(!("MouseMode::WindowsCursor is not supported by platform")); +#endif } else if (m_mode == MouseMode::DirectInput) { +#ifdef _WIN32 if (dinput_mouse_acquired) { HRESULT hr = dinput_lpdiMouse->GetDeviceState(sizeof(DIMOUSESTATE), (LPVOID)&dinput_mousestate); @@ -773,9 +817,11 @@ void IN_MouseMove(float frametime, usercmd_t *cmd) else if (hr == DI_BUFFEROVERFLOW) DINPUT_SetBufferSize(); } +#else + Assert(!("MouseMode::DirectInput is not supported by platform")); +#endif } else if (m_mode == MouseMode::RawInput) -#endif { IN_SetRelativeMouseMode(true); GetSDL()->GetRelativeMouseState(&deltaX, &deltaY); @@ -783,9 +829,9 @@ void IN_MouseMove(float frametime, usercmd_t *cmd) current_pos.y = deltaY; } -#ifdef _WIN32 - if (m_mode == MouseMode::Engine) + if (m_mode == MouseMode::WindowsCursor) { +#ifdef _WIN32 if (m_bMouseThread) { mx = current_pos.x; @@ -796,17 +842,23 @@ void IN_MouseMove(float frametime, usercmd_t *cmd) mx = current_pos.x - gEngfuncs.GetWindowCenterX() + mx_accum; my = current_pos.y - gEngfuncs.GetWindowCenterY() + my_accum; } +#else + Assert(!("MouseMode::WindowsCursor is not supported by platform")); +#endif } else if (m_mode == MouseMode::DirectInput) { +#ifdef _WIN32 if (dinput_mouse_acquired) { mx = dinput_mousestate.lX + mx_accum; my = dinput_mousestate.lY + my_accum; } +#else + Assert(!("MouseMode::DirectInput is not supported by platform")); +#endif } else if (m_mode == MouseMode::RawInput) -#endif { mx = deltaX + mx_accum; my = deltaY + my_accum; @@ -866,17 +918,20 @@ void IN_MouseMove(float frametime, usercmd_t *cmd) } else { -#ifdef _WIN32 - if (m_mode == MouseMode::DirectInput && dinput_mouse_acquired) + if (m_mode == MouseMode::DirectInput) { - // Discard any info when mouse is not active - DIMOUSESTATE state; - dinput_lpdiMouse->GetDeviceState(sizeof(DIMOUSESTATE), (LPVOID)&state); +#ifdef _WIN32 + if (dinput_mouse_acquired) + { + // Discard any info when mouse is not active + DIMOUSESTATE state; + dinput_lpdiMouse->GetDeviceState(sizeof(DIMOUSESTATE), (LPVOID)&state); + } +#endif } else if (m_mode == MouseMode::RawInput) -#endif { - // Disable realative mouse input + // Disable relative mouse input IN_SetRelativeMouseMode(false); } } @@ -908,9 +963,9 @@ void CL_DLLEXPORT IN_Accumulate(void) { if (mouseactive) { -#ifdef _WIN32 - if (m_mode == MouseMode::Engine) + if (m_mode == MouseMode::WindowsCursor) { +#ifdef _WIN32 if (!m_bMouseThread) { GetCursorPos(¤t_pos); @@ -918,9 +973,13 @@ void CL_DLLEXPORT IN_Accumulate(void) mx_accum += current_pos.x - gEngfuncs.GetWindowCenterX(); my_accum += current_pos.y - gEngfuncs.GetWindowCenterY(); } +#else + Assert(!("MouseMode::WindowsCursor is not supported by platform")); +#endif } else if (m_mode == MouseMode::DirectInput) { +#ifdef _WIN32 if (dinput_mouse_acquired) { HRESULT hr = dinput_lpdiMouse->GetDeviceState(sizeof(DIMOUSESTATE), (LPVOID)&dinput_mousestate); @@ -933,9 +992,11 @@ void CL_DLLEXPORT IN_Accumulate(void) mx_accum += dinput_mousestate.lX; my_accum += dinput_mousestate.lY; } +#else + Assert(!("MouseMode::DirectInput is not supported by platform")); +#endif } else if (m_mode == MouseMode::RawInput) -#endif { int deltaX, deltaY; GetSDL()->GetRelativeMouseState(&deltaX, &deltaY); @@ -971,12 +1032,10 @@ IN_StartupJoystick void IN_StartupJoystick(void) { // abort startup if user requests no joystick - if (gEngfuncs.CheckParm("-nojoy", NULL)) + static bool noJoy = gEngfuncs.CheckParm("-nojoy", NULL); + if (noJoy) return; - // assume no joystick - joy_avail = 0; - // Joystick only with SDL if (!GetSDL()->IsGood()) { @@ -984,36 +1043,53 @@ void IN_StartupJoystick(void) return; } + static float flLastCheck = 0.0f; + if (flLastCheck > 0.0f && (gEngfuncs.GetAbsoluteTime() - flLastCheck) < 1.0f) + return; + flLastCheck = gEngfuncs.GetAbsoluteTime(); + int nJoysticks = GetSDL()->NumJoysticks(); if (nJoysticks > 0) { - for (int i = 0; i < nJoysticks; i++) + if (!s_pJoystick) { - if (GetSDL()->IsGameController(i)) + for (int i = 0; i < nJoysticks; i++) { - s_pJoystick = GetSDL()->GameControllerOpen(i); - if (s_pJoystick) + if (GetSDL()->IsGameController(i)) { - //save the joystick's number of buttons and POV status - joy_numbuttons = SDL_CONTROLLER_BUTTON_MAX; - joy_haspov = 0; - - // old button and POV states default to no buttons pressed - joy_oldbuttonstate = joy_oldpovstate = 0; - - // mark the joystick as available and advanced initialization not completed - // this is needed as cvars are not available during initialization - gEngfuncs.Con_Printf("joystick found\n\n", GetSDL()->GameControllerName(s_pJoystick)); - joy_avail = 1; - joy_advancedinit = 0; - break; + s_pJoystick = GetSDL()->GameControllerOpen(i); + if (s_pJoystick) + { + //save the joystick's number of buttons and POV status + joy_numbuttons = SDL_CONTROLLER_BUTTON_MAX; + joy_haspov = 0; + + // old button and POV states default to no buttons pressed + joy_oldbuttonstate = joy_oldpovstate = 0; + + // mark the joystick as available and advanced initialization not completed + // this is needed as cvars are not available during initialization + gEngfuncs.Con_Printf("joystick found %s\n", GetSDL()->GameControllerName(s_pJoystick)); + joy_avail = 1; + joy_advancedinit = 0; + break; + } } } } } else { - gEngfuncs.Con_DPrintf("joystick not found -- driver not present\n\n"); + if (s_pJoystick) + GetSDL()->GameControllerClose(s_pJoystick); + + s_pJoystick = nullptr; + + if (joy_avail) + { + joy_avail = 0; + gEngfuncs.Con_DPrintf("joystick not found -- driver not present\n\n"); + } } } @@ -1201,6 +1277,9 @@ void IN_JoyMove(float frametime, usercmd_t *cmd) joy_advancedinit = 1; } + // re-scan for joystick presence + IN_StartupJoystick(); + // verify joystick is available and that the user wants to use it if (!joy_avail || !in_joystick->value) { @@ -1387,7 +1466,7 @@ IN_Init void IN_Init(void) { m_filter = gEngfuncs.pfnRegisterVariable("m_filter", "0", FCVAR_ARCHIVE); - sensitivity = gEngfuncs.pfnRegisterVariable("sensitivity", "3", FCVAR_ARCHIVE); // user mouse sensitivity setting. + sensitivity = gEngfuncs.pfnRegisterVariable("sensitivity", "3", FCVAR_ARCHIVE | FCVAR_FILTERSTUFFTEXT); // user mouse sensitivity setting. in_joystick = gEngfuncs.pfnRegisterVariable("joystick", "0", FCVAR_ARCHIVE); joy_name = gEngfuncs.pfnRegisterVariable("joyname", "joystick", 0); @@ -1398,6 +1477,7 @@ void IN_Init(void) joy_advaxisr = gEngfuncs.pfnRegisterVariable("joyadvaxisr", "0", 0); joy_advaxisu = gEngfuncs.pfnRegisterVariable("joyadvaxisu", "0", 0); joy_advaxisv = gEngfuncs.pfnRegisterVariable("joyadvaxisv", "0", 0); + joy_supported = gEngfuncs.pfnRegisterVariable("joysupported", "1", 0); joy_forwardthreshold = gEngfuncs.pfnRegisterVariable("joyforwardthreshold", "0.15", 0); joy_sidethreshold = gEngfuncs.pfnRegisterVariable("joysidethreshold", "0.15", 0); joy_pitchthreshold = gEngfuncs.pfnRegisterVariable("joypitchthreshold", "0.15", 0); @@ -1418,19 +1498,15 @@ void IN_Init(void) if (!IsWindows()) { - // All non-Windows version of the game should have m_rawinput. + // All non-Windows versions of the game should have m_rawinput. Assert(m_rawinput); } #ifdef _WIN32 - if (m_rawinput) - m_bRawInput = m_rawinput->value != 0; - else - m_bRawInput = false; m_bMouseThread = gEngfuncs.CheckParm("-mousethread", NULL) != NULL; m_mousethread_sleep = gEngfuncs.pfnRegisterVariable("m_mousethread_sleep", "10", FCVAR_ARCHIVE); - if (!m_bRawInput && m_bMouseThread && m_mousethread_sleep) + if (m_bMouseThread && m_mousethread_sleep) { s_mouseDeltaX = s_mouseDeltaY = 0; diff --git a/src/game/client/sdl_rt.cpp b/src/game/client/sdl_rt.cpp index 94d5ee3d..87727783 100644 --- a/src/game/client/sdl_rt.cpp +++ b/src/game/client/sdl_rt.cpp @@ -89,6 +89,7 @@ void CSDLRuntime::InitWindows() m_bIsGood = m_bIsGood && fnLoadSym(NumJoysticks, "SDL_NumJoysticks"); m_bIsGood = m_bIsGood && fnLoadSym(IsGameController, "SDL_IsGameController"); m_bIsGood = m_bIsGood && fnLoadSym(GameControllerOpen, "SDL_GameControllerOpen"); + m_bIsGood = m_bIsGood && fnLoadSym(GameControllerClose, "SDL_GameControllerClose"); m_bIsGood = m_bIsGood && fnLoadSym(GameControllerName, "SDL_GameControllerName"); m_bIsGood = m_bIsGood && fnLoadSym(GameControllerGetAxis, "SDL_GameControllerGetAxis"); m_bIsGood = m_bIsGood && fnLoadSym(GameControllerGetButton, "SDL_GameControllerGetButton"); @@ -110,6 +111,7 @@ void CSDLRuntime::InitOther() NumJoysticks = &SDL_NumJoysticks; IsGameController = &SDL_IsGameController; GameControllerOpen = &SDL_GameControllerOpen; + GameControllerClose = &SDL_GameControllerClose; GameControllerName = &SDL_GameControllerName; GameControllerGetAxis = &SDL_GameControllerGetAxis; GameControllerGetButton = &SDL_GameControllerGetButton; diff --git a/src/game/client/sdl_rt.h b/src/game/client/sdl_rt.h index 135f0779..ce517a0d 100644 --- a/src/game/client/sdl_rt.h +++ b/src/game/client/sdl_rt.h @@ -14,6 +14,7 @@ class CSDLRuntime decltype(SDL_NumJoysticks) *NumJoysticks = nullptr; decltype(SDL_IsGameController) *IsGameController = nullptr; decltype(SDL_GameControllerOpen) *GameControllerOpen = nullptr; + decltype(SDL_GameControllerClose) *GameControllerClose = nullptr; decltype(SDL_GameControllerName) *GameControllerName = nullptr; decltype(SDL_GameControllerGetAxis) *GameControllerGetAxis = nullptr; decltype(SDL_GameControllerGetButton) *GameControllerGetButton = nullptr;