From 1dc8a005ed111463c22c17b487e5ec8a3e2d30f3 Mon Sep 17 00:00:00 2001 From: daoge_cmd <3523206925@qq.com> Date: Wed, 4 Mar 2026 21:19:40 +0800 Subject: [PATCH] refactor: refactor KBM input code --- .../Common/Tutorial/ChoiceTask.cpp | 4 +- Minecraft.Client/Common/Tutorial/InfoTask.cpp | 4 +- .../UI/IUIScene_AbstractContainerMenu.cpp | 42 +- .../UI/IUIScene_AbstractContainerMenu.h | 12 + Minecraft.Client/Common/UI/UIControl.cpp | 2 - Minecraft.Client/Common/UI/UIControl.h | 2 +- Minecraft.Client/Common/UI/UIController.cpp | 254 +++++++++- Minecraft.Client/Common/UI/UIController.h | 4 + Minecraft.Client/Common/UI/UIGroup.h | 2 - Minecraft.Client/Common/UI/UIScene.h | 4 +- .../UI/UIScene_AbstractContainerMenu.cpp | 166 ++----- .../Common/UI/UIScene_AbstractContainerMenu.h | 8 +- .../Common/UI/UIScene_LoadOrJoinMenu.cpp | 8 + .../UI/UIScene_SettingsGraphicsMenu.cpp | 38 +- Minecraft.Client/Input.cpp | 147 +++--- Minecraft.Client/Input.h | 9 +- Minecraft.Client/LocalPlayer.cpp | 33 +- Minecraft.Client/Minecraft.cpp | 148 +++--- Minecraft.Client/Screen.cpp | 8 +- .../Windows64/KeyboardMouseInput.cpp | 454 +++++++++++------- .../Windows64/KeyboardMouseInput.h | 170 ++++--- .../Windows64/Windows64_Minecraft.cpp | 220 +++++---- Minecraft.Client/stubs.cpp | 103 +++- Minecraft.Client/stubs.h | 33 +- README.md | 1 - 25 files changed, 1187 insertions(+), 689 deletions(-) diff --git a/Minecraft.Client/Common/Tutorial/ChoiceTask.cpp b/Minecraft.Client/Common/Tutorial/ChoiceTask.cpp index f42b3ee0..1ea34ace 100644 --- a/Minecraft.Client/Common/Tutorial/ChoiceTask.cpp +++ b/Minecraft.Client/Common/Tutorial/ChoiceTask.cpp @@ -58,7 +58,7 @@ bool ChoiceTask::isCompleted() #ifdef _WINDOWS64 if (!m_bConfirmMappingComplete && (InputManager.GetValue(xboxPad, m_iConfirmMapping) > 0 - || KMInput.IsKeyDown(VK_RETURN))) + || g_KBMInput.IsKeyDown(VK_RETURN))) #else if (!m_bConfirmMappingComplete && InputManager.GetValue(xboxPad, m_iConfirmMapping) > 0) @@ -70,7 +70,7 @@ bool ChoiceTask::isCompleted() #ifdef _WINDOWS64 if (!m_bCancelMappingComplete && (InputManager.GetValue(xboxPad, m_iCancelMapping) > 0 - || KMInput.IsKeyDown('B'))) + || g_KBMInput.IsKeyDown('B'))) #else if (!m_bCancelMappingComplete && InputManager.GetValue(xboxPad, m_iCancelMapping) > 0) diff --git a/Minecraft.Client/Common/Tutorial/InfoTask.cpp b/Minecraft.Client/Common/Tutorial/InfoTask.cpp index 43a10357..748093e5 100644 --- a/Minecraft.Client/Common/Tutorial/InfoTask.cpp +++ b/Minecraft.Client/Common/Tutorial/InfoTask.cpp @@ -7,7 +7,7 @@ #include "TutorialConstraints.h" #include "InfoTask.h" #include "..\..\..\Minecraft.World\Material.h" -#include "..\..\KeyboardMouseInput.h" +#include "..\..\Windows64\KeyboardMouseInput.h" InfoTask::InfoTask(Tutorial *tutorial, int descriptionId, int promptId /*= -1*/, bool requiresUserInput /*= false*/, int iMapping /*= 0*/, ETelemetryChallenges telemetryEvent /*= eTelemetryTutorial_NoEvent*/) @@ -67,7 +67,7 @@ bool InfoTask::isCompleted() if(!current) { #ifdef _WINDOWS64 - if (InputManager.GetValue(pMinecraft->player->GetXboxPad(), (*it).first) > 0 || KMInput.IsKeyDown(VK_SPACE)) + if (InputManager.GetValue(pMinecraft->player->GetXboxPad(), (*it).first) > 0 || g_KBMInput.IsKeyDown(VK_SPACE)) #else if( InputManager.GetValue(pMinecraft->player->GetXboxPad(), (*it).first) > 0) #endif diff --git a/Minecraft.Client/Common/UI/IUIScene_AbstractContainerMenu.cpp b/Minecraft.Client/Common/UI/IUIScene_AbstractContainerMenu.cpp index 667431b2..88258421 100644 --- a/Minecraft.Client/Common/UI/IUIScene_AbstractContainerMenu.cpp +++ b/Minecraft.Client/Common/UI/IUIScene_AbstractContainerMenu.cpp @@ -13,6 +13,12 @@ #include #endif +#ifdef _WINDOWS64 +#include "..\..\Windows64\KeyboardMouseInput.h" + +SavedInventoryCursorPos g_savedInventoryCursorPos = { 0.0f, 0.0f, false }; +#endif + IUIScene_AbstractContainerMenu::IUIScene_AbstractContainerMenu() { m_menu = NULL; @@ -474,6 +480,34 @@ void IUIScene_AbstractContainerMenu::onMouseTick() } #endif +#ifdef _WINDOWS64 + if (!g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive()) + { + int deltaX = g_KBMInput.GetMouseDeltaX(); + int deltaY = g_KBMInput.GetMouseDeltaY(); + + extern HWND g_hWnd; + RECT rc; + GetClientRect(g_hWnd, &rc); + int winW = rc.right - rc.left; + int winH = rc.bottom - rc.top; + + if (winW > 0 && winH > 0) + { + float scaleX = (float)getMovieWidth() / (float)winW; + float scaleY = (float)getMovieHeight() / (float)winH; + + vPointerPos.x += (float)deltaX * scaleX; + vPointerPos.y += (float)deltaY * scaleY; + } + + if (deltaX != 0 || deltaY != 0) + { + bStickInput = true; + } + } +#endif + // Determine which slot the pointer is currently over. ESceneSection eSectionUnderPointer = eSectionNone; int iNewSlotX = -1; @@ -694,7 +728,11 @@ void IUIScene_AbstractContainerMenu::onMouseTick() // If there is no stick input, and we are over a slot, then snap pointer to slot centre. // 4J - TomK - only if this particular component allows so! - if(!m_bPointerDrivenByMouse && CanHaveFocus(eSectionUnderPointer)) +#ifdef _WINDOWS64 + if((g_KBMInput.IsMouseGrabbed() || !g_KBMInput.IsKBMActive()) && CanHaveFocus(eSectionUnderPointer)) +#else + if(CanHaveFocus(eSectionUnderPointer)) +#endif { vPointerPos.x = vSnapPos.x; vPointerPos.y = vSnapPos.y; @@ -1329,7 +1367,7 @@ bool IUIScene_AbstractContainerMenu::handleKeyDown(int iPad, int iAction, bool b // Standard left click buttonNum = 0; - if (KMInput.IsKeyDown(VK_SHIFT)) + if (g_KBMInput.IsKeyDown(VK_LSHIFT)) { { validKeyPress = TRUE; diff --git a/Minecraft.Client/Common/UI/IUIScene_AbstractContainerMenu.h b/Minecraft.Client/Common/UI/IUIScene_AbstractContainerMenu.h index fe79bf19..4877cfce 100644 --- a/Minecraft.Client/Common/UI/IUIScene_AbstractContainerMenu.h +++ b/Minecraft.Client/Common/UI/IUIScene_AbstractContainerMenu.h @@ -1,5 +1,15 @@ #pragma once +#ifdef _WINDOWS64 +struct SavedInventoryCursorPos +{ + float x; + float y; + bool hasSavedPos; +}; +extern SavedInventoryCursorPos g_savedInventoryCursorPos; +#endif + // Uncomment to enable tap input detection to jump 1 slot. Doesn't work particularly well yet, and I feel the system does not need it. // Would probably be required if we decide to slow down the pointer movement. // 4J Stu - There was a request to be able to navigate the scenes with the dpad, so I have used much of the TAP_DETECTION @@ -265,4 +275,6 @@ protected: public: virtual int getPad() = 0; + virtual int getMovieWidth() = 0; + virtual int getMovieHeight() = 0; }; diff --git a/Minecraft.Client/Common/UI/UIControl.cpp b/Minecraft.Client/Common/UI/UIControl.cpp index ec2e13d8..be267ada 100644 --- a/Minecraft.Client/Common/UI/UIControl.cpp +++ b/Minecraft.Client/Common/UI/UIControl.cpp @@ -42,7 +42,6 @@ bool UIControl::setupControl(UIScene *scene, IggyValuePath *parent, const string return res; } -#ifdef __PSVITA__ void UIControl::UpdateControl() { F64 fx, fy, fwidth, fheight; @@ -55,7 +54,6 @@ void UIControl::UpdateControl() m_width = (S32)Math::round(fwidth); m_height = (S32)Math::round(fheight); } -#endif // __PSVITA__ void UIControl::ReInit() { diff --git a/Minecraft.Client/Common/UI/UIControl.h b/Minecraft.Client/Common/UI/UIControl.h index e37f04de..29770df2 100644 --- a/Minecraft.Client/Common/UI/UIControl.h +++ b/Minecraft.Client/Common/UI/UIControl.h @@ -61,8 +61,8 @@ public: UIControl(); virtual bool setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName); -#ifdef __PSVITA__ void UpdateControl(); +#ifdef __PSVITA__ void setHidden(bool bHidden) {m_bHidden=bHidden;} bool getHidden(void) {return m_bHidden;} #endif diff --git a/Minecraft.Client/Common/UI/UIController.cpp b/Minecraft.Client/Common/UI/UIController.cpp index 6ac2f9ba..01ab49ba 100644 --- a/Minecraft.Client/Common/UI/UIController.cpp +++ b/Minecraft.Client/Common/UI/UIController.cpp @@ -2,6 +2,7 @@ #include "UIController.h" #include "UI.h" #include "UIScene.h" +#include "UIControl_Slider.h" #include "..\..\..\Minecraft.World\StringHelpers.h" #include "..\..\LocalPlayer.h" #include "..\..\DLCTexturePack.h" @@ -11,6 +12,9 @@ #include "..\..\EnderDragonRenderer.h" #include "..\..\MultiPlayerLocalPlayer.h" #include "UIFontData.h" +#ifdef _WINDOWS64 +#include "..\..\Windows64\KeyboardMouseInput.h" +#endif #ifdef __PSVITA__ #include #endif @@ -52,6 +56,21 @@ bool UIController::ms_bReloadSkinCSInitialised = false; DWORD UIController::m_dwTrialTimerLimitSecs=DYNAMIC_CONFIG_DEFAULT_TRIAL_TIME; +#ifdef _WINDOWS64 +static UIControl_Slider *FindSliderById(UIScene *pScene, int sliderId) +{ + vector *controls = pScene->GetControls(); + if (!controls) return NULL; + for (size_t i = 0; i < controls->size(); ++i) + { + UIControl *ctrl = (*controls)[i]; + if (ctrl && ctrl->getControlType() == UIControl::eSlider && ctrl->getId() == sliderId) + return (UIControl_Slider *)ctrl; + } + return NULL; +} +#endif + static void RADLINK WarningCallback(void *user_callback_data, Iggy *player, IggyResult code, const char *message) { //enum IggyResult{ IGGY_RESULT_SUCCESS = 0, IGGY_RESULT_Warning_None = 0, @@ -216,6 +235,10 @@ UIController::UIController() m_currentRenderViewport = C4JRender::VIEWPORT_TYPE_FULLSCREEN; m_bCustomRenderPosition = false; m_winUserIndex = 0; + m_mouseDraggingSliderScene = eUIScene_COUNT; + m_mouseDraggingSliderId = -1; + m_lastHoverMouseX = -1; + m_lastHoverMouseY = -1; m_accumulatedTicks = 0; m_lastUiSfx = 0; @@ -761,6 +784,168 @@ void UIController::tickInput() else #endif { +#ifdef _WINDOWS64 + if (!g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive()) + { + UIScene *pScene = NULL; + for (int grp = 0; grp < eUIGroup_COUNT && !pScene; ++grp) + { + pScene = m_groups[grp]->GetTopScene(eUILayer_Debug); + if (!pScene) pScene = m_groups[grp]->GetTopScene(eUILayer_Tooltips); + if (!pScene) pScene = m_groups[grp]->GetTopScene(eUILayer_Error); + if (!pScene) pScene = m_groups[grp]->GetTopScene(eUILayer_Alert); + if (!pScene) pScene = m_groups[grp]->GetTopScene(eUILayer_Popup); + if (!pScene) pScene = m_groups[grp]->GetTopScene(eUILayer_Fullscreen); + if (!pScene) pScene = m_groups[grp]->GetTopScene(eUILayer_Scene); + } + if (pScene && pScene->getMovie()) + { + Iggy *movie = pScene->getMovie(); + int rawMouseX = g_KBMInput.GetMouseX(); + int rawMouseY = g_KBMInput.GetMouseY(); + F32 mouseX = (F32)rawMouseX; + F32 mouseY = (F32)rawMouseY; + + extern HWND g_hWnd; + if (g_hWnd) + { + RECT rc; + GetClientRect(g_hWnd, &rc); + int winW = rc.right - rc.left; + int winH = rc.bottom - rc.top; + if (winW > 0 && winH > 0) + { + mouseX = mouseX * (m_fScreenWidth / (F32)winW); + mouseY = mouseY * (m_fScreenHeight / (F32)winH); + } + } + + // Only update hover focus when the mouse has actually moved, + // so that mouse-wheel scrolling can change list selection + // without the hover immediately snapping focus back. + bool mouseMoved = (rawMouseX != m_lastHoverMouseX || rawMouseY != m_lastHoverMouseY); + m_lastHoverMouseX = rawMouseX; + m_lastHoverMouseY = rawMouseY; + + if (mouseMoved) + { + IggyFocusHandle currentFocus = IGGY_FOCUS_NULL; + IggyFocusableObject focusables[64]; + S32 numFocusables = 0; + IggyPlayerGetFocusableObjects(movie, ¤tFocus, focusables, 64, &numFocusables); + + if (numFocusables > 0 && numFocusables <= 64) + { + IggyFocusHandle hitObject = IGGY_FOCUS_NULL; + for (S32 i = 0; i < numFocusables; ++i) + { + if (mouseX >= focusables[i].x0 && mouseX <= focusables[i].x1 && + mouseY >= focusables[i].y0 && mouseY <= focusables[i].y1) + { + hitObject = focusables[i].object; + break; + } + } + + if (hitObject != currentFocus) + { + IggyPlayerSetFocusRS(movie, hitObject, 0); + } + } + } + + // Convert mouse to scene/movie coordinates for slider hit testing + F32 sceneMouseX = mouseX; + F32 sceneMouseY = mouseY; + { + S32 displayWidth = 0, displayHeight = 0; + pScene->GetParentLayer()->getRenderDimensions(displayWidth, displayHeight); + if (displayWidth > 0 && displayHeight > 0) + { + sceneMouseX = mouseX * ((F32)pScene->getRenderWidth() / (F32)displayWidth); + sceneMouseY = mouseY * ((F32)pScene->getRenderHeight() / (F32)displayHeight); + } + } + + // Get main panel offset (controls are positioned relative to it) + S32 panelOffsetX = 0, panelOffsetY = 0; + UIControl *pMainPanel = pScene->GetMainPanel(); + if (pMainPanel) + { + pMainPanel->UpdateControl(); + panelOffsetX = pMainPanel->getXPos(); + panelOffsetY = pMainPanel->getYPos(); + } + + bool leftPressed = g_KBMInput.IsMouseButtonPressed(KeyboardMouseInput::MOUSE_LEFT); + bool leftDown = leftPressed || g_KBMInput.IsMouseButtonDown(KeyboardMouseInput::MOUSE_LEFT); + + if (m_mouseDraggingSliderScene != eUIScene_COUNT && m_mouseDraggingSliderScene != pScene->getSceneType()) + { + m_mouseDraggingSliderScene = eUIScene_COUNT; + m_mouseDraggingSliderId = -1; + } + + if (leftPressed) + { + vector *controls = pScene->GetControls(); + if (controls) + { + for (size_t i = 0; i < controls->size(); ++i) + { + UIControl *ctrl = (*controls)[i]; + if (!ctrl || ctrl->getControlType() != UIControl::eSlider || !ctrl->getVisible()) + continue; + + UIControl_Slider *pSlider = (UIControl_Slider *)ctrl; + pSlider->UpdateControl(); + S32 cx = pSlider->getXPos() + panelOffsetX; + S32 cy = pSlider->getYPos() + panelOffsetY; + S32 cw = pSlider->GetRealWidth(); + S32 ch = pSlider->getHeight(); + if (cw <= 0 || ch <= 0) + continue; + + if (sceneMouseX >= cx && sceneMouseX <= cx + cw && sceneMouseY >= cy && sceneMouseY <= cy + ch) + { + m_mouseDraggingSliderScene = pScene->getSceneType(); + m_mouseDraggingSliderId = pSlider->getId(); + break; + } + } + } + } + + if (leftDown && m_mouseDraggingSliderScene == pScene->getSceneType() && m_mouseDraggingSliderId >= 0) + { + UIControl_Slider *pSlider = FindSliderById(pScene, m_mouseDraggingSliderId); + if (pSlider && pSlider->getVisible()) + { + pSlider->UpdateControl(); + S32 sliderX = pSlider->getXPos() + panelOffsetX; + S32 sliderWidth = pSlider->GetRealWidth(); + if (sliderWidth > 0) + { + float fNewSliderPos = (sceneMouseX - (float)sliderX) / (float)sliderWidth; + if (fNewSliderPos < 0.0f) fNewSliderPos = 0.0f; + if (fNewSliderPos > 1.0f) fNewSliderPos = 1.0f; + pSlider->SetSliderTouchPos(fNewSliderPos); + } + } + else + { + m_mouseDraggingSliderScene = eUIScene_COUNT; + m_mouseDraggingSliderId = -1; + } + } + else if (!leftDown) + { + m_mouseDraggingSliderScene = eUIScene_COUNT; + m_mouseDraggingSliderId = -1; + } + } + } +#endif handleInput(); ++m_accumulatedTicks; } @@ -995,28 +1180,59 @@ void UIController::handleKeyPress(unsigned int iPad, unsigned int key) released = InputManager.ButtonReleased(iPad,key); // Toggle #ifdef _WINDOWS64 - // Keyboard menu input for player 0 if (iPad == 0) { - bool kbDown = false, kbPressed = false, kbReleased = false; - switch(key) + int vk = 0; + switch (key) { - case ACTION_MENU_UP: kbDown = KMInput.IsKeyDown(VK_UP); kbPressed = KMInput.IsKeyPressed(VK_UP); kbReleased = KMInput.IsKeyReleased(VK_UP); break; - case ACTION_MENU_DOWN: kbDown = KMInput.IsKeyDown(VK_DOWN); kbPressed = KMInput.IsKeyPressed(VK_DOWN); kbReleased = KMInput.IsKeyReleased(VK_DOWN); break; - case ACTION_MENU_LEFT: kbDown = KMInput.IsKeyDown(VK_LEFT); kbPressed = KMInput.IsKeyPressed(VK_LEFT); kbReleased = KMInput.IsKeyReleased(VK_LEFT); break; - case ACTION_MENU_RIGHT: kbDown = KMInput.IsKeyDown(VK_RIGHT); kbPressed = KMInput.IsKeyPressed(VK_RIGHT); kbReleased = KMInput.IsKeyReleased(VK_RIGHT); break; - case ACTION_MENU_OK: kbDown = KMInput.IsKeyDown(VK_RETURN); kbPressed = KMInput.IsKeyPressed(VK_RETURN); kbReleased = KMInput.IsKeyReleased(VK_RETURN); break; - case ACTION_MENU_A: kbDown = KMInput.IsKeyDown(VK_RETURN); kbPressed = KMInput.IsKeyPressed(VK_RETURN); kbReleased = KMInput.IsKeyReleased(VK_RETURN); break; - case ACTION_MENU_CANCEL: kbDown = KMInput.IsKeyDown(VK_ESCAPE); kbPressed = KMInput.IsKeyPressed(VK_ESCAPE); kbReleased = KMInput.IsKeyReleased(VK_ESCAPE); break; - case ACTION_MENU_B: kbDown = KMInput.IsKeyDown(VK_ESCAPE); kbPressed = KMInput.IsKeyPressed(VK_ESCAPE); kbReleased = KMInput.IsKeyReleased(VK_ESCAPE); break; - case ACTION_MENU_PAUSEMENU: kbDown = KMInput.IsKeyDown(VK_ESCAPE); kbPressed = KMInput.IsKeyPressed(VK_ESCAPE); kbReleased = KMInput.IsKeyReleased(VK_ESCAPE); break; - case ACTION_MENU_LEFT_SCROLL: kbDown = KMInput.IsKeyDown('Q'); kbPressed = KMInput.IsKeyPressed('Q'); kbReleased = KMInput.IsKeyReleased('Q'); break; - case ACTION_MENU_RIGHT_SCROLL: kbDown = KMInput.IsKeyDown('E'); kbPressed = KMInput.IsKeyPressed('E'); kbReleased = KMInput.IsKeyReleased('E'); break; - case ACTION_MENU_QUICK_MOVE: kbDown = KMInput.IsKeyDown(VK_SHIFT); kbPressed = KMInput.IsKeyPressed(VK_SHIFT); kbReleased = KMInput.IsKeyReleased(VK_SHIFT); break; + case ACTION_MENU_OK: case ACTION_MENU_A: vk = VK_RETURN; break; + case ACTION_MENU_CANCEL: case ACTION_MENU_B: vk = VK_ESCAPE; break; + case ACTION_MENU_UP: vk = VK_UP; break; + case ACTION_MENU_DOWN: vk = VK_DOWN; break; + case ACTION_MENU_LEFT: vk = VK_LEFT; break; + case ACTION_MENU_RIGHT: vk = VK_RIGHT; break; + case ACTION_MENU_X: vk = 'R'; break; + case ACTION_MENU_Y: vk = VK_TAB; break; + case ACTION_MENU_LEFT_SCROLL: vk = 'Q'; break; + case ACTION_MENU_RIGHT_SCROLL: vk = 'E'; break; + case ACTION_MENU_PAGEUP: vk = VK_PRIOR; break; + case ACTION_MENU_PAGEDOWN: vk = VK_NEXT; break; + } + if (vk != 0) + { + if (g_KBMInput.IsKeyPressed(vk)) { pressed = true; down = true; } + if (g_KBMInput.IsKeyReleased(vk)) { released = true; down = false; } + if (!pressed && !released && g_KBMInput.IsKeyDown(vk)) { down = true; } + } + + if ((key == ACTION_MENU_OK || key == ACTION_MENU_A) && !g_KBMInput.IsMouseGrabbed()) + { + if (m_mouseDraggingSliderId < 0) + { + if (g_KBMInput.IsMouseButtonPressed(KeyboardMouseInput::MOUSE_LEFT)) { pressed = true; down = true; } + if (g_KBMInput.IsMouseButtonReleased(KeyboardMouseInput::MOUSE_LEFT)) { released = true; down = false; } + if (!pressed && !released && g_KBMInput.IsMouseButtonDown(KeyboardMouseInput::MOUSE_LEFT)) { down = true; } + } + } + + // Scroll wheel for list scrolling — only consume the wheel value when the + // action key actually matches, so the other direction isn't lost. + if (!g_KBMInput.IsMouseGrabbed() && (key == ACTION_MENU_OTHER_STICK_UP || key == ACTION_MENU_OTHER_STICK_DOWN)) + { + int wheel = g_KBMInput.PeekMouseWheel(); + if (key == ACTION_MENU_OTHER_STICK_UP && wheel > 0) + { + g_KBMInput.ConsumeMouseWheel(); + pressed = true; + down = true; + } + else if (key == ACTION_MENU_OTHER_STICK_DOWN && wheel < 0) + { + g_KBMInput.ConsumeMouseWheel(); + pressed = true; + down = true; + } } - pressed = pressed || kbPressed; - released = released || kbReleased; - down = down || kbDown; } #endif @@ -3138,4 +3354,4 @@ void UIController::SendTouchInput(unsigned int iPad, unsigned int key, bool bPre } -#endif \ No newline at end of file +#endif diff --git a/Minecraft.Client/Common/UI/UIController.h b/Minecraft.Client/Common/UI/UIController.h index 49c78032..373d67b2 100644 --- a/Minecraft.Client/Common/UI/UIController.h +++ b/Minecraft.Client/Common/UI/UIController.h @@ -158,6 +158,10 @@ private: vector m_queuedMessageBoxData; unsigned int m_winUserIndex; + EUIScene m_mouseDraggingSliderScene; + int m_mouseDraggingSliderId; + int m_lastHoverMouseX; + int m_lastHoverMouseY; //bool m_bSysUIShowing; bool m_bSystemUIShowing; C4JThread *m_reloadSkinThread; diff --git a/Minecraft.Client/Common/UI/UIGroup.h b/Minecraft.Client/Common/UI/UIGroup.h index 0ffee0ca..28369f27 100644 --- a/Minecraft.Client/Common/UI/UIGroup.h +++ b/Minecraft.Client/Common/UI/UIGroup.h @@ -37,9 +37,7 @@ private: public: UIGroup(EUIGroup group, int iPad); -#ifdef __PSVITA__ EUIGroup GetGroup() {return m_group;} -#endif UIComponent_Tooltips *getTooltips() { return m_tooltips; } UIComponent_TutorialPopup *getTutorialPopup() { return m_tutorialPopup; } UIScene_HUD *getHUD() { return m_hud; } diff --git a/Minecraft.Client/Common/UI/UIScene.h b/Minecraft.Client/Common/UI/UIScene.h index 8c20aaae..b4008fa0 100644 --- a/Minecraft.Client/Common/UI/UIScene.h +++ b/Minecraft.Client/Common/UI/UIScene.h @@ -107,8 +107,10 @@ public: int getRenderHeight() { return m_renderHeight; } #ifdef __PSVITA__ - UILayer *GetParentLayer() {return m_parentLayer;} EUIGroup GetParentLayerGroup() {return m_parentLayer->m_parentGroup->GetGroup();} +#endif +#if defined(__PSVITA__) || defined(_WINDOWS64) + UILayer *GetParentLayer() {return m_parentLayer;} vector *GetControls() {return &m_controls;} #endif diff --git a/Minecraft.Client/Common/UI/UIScene_AbstractContainerMenu.cpp b/Minecraft.Client/Common/UI/UIScene_AbstractContainerMenu.cpp index bb56c780..6b196c1b 100644 --- a/Minecraft.Client/Common/UI/UIScene_AbstractContainerMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_AbstractContainerMenu.cpp @@ -28,17 +28,6 @@ UIScene_AbstractContainerMenu::UIScene_AbstractContainerMenu(int iPad, UILayer * ui.OverrideSFX(m_iPad,ACTION_MENU_DOWN,true); m_bIgnoreInput=false; -#ifdef _WINDOWS64 - m_bMouseDragSlider=false; - m_bHasMousePosition = false; - m_lastMouseX = 0; - m_lastMouseY = 0; - - for (int btn = 0; btn < 3; btn++) - { - KMInput.ConsumeMousePress(btn); - } -#endif } UIScene_AbstractContainerMenu::~UIScene_AbstractContainerMenu() @@ -49,6 +38,16 @@ UIScene_AbstractContainerMenu::~UIScene_AbstractContainerMenu() void UIScene_AbstractContainerMenu::handleDestroy() { app.DebugPrintf("UIScene_AbstractContainerMenu::handleDestroy\n"); + +#ifdef _WINDOWS64 + g_savedInventoryCursorPos.x = m_pointerPos.x; + g_savedInventoryCursorPos.y = m_pointerPos.y; + g_savedInventoryCursorPos.hasSavedPos = true; + + g_KBMInput.SetScreenCursorHidden(false); + g_KBMInput.SetCursorHiddenForUI(false); +#endif + Minecraft *pMinecraft = Minecraft::GetInstance(); if( pMinecraft->localgameModes[m_iPad] != NULL ) { @@ -84,6 +83,10 @@ void UIScene_AbstractContainerMenu::InitDataAssociations(int iPad, AbstractConta void UIScene_AbstractContainerMenu::PlatformInitialize(int iPad, int startIndex) { +#ifdef _WINDOWS64 + g_KBMInput.SetScreenCursorHidden(true); + g_KBMInput.SetCursorHiddenForUI(true); +#endif m_labelInventory.init( app.GetString(IDS_INVENTORY) ); @@ -168,6 +171,19 @@ void UIScene_AbstractContainerMenu::PlatformInitialize(int iPad, int startIndex) //m_pointerControl->SetPosition( &vPointerPos ); m_pointerPos = vPointerPos; +#ifdef _WINDOWS64 + if (g_savedInventoryCursorPos.hasSavedPos) + { + m_pointerPos.x = g_savedInventoryCursorPos.x; + m_pointerPos.y = g_savedInventoryCursorPos.y; + + if (m_pointerPos.x < m_fPointerMinX) m_pointerPos.x = m_fPointerMinX; + if (m_pointerPos.x > m_fPointerMaxX) m_pointerPos.x = m_fPointerMaxX; + if (m_pointerPos.y < m_fPointerMinY) m_pointerPos.y = m_fPointerMinY; + if (m_pointerPos.y > m_fPointerMaxY) m_pointerPos.y = m_fPointerMaxY; + } +#endif + IggyEvent mouseEvent; S32 width, height; m_parentLayer->getRenderDimensions(width, height); @@ -190,139 +206,15 @@ void UIScene_AbstractContainerMenu::tick() { UIScene::tick(); -#ifdef _WINDOWS64 - bool mouseActive = (m_iPad == 0 && !KMInput.IsCaptured()); - bool drivePointerFromMouse = false; - float rawMouseMovieX = 0, rawMouseMovieY = 0; - int scrollDelta = 0; - // Map Windows mouse position to the virtual pointer in movie coordinates - if (mouseActive) - { - RECT clientRect; - GetClientRect(KMInput.GetHWnd(), &clientRect); - int clientWidth = clientRect.right; - int clientHeight = clientRect.bottom; - if (clientWidth > 0 && clientHeight > 0) - { - int mouseX = KMInput.GetMouseX(); - int mouseY = KMInput.GetMouseY(); - bool mouseMoved = !m_bHasMousePosition || mouseX != m_lastMouseX || mouseY != m_lastMouseY; - - m_bHasMousePosition = true; - m_lastMouseX = mouseX; - m_lastMouseY = mouseY; - scrollDelta = KMInput.ConsumeScrollDelta(); - - // Convert mouse position to movie coordinates using the movie/client ratio - float mx = (float)mouseX * ((float)m_movieWidth / (float)clientWidth) - (float)m_controlMainPanel.getXPos(); - float my = (float)mouseY * ((float)m_movieHeight / (float)clientHeight) - (float)m_controlMainPanel.getYPos(); - - rawMouseMovieX = mx; - rawMouseMovieY = my; - - // Once the mouse has taken over the container cursor, keep following the OS cursor - // until explicit controller input takes ownership back. - drivePointerFromMouse = m_bPointerDrivenByMouse || mouseMoved || KMInput.IsMouseDown(0) || KMInput.IsMouseDown(1) || KMInput.IsMouseDown(2) || scrollDelta != 0; - if (drivePointerFromMouse) - { - m_bPointerDrivenByMouse = true; - m_eCurrTapState = eTapStateNoInput; - m_pointerPos.x = mx; - m_pointerPos.y = my; - } - } - } -#endif - onMouseTick(); -#ifdef _WINDOWS64 - // Dispatch mouse clicks AFTER onMouseTick() has updated m_eCurrSection from the new pointer position - if (mouseActive) - { - if (KMInput.ConsumeMousePress(0)) - { - if (m_eCurrSection == eSectionInventoryCreativeSlider) - { - // Scrollbar click: use raw mouse position (onMouseTick may have snapped m_pointerPos) - m_bMouseDragSlider = true; - m_pointerPos.x = rawMouseMovieX; - m_pointerPos.y = rawMouseMovieY; - handleOtherClicked(m_iPad, eSectionInventoryCreativeSlider, 0, false); - } - else - { - handleKeyDown(m_iPad, ACTION_MENU_A, false); - } - } - else if (m_bMouseDragSlider && KMInput.IsMouseDown(0)) - { - // Continue scrollbar drag: update scroll position from current mouse Y - m_pointerPos.x = rawMouseMovieX; - m_pointerPos.y = rawMouseMovieY; - handleOtherClicked(m_iPad, eSectionInventoryCreativeSlider, 0, false); - } - - if (!KMInput.IsMouseDown(0)) - m_bMouseDragSlider = false; - - if (KMInput.ConsumeMousePress(1)) - { - handleKeyDown(m_iPad, ACTION_MENU_X, false); - } - if (KMInput.ConsumeMousePress(2)) - { - handleKeyDown(m_iPad, ACTION_MENU_Y, false); - } - - // Mouse scroll wheel for page scrolling - if (scrollDelta > 0) - { - handleKeyDown(m_iPad, ACTION_MENU_OTHER_STICK_UP, false); - } - else if (scrollDelta < 0) - { - handleKeyDown(m_iPad, ACTION_MENU_OTHER_STICK_DOWN, false); - } - - // ESC to close — must be last since it may destroy this scene - if (KMInput.ConsumeKeyPress(VK_ESCAPE)) - { - handleKeyDown(m_iPad, ACTION_MENU_B, false); - return; - } - } -#endif - IggyEvent mouseEvent; S32 width, height; m_parentLayer->getRenderDimensions(width, height); -#ifdef _WINDOWS64 - S32 x, y; - if (mouseActive && m_bPointerDrivenByMouse) - { - // Send raw mouse position directly as Iggy event to avoid coordinate round-trip errors - // Scale mouse client coords to the Iggy display space (which was set to getRenderDimensions()) - RECT clientRect; - GetClientRect(KMInput.GetHWnd(), &clientRect); - float mouseMovieX = (float)KMInput.GetMouseX() * ((float)m_movieWidth / (float)clientRect.right); - float mouseMovieY = (float)KMInput.GetMouseY() * ((float)m_movieHeight / (float)clientRect.bottom); - float mouseLocalX = mouseMovieX - (float)m_controlMainPanel.getXPos(); - float mouseLocalY = mouseMovieY - (float)m_controlMainPanel.getYPos(); + S32 x = (S32)(m_pointerPos.x * ((float)width / m_movieWidth)); + S32 y = (S32)(m_pointerPos.y * ((float)height / m_movieHeight)); - x = (S32)(mouseLocalX * ((float)width / m_movieWidth)); - y = (S32)(mouseLocalY * ((float)height / m_movieHeight)); - } - else - { - x = (S32)(m_pointerPos.x * ((float)width / m_movieWidth)); - y = (S32)(m_pointerPos.y * ((float)height / m_movieHeight)); - } -#else - S32 x = m_pointerPos.x*((float)width/m_movieWidth); - S32 y = m_pointerPos.y*((float)height/m_movieHeight); -#endif IggyMakeEventMouseMove( &mouseEvent, x, y); // 4J Stu - This seems to be broken on Durango, so do it ourself diff --git a/Minecraft.Client/Common/UI/UIScene_AbstractContainerMenu.h b/Minecraft.Client/Common/UI/UIScene_AbstractContainerMenu.h index 812c5b41..605f5dbd 100644 --- a/Minecraft.Client/Common/UI/UIScene_AbstractContainerMenu.h +++ b/Minecraft.Client/Common/UI/UIScene_AbstractContainerMenu.h @@ -10,12 +10,6 @@ class UIScene_AbstractContainerMenu : public UIScene, public virtual IUIScene_Ab private: ESceneSection m_focusSection; bool m_bIgnoreInput; -#ifdef _WINDOWS64 - bool m_bMouseDragSlider; - bool m_bHasMousePosition; - int m_lastMouseX; - int m_lastMouseY; -#endif protected: UIControl m_controlMainPanel; @@ -42,6 +36,8 @@ public: virtual void handleDestroy(); int getPad() { return m_iPad; } + int getMovieWidth() { return m_movieWidth; } + int getMovieHeight() { return m_movieHeight; } bool getIgnoreInput() { return m_bIgnoreInput; } void setIgnoreInput(bool bVal) { m_bIgnoreInput=bVal; } diff --git a/Minecraft.Client/Common/UI/UIScene_LoadOrJoinMenu.cpp b/Minecraft.Client/Common/UI/UIScene_LoadOrJoinMenu.cpp index a5cb9aa2..d3cbe4c3 100644 --- a/Minecraft.Client/Common/UI/UIScene_LoadOrJoinMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_LoadOrJoinMenu.cpp @@ -1317,6 +1317,14 @@ void UIScene_LoadOrJoinMenu::handleInput(int iPad, int key, bool repeat, bool pr sendInputToMovie(key, repeat, pressed, released); handled = true; break; + case ACTION_MENU_OTHER_STICK_UP: + sendInputToMovie(ACTION_MENU_UP, repeat, pressed, released); + handled = true; + break; + case ACTION_MENU_OTHER_STICK_DOWN: + sendInputToMovie(ACTION_MENU_DOWN, repeat, pressed, released); + handled = true; + break; } } diff --git a/Minecraft.Client/Common/UI/UIScene_SettingsGraphicsMenu.cpp b/Minecraft.Client/Common/UI/UIScene_SettingsGraphicsMenu.cpp index 9bf3b983..0a76a5e5 100644 --- a/Minecraft.Client/Common/UI/UIScene_SettingsGraphicsMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_SettingsGraphicsMenu.cpp @@ -4,6 +4,33 @@ #include "..\..\Minecraft.h" #include "..\..\GameRenderer.h" +namespace +{ + const int FOV_MIN = 70; + const int FOV_MAX = 110; + const int FOV_SLIDER_MAX = 100; + + int clampFov(int value) + { + if (value < FOV_MIN) return FOV_MIN; + if (value > FOV_MAX) return FOV_MAX; + return value; + } + + int fovToSliderValue(float fov) + { + int clampedFov = clampFov((int)(fov + 0.5f)); + return ((clampedFov - FOV_MIN) * FOV_SLIDER_MAX) / (FOV_MAX - FOV_MIN); + } + + int sliderValueToFov(int sliderValue) + { + if (sliderValue < 0) sliderValue = 0; + if (sliderValue > FOV_SLIDER_MAX) sliderValue = FOV_SLIDER_MAX; + return FOV_MIN + ((sliderValue * (FOV_MAX - FOV_MIN)) / FOV_SLIDER_MAX); + } +} + UIScene_SettingsGraphicsMenu::UIScene_SettingsGraphicsMenu(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) { // Setup all the Iggy references we need for this scene @@ -22,8 +49,9 @@ UIScene_SettingsGraphicsMenu::UIScene_SettingsGraphicsMenu(int iPad, void *initD swprintf( (WCHAR *)TempString, 256, L"%ls: %d%%", app.GetString( IDS_SLIDER_GAMMA ),app.GetGameSettings(m_iPad,eGameSetting_Gamma)); m_sliderGamma.init(TempString,eControl_Gamma,0,100,app.GetGameSettings(m_iPad,eGameSetting_Gamma)); - swprintf((WCHAR*)TempString, 256, L"FOV: %d%%", (int)pMinecraft->gameRenderer->GetFovVal()); - m_sliderFOV.init(TempString, eControl_FOV, 70, 110, (int)pMinecraft->gameRenderer->GetFovVal()); + int initialFov = clampFov((int)(pMinecraft->gameRenderer->GetFovVal() + 0.5f)); + swprintf((WCHAR*)TempString, 256, L"FOV: %d", initialFov); + m_sliderFOV.init(TempString, eControl_FOV, 0, FOV_SLIDER_MAX, fovToSliderValue((float)initialFov)); swprintf( (WCHAR *)TempString, 256, L"%ls: %d%%", app.GetString( IDS_SLIDER_INTERFACEOPACITY ),app.GetGameSettings(m_iPad,eGameSetting_InterfaceOpacity)); m_sliderInterfaceOpacity.init(TempString,eControl_InterfaceOpacity,0,100,app.GetGameSettings(m_iPad,eGameSetting_InterfaceOpacity)); @@ -150,10 +178,12 @@ void UIScene_SettingsGraphicsMenu::handleSliderMove(F64 sliderId, F64 currentVal case eControl_FOV: { + m_sliderFOV.handleSliderMove(value); Minecraft* pMinecraft = Minecraft::GetInstance(); - pMinecraft->gameRenderer->SetFovVal((float)currentValue); + int fovValue = sliderValueToFov(value); + pMinecraft->gameRenderer->SetFovVal((float)fovValue); WCHAR TempString[256]; - swprintf((WCHAR*)TempString, 256, L"FOV: %d%%", (int)currentValue); + swprintf((WCHAR*)TempString, 256, L"FOV: %d", fovValue); m_sliderFOV.setLabel(TempString); } break; diff --git a/Minecraft.Client/Input.cpp b/Minecraft.Client/Input.cpp index 1639d09b..5e41da29 100644 --- a/Minecraft.Client/Input.cpp +++ b/Minecraft.Client/Input.cpp @@ -7,20 +7,21 @@ #include "Input.h" #include "..\Minecraft.Client\LocalPlayer.h" #include "Options.h" +#ifdef _WINDOWS64 +#include "Windows64\KeyboardMouseInput.h" +#endif Input::Input() { xa = 0; ya = 0; - sprintForward = 0; wasJumping = false; jumping = false; sneaking = false; - usingKeyboardMovement = false; + sprinting = false; lReset = false; rReset = false; - m_gamepadSneaking = false; } void Input::tick(LocalPlayer *player) @@ -32,43 +33,43 @@ void Input::tick(LocalPlayer *player) Minecraft *pMinecraft=Minecraft::GetInstance(); int iPad=player->GetXboxPad(); + float controllerXA = 0.0f; + float controllerYA = 0.0f; + // 4J-PB minecraft movement seems to be the wrong way round, so invert x! if( pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_LEFT) || pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_RIGHT) ) - xa = -InputManager.GetJoypadStick_LX(iPad); - else - xa = 0.0f; + controllerXA = -InputManager.GetJoypadStick_LX(iPad); if( pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_FORWARD) || pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_BACKWARD) ) - ya = InputManager.GetJoypadStick_LY(iPad); - else - ya = 0.0f; - sprintForward = ya; - usingKeyboardMovement = false; + controllerYA = InputManager.GetJoypadStick_LY(iPad); + float kbXA = 0.0f; + float kbYA = 0.0f; #ifdef _WINDOWS64 - // WASD movement (combine with gamepad) - if (iPad == 0 && KMInput.IsCaptured()) + if (iPad == 0 && g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive()) { - float kbX = 0.0f, kbY = 0.0f; - if (KMInput.IsKeyDown('W')) { kbY += 1.0f; sprintForward += 1.0f; usingKeyboardMovement = true; } - if (KMInput.IsKeyDown('S')) { kbY -= 1.0f; sprintForward -= 1.0f; usingKeyboardMovement = true; } - if (KMInput.IsKeyDown('A')) { kbX += 1.0f; usingKeyboardMovement = true; } // inverted like gamepad - if (KMInput.IsKeyDown('D')) { kbX -= 1.0f; usingKeyboardMovement = true; } - // Normalize diagonal - if (kbX != 0.0f && kbY != 0.0f) { kbX *= 0.707f; kbY *= 0.707f; } - if (pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_LEFT) || pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_RIGHT)) - xa = max(min(xa + kbX, 1.0f), -1.0f); - if (pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_FORWARD) || pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_BACKWARD)) - ya = max(min(ya + kbY, 1.0f), -1.0f); + if( pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_LEFT) || pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_RIGHT) ) + kbXA = g_KBMInput.GetMoveX(); + if( pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_FORWARD) || pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_BACKWARD) ) + kbYA = g_KBMInput.GetMoveY(); } #endif - sprintForward = max(min(sprintForward, 1.0f), -1.0f); + + if (kbXA != 0.0f || kbYA != 0.0f) + { + xa = kbXA; + ya = kbYA; + } + else + { + xa = controllerXA; + ya = controllerYA; + } #ifndef _CONTENT_PACKAGE if (app.GetFreezePlayers()) { xa = ya = 0.0f; - sprintForward = 0.0f; player->abilities.flying = true; } #endif @@ -80,7 +81,6 @@ void Input::tick(LocalPlayer *player) lReset = true; } xa = ya = 0.0f; - sprintForward = 0.0f; } // 4J: In flying mode, don't actually toggle sneaking (unless we're riding in which case we need to sneak to dismount) @@ -88,15 +88,46 @@ void Input::tick(LocalPlayer *player) { if((player->ullButtonsPressed&(1LL<localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_SNEAK_TOGGLE)) { - m_gamepadSneaking=!m_gamepadSneaking; + sneaking=!sneaking; } } - sneaking = m_gamepadSneaking; #ifdef _WINDOWS64 - // Keyboard hold-to-sneak (overrides gamepad toggle) - if (iPad == 0 && KMInput.IsCaptured() && KMInput.IsKeyDown(VK_SHIFT) && !player->abilities.flying) - sneaking = true; + if (iPad == 0 && g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive()) + { + // Left Shift = sneak (hold to crouch) + if (pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_SNEAK_TOGGLE)) + { + if (!player->abilities.flying) + { + sneaking = g_KBMInput.IsKeyDown(KeyboardMouseInput::KEY_SNEAK); + } + } + + // Left Ctrl + forward = sprint (hold to sprint) + if (!player->abilities.flying) + { + bool ctrlHeld = g_KBMInput.IsKeyDown(KeyboardMouseInput::KEY_SPRINT); + bool movingForward = (kbYA > 0.0f); + + if (ctrlHeld && movingForward) + { + sprinting = true; + } + else + { + sprinting = false; + } + } + else + { + sprinting = false; + } + } + else if (iPad == 0) + { + sprinting = false; + } #endif if(sneaking) @@ -109,6 +140,7 @@ void Input::tick(LocalPlayer *player) float tx = 0.0f; float ty = 0.0f; + if( pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_LOOK_LEFT) || pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_LOOK_RIGHT) ) tx = InputManager.GetJoypadStick_RX(iPad)*(((float)app.GetGameSettings(iPad,eGameSetting_Sensitivity_InGame))/100.0f); // apply sensitivity to look if( pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_LOOK_UP) || pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_LOOK_DOWN) ) @@ -132,47 +164,52 @@ void Input::tick(LocalPlayer *player) } tx = ty = 0.0f; } - player->interpolateTurn(tx * abs(tx) * turnSpeed, ty * abs(ty) * turnSpeed); + + float turnX = tx * abs(tx) * turnSpeed; + float turnY = ty * abs(ty) * turnSpeed; #ifdef _WINDOWS64 - // Mouse look is now handled per-frame in Minecraft::applyFrameMouseLook() - // to eliminate the 20Hz tick delay. Only flush any remaining delta here - // as a safety measure. - if (iPad == 0 && KMInput.IsCaptured()) + if (iPad == 0 && g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive()) { - float rawDx, rawDy; - KMInput.ConsumeMouseDelta(rawDx, rawDy); - // Delta should normally be 0 since applyFrameMouseLook() already consumed it - if (rawDx != 0.0f || rawDy != 0.0f) + float mouseSensitivity = ((float)app.GetGameSettings(iPad,eGameSetting_Sensitivity_InGame)) / 100.0f; + float mouseLookScale = 5.0f; + float mx = g_KBMInput.GetLookX(mouseSensitivity * mouseLookScale); + float my = g_KBMInput.GetLookY(mouseSensitivity * mouseLookScale); + + if ( app.GetGameSettings(iPad,eGameSetting_ControlInvertLook) ) { - float mouseSensitivity = ((float)app.GetGameSettings(iPad, eGameSetting_Sensitivity_InGame)) / 100.0f; - float mdx = rawDx * mouseSensitivity; - float mdy = -rawDy * mouseSensitivity; - if (app.GetGameSettings(iPad, eGameSetting_ControlInvertLook)) - mdy = -mdy; - player->interpolateTurn(mdx, mdy); + my = -my; } + + turnX += mx; + turnY += my; } #endif + player->interpolateTurn(turnX, turnY); + //jumping = controller.isButtonPressed(0); - unsigned int jump = InputManager.GetValue(iPad, MINECRAFT_ACTION_JUMP); - if( jump > 0 && pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_JUMP) ) + bool kbJump = false; +#ifdef _WINDOWS64 + kbJump = (iPad == 0) && g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive() && g_KBMInput.IsKeyDown(KeyboardMouseInput::KEY_JUMP); +#endif + if( (jump > 0 || kbJump) && pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_JUMP) ) jumping = true; else jumping = false; -#ifdef _WINDOWS64 - // Keyboard jump (Space) - if (iPad == 0 && KMInput.IsCaptured() && KMInput.IsKeyDown(VK_SPACE) && pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_JUMP)) - jumping = true; -#endif - #ifndef _CONTENT_PACKAGE if (app.GetFreezePlayers()) jumping = false; #endif +#ifdef _WINDOWS64 + if (iPad == 0 && g_KBMInput.IsKeyPressed(VK_ESCAPE) && g_KBMInput.IsMouseGrabbed()) + { + g_KBMInput.SetMouseGrabbed(false); + } +#endif + //OutputDebugString("INPUT: End input tick\n"); } diff --git a/Minecraft.Client/Input.h b/Minecraft.Client/Input.h index d8dedd57..c7e1eec4 100644 --- a/Minecraft.Client/Input.h +++ b/Minecraft.Client/Input.h @@ -6,20 +6,17 @@ class Input public: float xa; float ya; - float sprintForward; bool wasJumping; bool jumping; bool sneaking; - bool usingKeyboardMovement; - - Input(); // 4J - added + bool sprinting; + + Input(); virtual void tick(LocalPlayer *player); private: - bool lReset; bool rReset; - bool m_gamepadSneaking; }; diff --git a/Minecraft.Client/LocalPlayer.cpp b/Minecraft.Client/LocalPlayer.cpp index bb25105f..77306621 100644 --- a/Minecraft.Client/LocalPlayer.cpp +++ b/Minecraft.Client/LocalPlayer.cpp @@ -251,13 +251,10 @@ void LocalPlayer::aiStep() if (changingDimensionDelay > 0) changingDimensionDelay--; bool wasJumping = input->jumping; float runTreshold = 0.8f; - float sprintForward = input->sprintForward; - - bool wasRunning = sprintForward >= runTreshold; + bool wasRunning = input->ya >= runTreshold; //input->tick( dynamic_pointer_cast( shared_from_this() ) ); // 4J-PB - make it a localplayer input->tick( this ); - sprintForward = input->sprintForward; if (isUsingItem() && !isRiding()) { input->xa *= 0.2f; @@ -281,25 +278,9 @@ void LocalPlayer::aiStep() // world with low food, then reload it in creative. if(abilities.mayfly || isAllowedToFly() ) enoughFoodToSprint = true; - bool forwardEnoughToTriggerSprint = sprintForward >= runTreshold; - bool forwardReturnedToDeadzone = sprintForward == 0.0f; - bool forwardEnoughToContinueSprint = sprintForward >= runTreshold; - -#ifdef _WINDOWS64 - if (GetXboxPad() == 0 && input->usingKeyboardMovement) - { - forwardEnoughToContinueSprint = sprintForward > 0.0f; - } -#endif - -#ifdef _WINDOWS64 - // Keyboard sprint: Ctrl held while moving forward - if (GetXboxPad() == 0 && input->usingKeyboardMovement && KMInput.IsKeyDown(VK_CONTROL) && sprintForward > 0.0f && - enoughFoodToSprint && !isUsingItem() && !hasEffect(MobEffect::blindness) && onGround) - { - if (!isSprinting()) setSprinting(true); - } -#endif + bool forwardEnoughToTriggerSprint = input->ya >= runTreshold; + bool forwardReturnedToDeadzone = input->ya == 0.0f; + bool forwardEnoughToContinueSprint = input->ya >= runTreshold; // 4J - altered this slightly to make sure that the joypad returns to below returnTreshold in between registering two movements up to runThreshold if (onGround && !isSprinting() && enoughFoodToSprint && !isUsingItem() && !hasEffect(MobEffect::blindness)) @@ -327,6 +308,12 @@ void LocalPlayer::aiStep() } } if (isSneaking()) sprintTriggerTime = 0; +#ifdef _WINDOWS64 + if (input->sprinting && onGround && enoughFoodToSprint && !isUsingItem() && !hasEffect(MobEffect::blindness) && !isSneaking()) + { + setSprinting(true); + } +#endif // 4J-PB - try not stopping sprint on collision //if (isSprinting() && (input->ya < runTreshold || horizontalCollision || !enoughFoodToSprint)) if (isSprinting() && (!forwardEnoughToContinueSprint || !enoughFoodToSprint || isSneaking() || isUsingItem())) diff --git a/Minecraft.Client/Minecraft.cpp b/Minecraft.Client/Minecraft.cpp index bd75a61a..19ee79bd 100644 --- a/Minecraft.Client/Minecraft.cpp +++ b/Minecraft.Client/Minecraft.cpp @@ -502,6 +502,13 @@ void Minecraft::setScreen(Screen *screen) this->screen->removed(); } +#ifdef _WINDOWS64 + if (screen != NULL && g_KBMInput.IsMouseGrabbed()) + { + g_KBMInput.SetMouseGrabbed(false); + } +#endif + //4J Gordon: Do not force a stats save here /*if (dynamic_cast(screen)!=NULL) { @@ -1184,11 +1191,11 @@ void Minecraft::applyFrameMouseLook() int iPad = localplayers[i]->GetXboxPad(); if (iPad != 0) continue; // Mouse only applies to pad 0 - if (!KMInput.IsCaptured()) continue; + if (!g_KBMInput.IsMouseGrabbed()) continue; if (localgameModes[iPad] == NULL) continue; float rawDx, rawDy; - KMInput.ConsumeMouseDelta(rawDx, rawDy); + g_KBMInput.ConsumeMouseDelta(rawDx, rawDy); if (rawDx == 0.0f && rawDy == 0.0f) continue; float mouseSensitivity = ((float)app.GetGameSettings(iPad, eGameSetting_Sensitivity_InGame)) / 100.0f; @@ -1450,14 +1457,54 @@ void Minecraft::run_middle() // Keyboard/mouse button presses for player 0 if (i == 0) { - if (KMInput.ConsumeKeyPress(VK_ESCAPE)) localplayers[i]->ullButtonsPressed |= 1LL<ullButtonsPressed |= 1LL<ullButtonsPressed |= 1LL<ullButtonsPressed |= 1LL<ullButtonsPressed |= 1LL<ullButtonsPressed|=1LL<ullButtonsPressed|=1LL<ullButtonsPressed|=1LL<ullButtonsPressed|=1LL<ullButtonsPressed|=1LL<inventory) + localplayers[i]->inventory->selected = slot; + } + } + } + + // Utility keys always work regardless of KBM active state + if(g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_PAUSE) && !ui.IsTutorialVisible(i)) + { + localplayers[i]->ullButtonsPressed|=1LL<ullButtonsPressed|=1LL<ullButtonsPressed|=1LL<abilities.flying && KMInput.IsKeyDown(VK_SHIFT) && !ui.GetMenuDisplayed(i)) - localplayers[i]->ullButtonsPressed |= 1LL<abilities.flying && !ui.GetMenuDisplayed(i)) + localplayers[i]->ullButtonsPressed|=1LL< 0 && gameMode->isInputAllowed(MINECRAFT_ACTION_LEFT_SCROLL)) wheel += 1; - else if (kbWheel < 0 && gameMode->isInputAllowed(MINECRAFT_ACTION_RIGHT_SCROLL)) wheel -= 1; - - // 1-9 keys for direct hotbar selection - if (gameMode->isInputAllowed(MINECRAFT_ACTION_LEFT_SCROLL)) - { - for (int k = '1'; k <= '9'; k++) - { - if (KMInput.ConsumeKeyPress(k)) - { - player->inventory->selected = k - '1'; - app.SetOpacityTimer(iPad); - break; - } - } - } + wheel = g_KBMInput.GetMouseWheel(); } #endif if (wheel != 0) @@ -3485,33 +3528,20 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures) player->handleMouseClick(0); player->lastClickTick[0] = ticks; } -#ifdef _WINDOWS64 - else if (iPad == 0 && KMInput.IsCaptured() && KMInput.ConsumeMousePress(0)) - { - player->handleMouseClick(0); - player->lastClickTick[0] = ticks; - } -#endif - if (InputManager.ButtonDown(iPad, MINECRAFT_ACTION_ACTION) && ticks - player->lastClickTick[0] >= timer->ticksPerSecond / 4) +#ifdef _WINDOWS64 + bool actionHeld = InputManager.ButtonDown(iPad, MINECRAFT_ACTION_ACTION) || (iPad == 0 && g_KBMInput.IsKBMActive() && g_KBMInput.IsMouseButtonDown(KeyboardMouseInput::MOUSE_LEFT)); +#else + bool actionHeld = InputManager.ButtonDown(iPad, MINECRAFT_ACTION_ACTION); +#endif + if (actionHeld && ticks - player->lastClickTick[0] >= timer->ticksPerSecond / 4) { //printf("MINECRAFT_ACTION_ACTION ButtonDown"); player->handleMouseClick(0); player->lastClickTick[0] = ticks; } -#ifdef _WINDOWS64 - else if (iPad == 0 && KMInput.IsCaptured() && KMInput.IsMouseDown(0) && ticks - player->lastClickTick[0] >= timer->ticksPerSecond / 4) - { - player->handleMouseClick(0); - player->lastClickTick[0] = ticks; - } -#endif - if(InputManager.ButtonDown(iPad, MINECRAFT_ACTION_ACTION) -#ifdef _WINDOWS64 - || (iPad == 0 && KMInput.IsCaptured() && KMInput.IsMouseDown(0)) -#endif - ) + if(actionHeld) { player->handleMouseDown(0, true ); } @@ -3530,25 +3560,21 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures) lastClickTick = ticks; } */ +#ifdef _WINDOWS64 + bool useHeld = InputManager.ButtonDown(iPad, MINECRAFT_ACTION_USE) || (iPad == 0 && g_KBMInput.IsKBMActive() && g_KBMInput.IsMouseButtonDown(KeyboardMouseInput::MOUSE_RIGHT)); +#else + bool useHeld = InputManager.ButtonDown(iPad, MINECRAFT_ACTION_USE); +#endif if( player->isUsingItem() ) { - if(!InputManager.ButtonDown(iPad, MINECRAFT_ACTION_USE) -#ifdef _WINDOWS64 - && !(iPad == 0 && KMInput.IsCaptured() && KMInput.IsMouseDown(1)) -#endif - ) gameMode->releaseUsingItem(player); + if(!useHeld) gameMode->releaseUsingItem(player); } else if( gameMode->isInputAllowed(MINECRAFT_ACTION_USE) ) { -#ifdef _WINDOWS64 - bool useButtonDown = InputManager.ButtonDown(iPad, MINECRAFT_ACTION_USE) || (iPad == 0 && KMInput.IsCaptured() && KMInput.IsMouseDown(1)); -#else - bool useButtonDown = InputManager.ButtonDown(iPad, MINECRAFT_ACTION_USE); -#endif if( player->abilities.instabuild ) { // 4J - attempt to handle click in special creative mode fashion if possible (used for placing blocks at regular intervals) - bool didClick = player->creativeModeHandleMouseClick(1, useButtonDown ); + bool didClick = player->creativeModeHandleMouseClick(1, useHeld ); // If this handler has put us in lastClick_oldRepeat mode then it is because we aren't placing blocks - behave largely as the code used to if( player->lastClickState == LocalPlayer::lastClick_oldRepeat ) { @@ -3560,7 +3586,7 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures) else { // Otherwise just the original game code for handling autorepeat - if (useButtonDown && ticks - player->lastClickTick[1] >= timer->ticksPerSecond / 4) + if (useHeld && ticks - player->lastClickTick[1] >= timer->ticksPerSecond / 4) { player->handleMouseClick(1); player->lastClickTick[1] = ticks; @@ -3576,7 +3602,7 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures) bool firstClick = ( player->lastClickTick[1] == 0 ); bool autoRepeat = ticks - player->lastClickTick[1] >= timer->ticksPerSecond / 4; if ( player->isRiding() || player->isSprinting() || player->isSleeping() ) autoRepeat = false; - if (useButtonDown ) + if (useHeld ) { // If the player has just exited a bed, then delay the time before a repeat key is allowed without releasing if(player->isSleeping() ) player->lastClickTick[1] = ticks + (timer->ticksPerSecond * 2); diff --git a/Minecraft.Client/Screen.cpp b/Minecraft.Client/Screen.cpp index 6a402b04..e69b7373 100644 --- a/Minecraft.Client/Screen.cpp +++ b/Minecraft.Client/Screen.cpp @@ -110,13 +110,13 @@ void Screen::updateEvents() // Poll mouse button state and dispatch click/release events for (int btn = 0; btn < 3; btn++) { - if (KMInput.ConsumeMousePress(btn)) + if (g_KBMInput.IsMouseButtonPressed(btn)) { int xm = Mouse::getX() * width / minecraft->width; int ym = height - Mouse::getY() * height / minecraft->height - 1; mouseClicked(xm, ym, btn); } - if (KMInput.ConsumeMouseRelease(btn)) + if (g_KBMInput.IsMouseButtonReleased(btn)) { int xm = Mouse::getX() * width / minecraft->width; int ym = height - Mouse::getY() * height / minecraft->height - 1; @@ -127,7 +127,7 @@ void Screen::updateEvents() // Poll keyboard events for (int vk = 0; vk < 256; vk++) { - if (KMInput.ConsumeKeyPress(vk)) + if (g_KBMInput.IsKeyPressed(vk)) { // Map Windows virtual key to the Keyboard constants used by Screen::keyPressed int mappedKey = -1; @@ -144,7 +144,7 @@ void Screen::updateEvents() else if (vk >= 'A' && vk <= 'Z') { ch = (wchar_t)(vk - 'A' + L'a'); - if (KMInput.IsKeyDown(VK_SHIFT)) ch = (wchar_t)vk; + if (g_KBMInput.IsKeyDown(VK_LSHIFT) || g_KBMInput.IsKeyDown(VK_RSHIFT)) ch = (wchar_t)vk; } else if (vk >= '0' && vk <= '9') ch = (wchar_t)vk; else if (vk == VK_SPACE) ch = L' '; diff --git a/Minecraft.Client/Windows64/KeyboardMouseInput.cpp b/Minecraft.Client/Windows64/KeyboardMouseInput.cpp index df57db44..7ab99a71 100644 --- a/Minecraft.Client/Windows64/KeyboardMouseInput.cpp +++ b/Minecraft.Client/Windows64/KeyboardMouseInput.cpp @@ -3,118 +3,158 @@ #ifdef _WINDOWS64 #include "KeyboardMouseInput.h" +#include -KeyboardMouseInput KMInput; +KeyboardMouseInput g_KBMInput; -KeyboardMouseInput::KeyboardMouseInput() - : m_mouseDeltaXAccum(0.0f) - , m_mouseDeltaYAccum(0.0f) - , m_scrollDeltaAccum(0) - , m_captured(false) - , m_hWnd(NULL) - , m_initialized(false) - , m_mouseX(0) - , m_mouseY(0) +extern HWND g_hWnd; + +// Forward declaration +static void ClipCursorToWindow(HWND hWnd); + +void KeyboardMouseInput::Init() { - memset(m_keyState, 0, sizeof(m_keyState)); - memset(m_keyStatePrev, 0, sizeof(m_keyStatePrev)); - memset(m_mouseButtons, 0, sizeof(m_mouseButtons)); - memset(m_mouseButtonsPrev, 0, sizeof(m_mouseButtonsPrev)); + memset(m_keyDown, 0, sizeof(m_keyDown)); + memset(m_keyDownPrev, 0, sizeof(m_keyDownPrev)); memset(m_keyPressedAccum, 0, sizeof(m_keyPressedAccum)); - memset(m_mousePressedAccum, 0, sizeof(m_mousePressedAccum)); - memset(m_mouseReleasedAccum, 0, sizeof(m_mouseReleasedAccum)); -} + memset(m_keyReleasedAccum, 0, sizeof(m_keyReleasedAccum)); + memset(m_keyPressed, 0, sizeof(m_keyPressed)); + memset(m_keyReleased, 0, sizeof(m_keyReleased)); + memset(m_mouseButtonDown, 0, sizeof(m_mouseButtonDown)); + memset(m_mouseButtonDownPrev, 0, sizeof(m_mouseButtonDownPrev)); + memset(m_mouseBtnPressedAccum, 0, sizeof(m_mouseBtnPressedAccum)); + memset(m_mouseBtnReleasedAccum, 0, sizeof(m_mouseBtnReleasedAccum)); + memset(m_mouseBtnPressed, 0, sizeof(m_mouseBtnPressed)); + memset(m_mouseBtnReleased, 0, sizeof(m_mouseBtnReleased)); + m_mouseX = 0; + m_mouseY = 0; + m_mouseDeltaX = 0; + m_mouseDeltaY = 0; + m_mouseDeltaAccumX = 0; + m_mouseDeltaAccumY = 0; + m_mouseWheelAccum = 0; + m_mouseGrabbed = false; + m_cursorHiddenForUI = false; + m_windowFocused = true; + m_hasInput = false; + m_kbmActive = true; + m_screenWantsCursorHidden = false; -KeyboardMouseInput::~KeyboardMouseInput() -{ - if (m_captured) - { - SetCapture(false); - } -} - -void KeyboardMouseInput::Init(HWND hWnd) -{ - m_hWnd = hWnd; - m_initialized = true; - - // Register for raw mouse input RAWINPUTDEVICE rid; - rid.usUsagePage = HID_USAGE_PAGE_GENERIC; - rid.usUsage = HID_USAGE_GENERIC_MOUSE; + rid.usUsagePage = 0x01; // HID_USAGE_PAGE_GENERIC + rid.usUsage = 0x02; // HID_USAGE_GENERIC_MOUSE rid.dwFlags = 0; - rid.hwndTarget = hWnd; + rid.hwndTarget = g_hWnd; RegisterRawInputDevices(&rid, 1, sizeof(rid)); } +void KeyboardMouseInput::ClearAllState() +{ + memset(m_keyDown, 0, sizeof(m_keyDown)); + memset(m_keyDownPrev, 0, sizeof(m_keyDownPrev)); + memset(m_keyPressedAccum, 0, sizeof(m_keyPressedAccum)); + memset(m_keyReleasedAccum, 0, sizeof(m_keyReleasedAccum)); + memset(m_keyPressed, 0, sizeof(m_keyPressed)); + memset(m_keyReleased, 0, sizeof(m_keyReleased)); + memset(m_mouseButtonDown, 0, sizeof(m_mouseButtonDown)); + memset(m_mouseButtonDownPrev, 0, sizeof(m_mouseButtonDownPrev)); + memset(m_mouseBtnPressedAccum, 0, sizeof(m_mouseBtnPressedAccum)); + memset(m_mouseBtnReleasedAccum, 0, sizeof(m_mouseBtnReleasedAccum)); + memset(m_mouseBtnPressed, 0, sizeof(m_mouseBtnPressed)); + memset(m_mouseBtnReleased, 0, sizeof(m_mouseBtnReleased)); + m_mouseDeltaX = 0; + m_mouseDeltaY = 0; + m_mouseDeltaAccumX = 0; + m_mouseDeltaAccumY = 0; + m_mouseWheelAccum = 0; +} + void KeyboardMouseInput::Tick() { - // Keep cursor pinned to center while captured - if (m_captured) - CenterCursor(); -} + memcpy(m_keyDownPrev, m_keyDown, sizeof(m_keyDown)); + memcpy(m_mouseButtonDownPrev, m_mouseButtonDown, sizeof(m_mouseButtonDown)); -void KeyboardMouseInput::EndFrame() -{ - // Advance previous state for next frame's edge detection. - // Must be called AFTER all per-frame consumers have read IsKeyPressed/Released etc. - memcpy(m_keyStatePrev, m_keyState, sizeof(m_keyState)); - memcpy(m_mouseButtonsPrev, m_mouseButtons, sizeof(m_mouseButtons)); -} + memcpy(m_keyPressed, m_keyPressedAccum, sizeof(m_keyPressedAccum)); + memcpy(m_keyReleased, m_keyReleasedAccum, sizeof(m_keyReleasedAccum)); + memset(m_keyPressedAccum, 0, sizeof(m_keyPressedAccum)); + memset(m_keyReleasedAccum, 0, sizeof(m_keyReleasedAccum)); -void KeyboardMouseInput::OnKeyDown(WPARAM vk) -{ - if (vk < 256) + memcpy(m_mouseBtnPressed, m_mouseBtnPressedAccum, sizeof(m_mouseBtnPressedAccum)); + memcpy(m_mouseBtnReleased, m_mouseBtnReleasedAccum, sizeof(m_mouseBtnReleasedAccum)); + memset(m_mouseBtnPressedAccum, 0, sizeof(m_mouseBtnPressedAccum)); + memset(m_mouseBtnReleasedAccum, 0, sizeof(m_mouseBtnReleasedAccum)); + + m_mouseDeltaX = m_mouseDeltaAccumX; + m_mouseDeltaY = m_mouseDeltaAccumY; + m_mouseDeltaAccumX = 0; + m_mouseDeltaAccumY = 0; + + m_hasInput = (m_mouseDeltaX != 0 || m_mouseDeltaY != 0 || m_mouseWheelAccum != 0); + if (!m_hasInput) { - if (!m_keyState[vk]) m_keyPressedAccum[vk] = true; - m_keyState[vk] = true; - } -} - -void KeyboardMouseInput::OnKeyUp(WPARAM vk) -{ - if (vk < 256) - { - m_keyState[vk] = false; - } -} - -void KeyboardMouseInput::OnRawMouseInput(LPARAM lParam) -{ - if (!m_captured) return; - - UINT dwSize = 0; - GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER)); - - BYTE* lpb = (BYTE*)alloca(dwSize); - if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER)) != dwSize) - return; - - RAWINPUT* raw = (RAWINPUT*)lpb; - if (raw->header.dwType == RIM_TYPEMOUSE) - { - if (raw->data.mouse.usFlags == MOUSE_MOVE_RELATIVE) + for (int i = 0; i < MAX_KEYS; i++) { - m_mouseDeltaXAccum += (float)raw->data.mouse.lLastX; - m_mouseDeltaYAccum += (float)raw->data.mouse.lLastY; + if (m_keyDown[i]) { m_hasInput = true; break; } } } -} - -void KeyboardMouseInput::OnMouseButton(int button, bool down) -{ - if (ui.IsPauseMenuDisplayed(ProfileManager.GetPrimaryPad())) { return; } - if (button >= 0 && button < 3) + if (!m_hasInput) { - if (down && !m_mouseButtons[button]) m_mousePressedAccum[button] = true; - if (!down && m_mouseButtons[button]) m_mouseReleasedAccum[button] = true; - m_mouseButtons[button] = down; + for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) + { + if (m_mouseButtonDown[i]) { m_hasInput = true; break; } + } + } + + if ((m_mouseGrabbed || m_cursorHiddenForUI) && g_hWnd) + { + RECT rc; + GetClientRect(g_hWnd, &rc); + POINT center; + center.x = (rc.right - rc.left) / 2; + center.y = (rc.bottom - rc.top) / 2; + ClientToScreen(g_hWnd, ¢er); + SetCursorPos(center.x, center.y); } } -void KeyboardMouseInput::OnMouseWheel(int delta) +void KeyboardMouseInput::OnKeyDown(int vkCode) { - m_scrollDeltaAccum += delta; + if (vkCode >= 0 && vkCode < MAX_KEYS) + { + if (!m_keyDown[vkCode]) + m_keyPressedAccum[vkCode] = true; + m_keyDown[vkCode] = true; + } +} + +void KeyboardMouseInput::OnKeyUp(int vkCode) +{ + if (vkCode >= 0 && vkCode < MAX_KEYS) + { + if (m_keyDown[vkCode]) + m_keyReleasedAccum[vkCode] = true; + m_keyDown[vkCode] = false; + } +} + +void KeyboardMouseInput::OnMouseButtonDown(int button) +{ + if (button >= 0 && button < MAX_MOUSE_BUTTONS) + { + if (!m_mouseButtonDown[button]) + m_mouseBtnPressedAccum[button] = true; + m_mouseButtonDown[button] = true; + } +} + +void KeyboardMouseInput::OnMouseButtonUp(int button) +{ + if (button >= 0 && button < MAX_MOUSE_BUTTONS) + { + if (m_mouseButtonDown[button]) + m_mouseBtnReleasedAccum[button] = true; + m_mouseButtonDown[button] = false; + } } void KeyboardMouseInput::OnMouseMove(int x, int y) @@ -123,139 +163,193 @@ void KeyboardMouseInput::OnMouseMove(int x, int y) m_mouseY = y; } -int KeyboardMouseInput::GetMouseX() const { return m_mouseX; } -int KeyboardMouseInput::GetMouseY() const { return m_mouseY; } -HWND KeyboardMouseInput::GetHWnd() const { return m_hWnd; } - -void KeyboardMouseInput::ClearAllState() +void KeyboardMouseInput::OnMouseWheel(int delta) { - memset(m_keyState, 0, sizeof(m_keyState)); - memset(m_mouseButtons, 0, sizeof(m_mouseButtons)); - memset(m_keyPressedAccum, 0, sizeof(m_keyPressedAccum)); - memset(m_mousePressedAccum, 0, sizeof(m_mousePressedAccum)); - memset(m_mouseReleasedAccum, 0, sizeof(m_mouseReleasedAccum)); - m_mouseDeltaXAccum = 0.0f; - m_mouseDeltaYAccum = 0.0f; - m_scrollDeltaAccum = 0; + // Normalize from raw Windows delta (multiples of WHEEL_DELTA=120) to discrete notch counts + m_mouseWheelAccum += delta / WHEEL_DELTA; } -// Per-frame key queries -bool KeyboardMouseInput::IsKeyDown(int vk) const +int KeyboardMouseInput::GetMouseWheel() { - if (vk < 0 || vk >= 256) return false; - return m_keyState[vk]; + int val = m_mouseWheelAccum; + m_mouseWheelAccum = 0; + return val; } -bool KeyboardMouseInput::IsKeyPressed(int vk) const +void KeyboardMouseInput::OnRawMouseDelta(int dx, int dy) { - if (vk < 0 || vk >= 256) return false; - return m_keyState[vk] && !m_keyStatePrev[vk]; + m_mouseDeltaAccumX += dx; + m_mouseDeltaAccumY += dy; } -bool KeyboardMouseInput::IsKeyReleased(int vk) const +bool KeyboardMouseInput::IsKeyDown(int vkCode) const { - if (vk < 0 || vk >= 256) return false; - return !m_keyState[vk] && m_keyStatePrev[vk]; + if (vkCode >= 0 && vkCode < MAX_KEYS) + return m_keyDown[vkCode]; + return false; } -// Per-frame mouse button queries -bool KeyboardMouseInput::IsMouseDown(int btn) const +bool KeyboardMouseInput::IsKeyPressed(int vkCode) const { - if (btn < 0 || btn >= 3) return false; - return m_mouseButtons[btn]; + if (vkCode >= 0 && vkCode < MAX_KEYS) + return m_keyPressed[vkCode]; + return false; } -bool KeyboardMouseInput::IsMousePressed(int btn) const +bool KeyboardMouseInput::IsKeyReleased(int vkCode) const { - if (btn < 0 || btn >= 3) return false; - return m_mouseButtons[btn] && !m_mouseButtonsPrev[btn]; + if (vkCode >= 0 && vkCode < MAX_KEYS) + return m_keyReleased[vkCode]; + return false; } -bool KeyboardMouseInput::IsMouseReleased(int btn) const +bool KeyboardMouseInput::IsMouseButtonDown(int button) const { - if (btn < 0 || btn >= 3) return false; - return !m_mouseButtons[btn] && m_mouseButtonsPrev[btn]; + if (button >= 0 && button < MAX_MOUSE_BUTTONS) + return m_mouseButtonDown[button]; + return false; } -// Game-tick consume methods -bool KeyboardMouseInput::ConsumeKeyPress(int vk) +bool KeyboardMouseInput::IsMouseButtonPressed(int button) const { - if (vk < 0 || vk >= 256) return false; - bool pressed = m_keyPressedAccum[vk]; - m_keyPressedAccum[vk] = false; - return pressed; + if (button >= 0 && button < MAX_MOUSE_BUTTONS) + return m_mouseBtnPressed[button]; + return false; } -bool KeyboardMouseInput::ConsumeMousePress(int btn) +bool KeyboardMouseInput::IsMouseButtonReleased(int button) const { - if (btn < 0 || btn >= 3) return false; - bool pressed = m_mousePressedAccum[btn]; - m_mousePressedAccum[btn] = false; - return pressed; -} - -bool KeyboardMouseInput::ConsumeMouseRelease(int btn) -{ - if (btn < 0 || btn >= 3) return false; - bool released = m_mouseReleasedAccum[btn]; - m_mouseReleasedAccum[btn] = false; - return released; + if (button >= 0 && button < MAX_MOUSE_BUTTONS) + return m_mouseBtnReleased[button]; + return false; } void KeyboardMouseInput::ConsumeMouseDelta(float &dx, float &dy) { - dx = m_mouseDeltaXAccum; - dy = m_mouseDeltaYAccum; - m_mouseDeltaXAccum = 0.0f; - m_mouseDeltaYAccum = 0.0f; + dx = (float)m_mouseDeltaAccumX; + dy = (float)m_mouseDeltaAccumY; + m_mouseDeltaAccumX = 0; + m_mouseDeltaAccumY = 0; } -int KeyboardMouseInput::ConsumeScrollDelta() +void KeyboardMouseInput::SetMouseGrabbed(bool grabbed) { - int delta = m_scrollDeltaAccum; - m_scrollDeltaAccum = 0; - return delta; -} + if (m_mouseGrabbed == grabbed) + return; -// Mouse capture -void KeyboardMouseInput::SetCapture(bool capture) -{ - if (capture == m_captured) return; - m_captured = capture; - - if (capture) + m_mouseGrabbed = grabbed; + if (grabbed && g_hWnd) { - ShowCursor(FALSE); - RECT rect; - GetClientRect(m_hWnd, &rect); - POINT topLeft = { rect.left, rect.top }; - POINT bottomRight = { rect.right, rect.bottom }; - ClientToScreen(m_hWnd, &topLeft); - ClientToScreen(m_hWnd, &bottomRight); - RECT screenRect = { topLeft.x, topLeft.y, bottomRight.x, bottomRight.y }; - ClipCursor(&screenRect); - CenterCursor(); + while (ShowCursor(FALSE) >= 0) {} + ClipCursorToWindow(g_hWnd); - // Flush accumulated deltas so the snap-to-center doesn't cause a jump - m_mouseDeltaXAccum = 0.0f; - m_mouseDeltaYAccum = 0.0f; + RECT rc; + GetClientRect(g_hWnd, &rc); + POINT center; + center.x = (rc.right - rc.left) / 2; + center.y = (rc.bottom - rc.top) / 2; + ClientToScreen(g_hWnd, ¢er); + SetCursorPos(center.x, center.y); + + m_mouseDeltaAccumX = 0; + m_mouseDeltaAccumY = 0; } - else + else if (!grabbed && !m_cursorHiddenForUI && g_hWnd) { - ShowCursor(TRUE); + while (ShowCursor(TRUE) < 0) {} ClipCursor(NULL); } } -bool KeyboardMouseInput::IsCaptured() const { return m_captured; } - -void KeyboardMouseInput::CenterCursor() +void KeyboardMouseInput::SetCursorHiddenForUI(bool hidden) { - RECT rect; - GetClientRect(m_hWnd, &rect); - POINT center = { (rect.left + rect.right) / 2, (rect.top + rect.bottom) / 2 }; - ClientToScreen(m_hWnd, ¢er); - SetCursorPos(center.x, center.y); + if (m_cursorHiddenForUI == hidden) + return; + + m_cursorHiddenForUI = hidden; + if (hidden && g_hWnd) + { + while (ShowCursor(FALSE) >= 0) {} + ClipCursorToWindow(g_hWnd); + + RECT rc; + GetClientRect(g_hWnd, &rc); + POINT center; + center.x = (rc.right - rc.left) / 2; + center.y = (rc.bottom - rc.top) / 2; + ClientToScreen(g_hWnd, ¢er); + SetCursorPos(center.x, center.y); + + m_mouseDeltaAccumX = 0; + m_mouseDeltaAccumY = 0; + } + else if (!hidden && !m_mouseGrabbed && g_hWnd) + { + while (ShowCursor(TRUE) < 0) {} + ClipCursor(NULL); + } +} + +static void ClipCursorToWindow(HWND hWnd) +{ + if (!hWnd) return; + RECT rc; + GetClientRect(hWnd, &rc); + POINT topLeft = { rc.left, rc.top }; + POINT bottomRight = { rc.right, rc.bottom }; + ClientToScreen(hWnd, &topLeft); + ClientToScreen(hWnd, &bottomRight); + RECT clipRect = { topLeft.x, topLeft.y, bottomRight.x, bottomRight.y }; + ClipCursor(&clipRect); +} + +void KeyboardMouseInput::SetWindowFocused(bool focused) +{ + m_windowFocused = focused; + if (focused) + { + if (m_mouseGrabbed || m_cursorHiddenForUI) + { + while (ShowCursor(FALSE) >= 0) {} + ClipCursorToWindow(g_hWnd); + } + else + { + while (ShowCursor(TRUE) < 0) {} + ClipCursor(NULL); + } + } + else + { + while (ShowCursor(TRUE) < 0) {} + ClipCursor(NULL); + } +} + +float KeyboardMouseInput::GetMoveX() const +{ + float x = 0.0f; + if (m_keyDown[KEY_LEFT]) x += 1.0f; + if (m_keyDown[KEY_RIGHT]) x -= 1.0f; + return x; +} + +float KeyboardMouseInput::GetMoveY() const +{ + float y = 0.0f; + if (m_keyDown[KEY_FORWARD]) y += 1.0f; + if (m_keyDown[KEY_BACKWARD]) y -= 1.0f; + return y; +} + +float KeyboardMouseInput::GetLookX(float sensitivity) const +{ + return (float)m_mouseDeltaX * sensitivity; +} + +float KeyboardMouseInput::GetLookY(float sensitivity) const +{ + return (float)(-m_mouseDeltaY) * sensitivity; } #endif // _WINDOWS64 diff --git a/Minecraft.Client/Windows64/KeyboardMouseInput.h b/Minecraft.Client/Windows64/KeyboardMouseInput.h index a09843f9..f098ccab 100644 --- a/Minecraft.Client/Windows64/KeyboardMouseInput.h +++ b/Minecraft.Client/Windows64/KeyboardMouseInput.h @@ -4,88 +4,130 @@ #include -// HID usage page and usage for raw input registration -#ifndef HID_USAGE_PAGE_GENERIC -#define HID_USAGE_PAGE_GENERIC ((USHORT)0x01) -#endif -#ifndef HID_USAGE_GENERIC_MOUSE -#define HID_USAGE_GENERIC_MOUSE ((USHORT)0x02) -#endif - class KeyboardMouseInput { public: - KeyboardMouseInput(); - ~KeyboardMouseInput(); + static const int MAX_KEYS = 256; - void Init(HWND hWnd); + static const int MOUSE_LEFT = 0; + static const int MOUSE_RIGHT = 1; + static const int MOUSE_MIDDLE = 2; + static const int MAX_MOUSE_BUTTONS = 3; + + static const int KEY_FORWARD = 'W'; + static const int KEY_BACKWARD = 'S'; + static const int KEY_LEFT = 'A'; + static const int KEY_RIGHT = 'D'; + static const int KEY_JUMP = VK_SPACE; + static const int KEY_SNEAK = VK_LSHIFT; + static const int KEY_SPRINT = VK_LCONTROL; + static const int KEY_INVENTORY = 'E'; + static const int KEY_DROP = 'Q'; + static const int KEY_CRAFTING = 'C'; + static const int KEY_CRAFTING_ALT = 'R'; + static const int KEY_CONFIRM = VK_RETURN; + static const int KEY_CANCEL = VK_ESCAPE; + static const int KEY_PAUSE = VK_ESCAPE; + static const int KEY_THIRD_PERSON = VK_F5; + static const int KEY_DEBUG_INFO = VK_F3; + + void Init(); void Tick(); - void EndFrame(); - - // Called from WndProc - void OnKeyDown(WPARAM vk); - void OnKeyUp(WPARAM vk); - void OnRawMouseInput(LPARAM lParam); - void OnMouseButton(int button, bool down); - void OnMouseWheel(int delta); void ClearAllState(); - // Per-frame edge detection (for UI / per-frame logic like Alt toggle) - bool IsKeyDown(int vk) const; - bool IsKeyPressed(int vk) const; - bool IsKeyReleased(int vk) const; - bool IsMouseDown(int btn) const; - bool IsMousePressed(int btn) const; - bool IsMouseReleased(int btn) const; - - // Game-tick consume methods: accumulate across frames, clear on read. - // Use these from code that runs at game tick rate (20Hz). - bool ConsumeKeyPress(int vk); - bool ConsumeMousePress(int btn); - bool ConsumeMouseRelease(int btn); - void ConsumeMouseDelta(float &dx, float &dy); - int ConsumeScrollDelta(); - - // Absolute cursor position (client-area coordinates, for GUI when not captured) + void OnKeyDown(int vkCode); + void OnKeyUp(int vkCode); + void OnMouseButtonDown(int button); + void OnMouseButtonUp(int button); void OnMouseMove(int x, int y); - int GetMouseX() const; - int GetMouseY() const; - HWND GetHWnd() const; + void OnMouseWheel(int delta); + void OnRawMouseDelta(int dx, int dy); - // Mouse capture for FPS look - void SetCapture(bool capture); - bool IsCaptured() const; + bool IsKeyDown(int vkCode) const; + bool IsKeyPressed(int vkCode) const; + bool IsKeyReleased(int vkCode) const; + + bool IsMouseButtonDown(int button) const; + bool IsMouseButtonPressed(int button) const; + bool IsMouseButtonReleased(int button) const; + + int GetMouseX() const { return m_mouseX; } + int GetMouseY() const { return m_mouseY; } + + int GetMouseDeltaX() const { return m_mouseDeltaX; } + int GetMouseDeltaY() const { return m_mouseDeltaY; } + + int GetMouseWheel(); + int PeekMouseWheel() const { return m_mouseWheelAccum; } + void ConsumeMouseWheel() { m_mouseWheelAccum = 0; } + + // Per-frame delta consumption for low-latency mouse look. + // Reads and clears the raw accumulators (not the per-tick snapshot). + void ConsumeMouseDelta(float &dx, float &dy); + + void SetMouseGrabbed(bool grabbed); + bool IsMouseGrabbed() const { return m_mouseGrabbed; } + + void SetCursorHiddenForUI(bool hidden); + bool IsCursorHiddenForUI() const { return m_cursorHiddenForUI; } + + void SetWindowFocused(bool focused); + bool IsWindowFocused() const { return m_windowFocused; } + + bool HasAnyInput() const { return m_hasInput; } + + void SetKBMActive(bool active) { m_kbmActive = active; } + bool IsKBMActive() const { return m_kbmActive; } + + void SetScreenCursorHidden(bool hidden) { m_screenWantsCursorHidden = hidden; } + bool IsScreenCursorHidden() const { return m_screenWantsCursorHidden; } + + float GetMoveX() const; + float GetMoveY() const; + + float GetLookX(float sensitivity) const; + float GetLookY(float sensitivity) const; private: - void CenterCursor(); + bool m_keyDown[MAX_KEYS]; + bool m_keyDownPrev[MAX_KEYS]; - // Per-frame double-buffered state (for IsKeyPressed/Released per-frame edge detection) - bool m_keyState[256]; - bool m_keyStatePrev[256]; - bool m_mouseButtons[3]; - bool m_mouseButtonsPrev[3]; + bool m_keyPressedAccum[MAX_KEYS]; + bool m_keyReleasedAccum[MAX_KEYS]; + bool m_keyPressed[MAX_KEYS]; + bool m_keyReleased[MAX_KEYS]; - // Sticky press accumulators (persist until consumed by game tick) - bool m_keyPressedAccum[256]; - bool m_mousePressedAccum[3]; - bool m_mouseReleasedAccum[3]; + bool m_mouseButtonDown[MAX_MOUSE_BUTTONS]; + bool m_mouseButtonDownPrev[MAX_MOUSE_BUTTONS]; - // Mouse delta accumulators (persist until consumed by game tick) - float m_mouseDeltaXAccum; - float m_mouseDeltaYAccum; + bool m_mouseBtnPressedAccum[MAX_MOUSE_BUTTONS]; + bool m_mouseBtnReleasedAccum[MAX_MOUSE_BUTTONS]; + bool m_mouseBtnPressed[MAX_MOUSE_BUTTONS]; + bool m_mouseBtnReleased[MAX_MOUSE_BUTTONS]; - // Scroll accumulator (persists until consumed by game tick) - int m_scrollDeltaAccum; - - bool m_captured; - HWND m_hWnd; - bool m_initialized; - - // Absolute cursor position in client coordinates int m_mouseX; int m_mouseY; + + int m_mouseDeltaX; + int m_mouseDeltaY; + int m_mouseDeltaAccumX; + int m_mouseDeltaAccumY; + + int m_mouseWheelAccum; + + bool m_mouseGrabbed; + + bool m_cursorHiddenForUI; + + bool m_windowFocused; + + bool m_hasInput; + + bool m_kbmActive; + + bool m_screenWantsCursorHidden; }; -extern KeyboardMouseInput KMInput; +extern KeyboardMouseInput g_KBMInput; #endif // _WINDOWS64 diff --git a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp index ec89feb1..0f83fae5 100644 --- a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp +++ b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp @@ -501,82 +501,90 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) PostQuitMessage(0); break; - // Keyboard/Mouse input handling - case WM_KEYDOWN: - if (!(lParam & 0x40000000)) // ignore auto-repeat - KMInput.OnKeyDown(wParam); - break; - case WM_KEYUP: - KMInput.OnKeyUp(wParam); - break; - case WM_SYSKEYDOWN: - if (wParam == VK_MENU) // Alt key - { - if (!(lParam & 0x40000000)) - KMInput.OnKeyDown(wParam); - return 0; // prevent default Alt behavior - } - return DefWindowProc(hWnd, message, wParam, lParam); - case WM_SYSKEYUP: - if (wParam == VK_MENU) - { - KMInput.OnKeyUp(wParam); - return 0; - } - return DefWindowProc(hWnd, message, wParam, lParam); - case WM_INPUT: - KMInput.OnRawMouseInput(lParam); - break; - case WM_LBUTTONDOWN: - KMInput.OnMouseButton(0, true); - break; - case WM_LBUTTONUP: - KMInput.OnMouseButton(0, false); - break; - case WM_RBUTTONDOWN: - KMInput.OnMouseButton(1, true); - break; - case WM_RBUTTONUP: - KMInput.OnMouseButton(1, false); - break; - case WM_MBUTTONDOWN: - KMInput.OnMouseButton(2, true); - break; - case WM_MBUTTONUP: - KMInput.OnMouseButton(2, false); - break; - case WM_MOUSEWHEEL: - KMInput.OnMouseWheel(GET_WHEEL_DELTA_WPARAM(wParam)); - break; - case WM_MOUSEMOVE: - KMInput.OnMouseMove(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); - break; - case WM_ACTIVATE: - if (LOWORD(wParam) == WA_INACTIVE) - KMInput.SetCapture(false); - break; - case WM_SETFOCUS: - { - // Re-capture when window receives focus (e.g., after clicking on it) - Minecraft *pMinecraft = Minecraft::GetInstance(); - bool shouldCapture = pMinecraft && app.GetGameStarted() && !ui.GetMenuDisplayed(0) && pMinecraft->screen == NULL; - if (shouldCapture) - KMInput.SetCapture(true); - } - break; case WM_KILLFOCUS: - KMInput.SetCapture(false); - KMInput.ClearAllState(); + g_KBMInput.ClearAllState(); + g_KBMInput.SetWindowFocused(false); + if (g_KBMInput.IsMouseGrabbed()) + g_KBMInput.SetMouseGrabbed(false); break; - case WM_SETCURSOR: - // Hide the OS cursor when an Iggy/Flash menu is displayed (it has its own Flash cursor) - if (LOWORD(lParam) == HTCLIENT && !KMInput.IsCaptured() && ui.GetMenuDisplayed(0)) + case WM_SETFOCUS: + g_KBMInput.SetWindowFocused(true); + break; + + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + { + int vk = (int)wParam; + if (lParam & 0x40000000) break; // ignore auto-repeat + if (vk == VK_SHIFT) + vk = (MapVirtualKey((lParam >> 16) & 0xFF, MAPVK_VSC_TO_VK_EX) == VK_RSHIFT) ? VK_RSHIFT : VK_LSHIFT; + else if (vk == VK_CONTROL) + vk = (lParam & (1 << 24)) ? VK_RCONTROL : VK_LCONTROL; + else if (vk == VK_MENU) + vk = (lParam & (1 << 24)) ? VK_RMENU : VK_LMENU; + g_KBMInput.OnKeyDown(vk); + break; + } + case WM_KEYUP: + case WM_SYSKEYUP: + { + int vk = (int)wParam; + if (vk == VK_SHIFT) + vk = (MapVirtualKey((lParam >> 16) & 0xFF, MAPVK_VSC_TO_VK_EX) == VK_RSHIFT) ? VK_RSHIFT : VK_LSHIFT; + else if (vk == VK_CONTROL) + vk = (lParam & (1 << 24)) ? VK_RCONTROL : VK_LCONTROL; + else if (vk == VK_MENU) + vk = (lParam & (1 << 24)) ? VK_RMENU : VK_LMENU; + g_KBMInput.OnKeyUp(vk); + break; + } + + case WM_LBUTTONDOWN: + g_KBMInput.OnMouseButtonDown(KeyboardMouseInput::MOUSE_LEFT); + break; + case WM_LBUTTONUP: + g_KBMInput.OnMouseButtonUp(KeyboardMouseInput::MOUSE_LEFT); + break; + case WM_RBUTTONDOWN: + g_KBMInput.OnMouseButtonDown(KeyboardMouseInput::MOUSE_RIGHT); + break; + case WM_RBUTTONUP: + g_KBMInput.OnMouseButtonUp(KeyboardMouseInput::MOUSE_RIGHT); + break; + case WM_MBUTTONDOWN: + g_KBMInput.OnMouseButtonDown(KeyboardMouseInput::MOUSE_MIDDLE); + break; + case WM_MBUTTONUP: + g_KBMInput.OnMouseButtonUp(KeyboardMouseInput::MOUSE_MIDDLE); + break; + + case WM_MOUSEMOVE: + g_KBMInput.OnMouseMove(LOWORD(lParam), HIWORD(lParam)); + break; + + case WM_MOUSEWHEEL: + g_KBMInput.OnMouseWheel(GET_WHEEL_DELTA_WPARAM(wParam)); + break; + + case WM_INPUT: { - SetCursor(NULL); - return TRUE; + UINT dwSize = 0; + GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER)); + if (dwSize > 0 && dwSize <= 256) + { + BYTE rawBuffer[256]; + if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, rawBuffer, &dwSize, sizeof(RAWINPUTHEADER)) == dwSize) + { + RAWINPUT* raw = (RAWINPUT*)rawBuffer; + if (raw->header.dwType == RIM_TYPEMOUSE) + { + g_KBMInput.OnRawMouseDelta(raw->data.mouse.lLastX, raw->data.mouse.lLastY); + } + } + } } - return DefWindowProc(hWnd, message, wParam, lParam); + break; default: return DefWindowProc(hWnd, message, wParam, lParam); } @@ -852,6 +860,9 @@ void ToggleFullscreen() SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_FRAMECHANGED); } g_isFullscreen = !g_isFullscreen; + + if (g_KBMInput.IsWindowFocused()) + g_KBMInput.SetWindowFocused(true); } //-------------------------------------------------------------------------------------- @@ -877,7 +888,7 @@ static Minecraft* InitialiseMinecraftRuntime() ui.init(g_pd3dDevice, g_pImmediateContext, g_pRenderTargetView, g_pDepthStencilView, g_iScreenWidth, g_iScreenHeight); InputManager.Initialise(1, 3, MINECRAFT_ACTION_MAX, ACTION_MAX_MENU); - KMInput.Init(g_hWnd); + g_KBMInput.Init(); DefineActions(); InputManager.SetJoypadMapVal(0, 0); InputManager.SetKeyRepeatRate(0.3f, 0.2f); @@ -1263,12 +1274,16 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, MSG msg = {0}; while( WM_QUIT != msg.message && !app.m_bShutdown) { - if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) + g_KBMInput.Tick(); + + while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); - continue; + if (msg.message == WM_QUIT) break; } + if (msg.message == WM_QUIT) break; + RenderManager.StartFrame(); #if 0 if(pMinecraft->soundEngine->isStreamingWavebankReady() && @@ -1290,7 +1305,34 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, app.UpdateTime(); PIXBeginNamedEvent(0,"Input manager tick"); InputManager.Tick(); - KMInput.Tick(); + + // Detect KBM vs controller input mode + if (InputManager.IsPadConnected(0)) + { + bool controllerUsed = InputManager.ButtonPressed(0) || + InputManager.GetJoypadStick_LX(0, false) != 0.0f || + InputManager.GetJoypadStick_LY(0, false) != 0.0f || + InputManager.GetJoypadStick_RX(0, false) != 0.0f || + InputManager.GetJoypadStick_RY(0, false) != 0.0f; + + if (controllerUsed) + g_KBMInput.SetKBMActive(false); + else if (g_KBMInput.HasAnyInput()) + g_KBMInput.SetKBMActive(true); + } + else + { + g_KBMInput.SetKBMActive(true); + } + + if (!g_KBMInput.IsMouseGrabbed()) + { + if (!g_KBMInput.IsKBMActive()) + g_KBMInput.SetCursorHiddenForUI(true); + else if (!g_KBMInput.IsScreenCursorHidden()) + g_KBMInput.SetCursorHiddenForUI(false); + } + PIXEndNamedEvent(); PIXBeginNamedEvent(0,"Profile manager tick"); // ProfileManager.Tick(); @@ -1420,29 +1462,29 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, ui.CheckMenuDisplayed(); - // Update mouse capture: capture when in-game and no menu is open + // Update mouse grab: grab when in-game and no menu is open { static bool altToggleSuppressCapture = false; bool shouldCapture = app.GetGameStarted() && !ui.GetMenuDisplayed(0) && pMinecraft->screen == NULL; // Left Alt key toggles capture on/off for debugging - if (KMInput.IsKeyPressed(VK_MENU)) + if (g_KBMInput.IsKeyPressed(VK_LMENU) || g_KBMInput.IsKeyPressed(VK_RMENU)) { - if (KMInput.IsCaptured()) { KMInput.SetCapture(false); altToggleSuppressCapture = true; } - else if (shouldCapture) { KMInput.SetCapture(true); altToggleSuppressCapture = false; } + if (g_KBMInput.IsMouseGrabbed()) { g_KBMInput.SetMouseGrabbed(false); altToggleSuppressCapture = true; } + else if (shouldCapture) { g_KBMInput.SetMouseGrabbed(true); altToggleSuppressCapture = false; } } else if (!shouldCapture) { - if (KMInput.IsCaptured()) KMInput.SetCapture(false); + if (g_KBMInput.IsMouseGrabbed()) g_KBMInput.SetMouseGrabbed(false); altToggleSuppressCapture = false; } - else if (shouldCapture && !KMInput.IsCaptured() && GetFocus() == g_hWnd && !altToggleSuppressCapture) + else if (shouldCapture && !g_KBMInput.IsMouseGrabbed() && GetFocus() == g_hWnd && !altToggleSuppressCapture) { - KMInput.SetCapture(true); + g_KBMInput.SetMouseGrabbed(true); } } // F1 toggles the HUD - if (KMInput.IsKeyPressed(VK_F1)) + if (g_KBMInput.IsKeyPressed(VK_F1)) { int primaryPad = ProfileManager.GetPrimaryPad(); unsigned char displayHud = app.GetGameSettings(primaryPad, eGameSetting_DisplayHUD); @@ -1451,7 +1493,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, } // F3 toggles onscreen debug info - if (KMInput.IsKeyPressed(VK_F3)) + if (g_KBMInput.IsKeyPressed(VK_F3)) { if (Minecraft* pMinecraft = Minecraft::GetInstance()) { @@ -1464,7 +1506,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, #ifdef _DEBUG_MENUS_ENABLED // F4 Open debug overlay - if (KMInput.IsKeyPressed(VK_F4)) + if (g_KBMInput.IsKeyPressed(VK_F4)) { if (Minecraft *pMinecraft = Minecraft::GetInstance()) { @@ -1477,7 +1519,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, } // F6 Open debug console - if (KMInput.IsKeyPressed(VK_F6)) + if (g_KBMInput.IsKeyPressed(VK_F6)) { static bool s_debugConsole = false; s_debugConsole = !s_debugConsole; @@ -1486,13 +1528,13 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, #endif // F11 Toggle fullscreen - if (KMInput.IsKeyPressed(VK_F11)) + if (g_KBMInput.IsKeyPressed(VK_F11)) { ToggleFullscreen(); } // TAB opens game info menu. - Vvis :3 - Updated by detectiveren - if (KMInput.IsKeyPressed(VK_TAB) && !ui.GetMenuDisplayed(0)) + if (g_KBMInput.IsKeyPressed(VK_TAB) && !ui.GetMenuDisplayed(0)) { if (Minecraft* pMinecraft = Minecraft::GetInstance()) { @@ -1593,8 +1635,6 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, // Fix for #7318 - Title crashes after short soak in the leaderboards menu // A memory leak was caused because the icon renderer kept creating new Vec3's because the pool wasn't reset Vec3::resetPool(); - - KMInput.EndFrame(); } // Free resources, unregister custom classes, and exit. diff --git a/Minecraft.Client/stubs.cpp b/Minecraft.Client/stubs.cpp index 16215ba2..af65eb75 100644 --- a/Minecraft.Client/stubs.cpp +++ b/Minecraft.Client/stubs.cpp @@ -3,41 +3,98 @@ #ifdef _WINDOWS64 #include "Windows64\KeyboardMouseInput.h" +static const int s_keyToVK[] = { + 'A', // KEY_A = 0 + 'B', // KEY_B = 1 + 'C', // KEY_C = 2 + 'D', // KEY_D = 3 + 'E', // KEY_E = 4 + 'F', // KEY_F = 5 + 'G', // KEY_G = 6 + 'H', // KEY_H = 7 + 'I', // KEY_I = 8 + 'J', // KEY_J = 9 + 'K', // KEY_K = 10 + 'L', // KEY_L = 11 + 'M', // KEY_M = 12 + 'N', // KEY_N = 13 + 'O', // KEY_O = 14 + 'P', // KEY_P = 15 + 'Q', // KEY_Q = 16 + 'R', // KEY_R = 17 + 'S', // KEY_S = 18 + 'T', // KEY_T = 19 + 'U', // KEY_U = 20 + 'V', // KEY_V = 21 + 'W', // KEY_W = 22 + 'X', // KEY_X = 23 + 'Y', // KEY_Y = 24 + 'Z', // KEY_Z = 25 + VK_SPACE, // KEY_SPACE = 26 + VK_LSHIFT, // KEY_LSHIFT = 27 + VK_ESCAPE, // KEY_ESCAPE = 28 + VK_BACK, // KEY_BACK = 29 + VK_RETURN, // KEY_RETURN = 30 + VK_RSHIFT, // KEY_RSHIFT = 31 + VK_UP, // KEY_UP = 32 + VK_DOWN, // KEY_DOWN = 33 + VK_TAB, // KEY_TAB = 34 + '1', // KEY_1 = 35 + '2', // KEY_2 = 36 + '3', // KEY_3 = 37 + '4', // KEY_4 = 38 + '5', // KEY_5 = 39 + '6', // KEY_6 = 40 + '7', // KEY_7 = 41 + '8', // KEY_8 = 42 + '9', // KEY_9 = 43 + VK_F1, // KEY_F1 = 44 + VK_F3, // KEY_F3 = 45 + VK_F4, // KEY_F4 = 46 + VK_F5, // KEY_F5 = 47 + VK_F6, // KEY_F6 = 48 + VK_F8, // KEY_F8 = 49 + VK_F9, // KEY_F9 = 50 + VK_F11, // KEY_F11 = 51 + VK_ADD, // KEY_ADD = 52 + VK_SUBTRACT,// KEY_SUBTRACT = 53 + VK_LEFT, // KEY_LEFT = 54 + VK_RIGHT, // KEY_RIGHT = 55 +}; +static const int s_keyToVKCount = sizeof(s_keyToVK) / sizeof(s_keyToVK[0]); + +int Keyboard::toVK(int keyConst) +{ + if (keyConst >= 0 && keyConst < s_keyToVKCount) + return s_keyToVK[keyConst]; + return 0; +} + +bool Keyboard::isKeyDown(int keyCode) +{ + int vk = toVK(keyCode); + if (vk > 0) + return g_KBMInput.IsKeyDown(vk); + return false; +} + int Mouse::getX() { - return KMInput.GetMouseX(); + return g_KBMInput.GetMouseX(); } int Mouse::getY() { // Return Y in bottom-up coordinates (OpenGL convention, matching original Java LWJGL Mouse) + extern HWND g_hWnd; RECT rect; - GetClientRect(KMInput.GetHWnd(), &rect); - return (rect.bottom - 1) - KMInput.GetMouseY(); + GetClientRect(g_hWnd, &rect); + return (rect.bottom - 1) - g_KBMInput.GetMouseY(); } bool Mouse::isButtonDown(int button) { - return KMInput.IsMouseDown(button); -} - -bool Keyboard::isKeyDown(int key) -{ - // Map Keyboard constants to Windows virtual key codes - if (key == Keyboard::KEY_LSHIFT) return KMInput.IsKeyDown(VK_LSHIFT); - if (key == Keyboard::KEY_RSHIFT) return KMInput.IsKeyDown(VK_RSHIFT); - if (key == Keyboard::KEY_ESCAPE) return KMInput.IsKeyDown(VK_ESCAPE); - if (key == Keyboard::KEY_RETURN) return KMInput.IsKeyDown(VK_RETURN); - if (key == Keyboard::KEY_BACK) return KMInput.IsKeyDown(VK_BACK); - if (key == Keyboard::KEY_SPACE) return KMInput.IsKeyDown(VK_SPACE); - if (key == Keyboard::KEY_TAB) return KMInput.IsKeyDown(VK_TAB); - if (key == Keyboard::KEY_UP) return KMInput.IsKeyDown(VK_UP); - if (key == Keyboard::KEY_DOWN) return KMInput.IsKeyDown(VK_DOWN); - if (key == Keyboard::KEY_LEFT) return KMInput.IsKeyDown(VK_LEFT); - if (key == Keyboard::KEY_RIGHT) return KMInput.IsKeyDown(VK_RIGHT); - if (key >= Keyboard::KEY_A && key <= Keyboard::KEY_Z) - return KMInput.IsKeyDown('A' + (key - Keyboard::KEY_A)); - return false; + return g_KBMInput.IsMouseButtonDown(button); } #endif diff --git a/Minecraft.Client/stubs.h b/Minecraft.Client/stubs.h index cc788867..f4ae056f 100644 --- a/Minecraft.Client/stubs.h +++ b/Minecraft.Client/stubs.h @@ -187,12 +187,13 @@ public: static void create() {} static void destroy() {} #ifdef _WINDOWS64 - static bool isKeyDown(int key); + static bool isKeyDown(int keyCode); #else - static bool isKeyDown(int) {return false;} + static bool isKeyDown(int) { return false; } #endif static wstring getKeyName(int) { return L"KEYNAME"; } static void enableRepeatEvents(bool) {} + static const int KEY_A = 0; static const int KEY_B = 1; static const int KEY_C = 2; @@ -228,8 +229,32 @@ public: static const int KEY_UP = 32; static const int KEY_DOWN = 33; static const int KEY_TAB = 34; - static const int KEY_LEFT = 35; - static const int KEY_RIGHT = 36; + static const int KEY_1 = 35; + static const int KEY_2 = 36; + static const int KEY_3 = 37; + static const int KEY_4 = 38; + static const int KEY_5 = 39; + static const int KEY_6 = 40; + static const int KEY_7 = 41; + static const int KEY_8 = 42; + static const int KEY_9 = 43; + static const int KEY_F1 = 44; + static const int KEY_F3 = 45; + static const int KEY_F4 = 46; + static const int KEY_F5 = 47; + static const int KEY_F6 = 48; + static const int KEY_F8 = 49; + static const int KEY_F9 = 50; + static const int KEY_F11 = 51; + static const int KEY_ADD = 52; + static const int KEY_SUBTRACT = 53; + static const int KEY_LEFT = 54; + static const int KEY_RIGHT = 55; + +#ifdef _WINDOWS64 + // Map LWJGL-style key constant to Windows VK code + static int toVK(int keyConst); +#endif }; class Mouse diff --git a/README.md b/README.md index 7578f159..960deff3 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,6 @@ The headless server also reads and writes `server.properties` in the working dir - **Toggle View (FPS/TPS)**: `F5` - **Fullscreen**: `F11` - **Pause Menu**: `Esc` -- **Toggle Mouse Capture**: `Left Alt` (for debugging) - **Attack / Destroy**: `Left Click` - **Use / Place**: `Right Click` - **Select Item**: `Mouse Wheel` or keys `1` to `9`