Skip to content

Instantly share code, notes, and snippets.

@Kaloyan501
Created March 3, 2026 18:34
Show Gist options
  • Select an option

  • Save Kaloyan501/6d6abc33e84abca1432304b01a14d098 to your computer and use it in GitHub Desktop.

Select an option

Save Kaloyan501/6d6abc33e84abca1432304b01a14d098 to your computer and use it in GitHub Desktop.
Advanced Keyboard and Mouse shim for Minecraft Legacy Console Edition
#include "stdafx.h"
#include <Windows64\4JLibs\inc\4J_Input.h>
#include <Common\App_enums.h>
#include <Windows.h>
#include <iostream>
#include <atomic>
#include <chrono>
#include <vector>
#include <algorithm>
C_4JInput InputManager;
// --- MOUSE EMULATION (cursor sampling / recenter) ---
namespace MouseEmu
{
static std::atomic<int> g_dx(0);
static std::atomic<int> g_dy(0);
static float g_cached_rx = 0.0f;
static float g_cached_ry = 0.0f;
static bool g_cache_valid = false;
static DWORD g_last_poll = 0;
static bool g_was_focused = false;
static POINT g_center = {0,0};
static float g_sensitivity = 0.035f;
static float g_deadzone = 0.02f;
static float g_decayPerSecond = 16.0f;
template<typename T>
T Clamp(T val, T minVal, T maxVal)
{
if (val < minVal) return minVal;
if (val > maxVal) return maxVal;
return val;
}
static void SetupFocusState()
{
HWND hwnd = FindWindowA("MinecraftClass", NULL);
if (!hwnd) return;
RECT rc;
if (!GetClientRect(hwnd, &rc)) return;
POINT ul = { rc.left, rc.top }, lr = { rc.right, rc.bottom };
ClientToScreen(hwnd, &ul);
ClientToScreen(hwnd, &lr);
g_center.x = (ul.x + lr.x) / 2;
g_center.y = (ul.y + lr.y) / 2;
RECT clip = { ul.x, ul.y, lr.x, lr.y };
ClipCursor(&clip);
SetCursorPos(g_center.x, g_center.y);
ShowCursor(FALSE);
}
static void ClearFocusState()
{
ClipCursor(NULL);
ShowCursor(TRUE);
g_dx.store(0);
g_dy.store(0);
g_cached_rx = 0.0f;
g_cached_ry = 0.0f;
}
void NewFrame()
{
g_cache_valid = false;
bool focused = false;
HWND hwnd = FindWindowA("MinecraftClass", NULL);
if (hwnd && GetForegroundWindow() == hwnd) focused = true;
if (focused && !g_was_focused) {
SetupFocusState();
g_was_focused = true;
g_last_poll = GetTickCount();
return;
}
if (!focused && g_was_focused) {
ClearFocusState();
g_was_focused = false;
return;
}
if (focused) {
POINT p;
if (GetCursorPos(&p)) {
int dx = p.x - g_center.x;
int dy = p.y - g_center.y;
if (dx != 0 || dy != 0) {
g_dx.fetch_add(dx);
g_dy.fetch_add(dy);
SetCursorPos(g_center.x, g_center.y);
}
}
} else {
g_dx.store(0);
g_dy.store(0);
}
}
void Poll()
{
DWORD now = GetTickCount();
float dt = 1.0f / 60.0f;
if (g_last_poll != 0) {
DWORD diff = now - g_last_poll;
dt = diff / 1000.0f;
if (dt <= 0.0f) dt = 1.0f / 60.0f;
}
g_last_poll = now;
if (g_cache_valid) return;
int dx = g_dx.exchange(0);
int dy = g_dy.exchange(0);
dx = Clamp(dx, -30, 30);
dy = Clamp(dy, -30, 30);
float raw_rx = dx * g_sensitivity;
float raw_ry = -dy * g_sensitivity;
raw_rx = Clamp(raw_rx, -1.0f, 1.0f);
raw_ry = Clamp(raw_ry, -1.0f, 1.0f);
float lerpFactor = 1.0f - (float)exp(-g_decayPerSecond * dt);
g_cached_rx = g_cached_rx + (raw_rx - g_cached_rx) * lerpFactor;
g_cached_ry = g_cached_ry + (raw_ry - g_cached_ry) * lerpFactor;
//char buf[128];
//_snprintf_s(buf, sizeof(buf), _TRUNCATE, "MouseEmu: dx=%d dy=%d raw_rx=%.3f raw_ry=%.3f\n", dx, dy, raw_rx, raw_ry);
//OutputDebugStringA(buf);
g_cache_valid = true;
}
float GetRX() { Poll(); return g_cached_rx; }
float GetRY() { Poll(); return g_cached_ry; }
}
bool IsWindowFocused()
{
HWND hwnd = FindWindowA("MinecraftClass", NULL);
if (!hwnd)
return false;
return GetForegroundWindow() == hwnd;
}
void C_4JInput::Initialise(int iInputStateC, unsigned char ucMapC, unsigned char ucActionC, unsigned char ucMenuActionC)
{
}
bool m_CurrentKeys[256] = {};
bool m_PreviousKeys[256] = {};
void C_4JInput::Tick(void)
{
MouseEmu::NewFrame();
for (int i = 0; i < 256; ++i)
{
m_PreviousKeys[i] = m_CurrentKeys[i];
m_CurrentKeys[i] = (GetAsyncKeyState(i) & 0x8000) != 0;
}
}
void C_4JInput::SetDeadzoneAndMovementRange(unsigned int uiDeadzone, unsigned int uiMovementRangeMax)
{
}
void C_4JInput::SetGameJoypadMaps(unsigned char ucMap, unsigned char ucAction, unsigned int uiActionVal)
{
}
unsigned int C_4JInput::GetGameJoypadMaps(unsigned char ucMap, unsigned char ucAction)
{
return 0;
}
void C_4JInput::SetJoypadMapVal(int iPad, unsigned char ucMap)
{
}
unsigned char C_4JInput::GetJoypadMapVal(int iPad)
{
return 0;
}
void C_4JInput::SetJoypadSensitivity(int iPad, float fSensitivity)
{
}
bool KeyDown(int key) {
return (GetAsyncKeyState(key) & 0x8000) != 0;
}
bool KeyPressed(int key) {
return m_CurrentKeys[key] && !m_PreviousKeys[key];
}
bool KeyReleased(int key) {
return !m_CurrentKeys[key] && m_PreviousKeys[key];
}
bool C_4JInput::ButtonPressed(int iPad, unsigned char ucAction)
{
if (!IsWindowFocused())
return false;
switch (ucAction) {
//MAIN CONTROLS
case ACTION_MENU_UP: return KeyPressed(VK_UP);
case ACTION_MENU_DOWN: return KeyPressed(VK_DOWN);
case ACTION_MENU_LEFT: return KeyPressed(VK_LEFT);
case ACTION_MENU_RIGHT: return KeyPressed(VK_RIGHT);
case ACTION_MENU_OK:
case ACTION_MENU_A: return KeyPressed(VK_RETURN);
case ACTION_MENU_CANCEL:
case ACTION_MENU_B: return KeyPressed(VK_ESCAPE);
case ACTION_MENU_X: return KeyPressed('R');
case ACTION_MENU_Y: return KeyPressed('T');
case MINECRAFT_ACTION_FORWARD: return KeyPressed('W');
case MINECRAFT_ACTION_BACKWARD: return KeyPressed('S');
case MINECRAFT_ACTION_LEFT: return KeyPressed('A');
case MINECRAFT_ACTION_RIGHT: return KeyPressed('D');
case MINECRAFT_ACTION_JUMP: return KeyPressed(VK_SPACE);
case MINECRAFT_ACTION_INVENTORY: return KeyPressed('R');
case MINECRAFT_ACTION_DROP: return KeyPressed('Z');
case MINECRAFT_ACTION_ACTION: return KeyPressed(VK_LBUTTON);
case MINECRAFT_ACTION_USE: return KeyPressed(VK_RBUTTON);
//AUXILARY ACTIONS
case MINECRAFT_ACTION_RENDER_THIRD_PERSON: return KeyPressed(VK_F5);
case MINECRAFT_ACTION_PAUSEMENU: return KeyPressed(VK_ESCAPE);
case MINECRAFT_ACTION_CRAFTING: return KeyPressed('T');
case MINECRAFT_ACTION_SNEAK_TOGGLE: return KeyPressed(VK_LSHIFT);
//DPAD
case MINECRAFT_ACTION_DPAD_UP: return KeyPressed('I');
case MINECRAFT_ACTION_DPAD_DOWN: return KeyPressed('K');
case MINECRAFT_ACTION_DPAD_LEFT: return KeyPressed('J');
case MINECRAFT_ACTION_DPAD_RIGHT: return KeyPressed('L');
case MINECRAFT_ACTION_LEFT_SCROLL: return KeyPressed('1');
case MINECRAFT_ACTION_RIGHT_SCROLL: return KeyPressed('2');
case ACTION_MENU_LEFT_SCROLL: return KeyPressed('Q');
case ACTION_MENU_RIGHT_SCROLL: return KeyPressed('E');
case ACTION_MENU_OTHER_STICK_PRESS:
case ACTION_MENU_STICK_PRESS: return KeyPressed(VK_LCONTROL);
}
return false;
}
bool C_4JInput::ButtonReleased(int iPad, unsigned char ucAction)
{
if (!IsWindowFocused())
return false;
switch (ucAction) {
//MAIN CONTROLS
case ACTION_MENU_UP: return KeyReleased(VK_UP);
case ACTION_MENU_DOWN: return KeyReleased(VK_DOWN);
case ACTION_MENU_LEFT: return KeyReleased(VK_LEFT);
case ACTION_MENU_RIGHT: return KeyReleased(VK_RIGHT);
case ACTION_MENU_OK:
case ACTION_MENU_A: return KeyReleased(VK_RETURN);
case ACTION_MENU_CANCEL:
case ACTION_MENU_B: return KeyReleased(VK_ESCAPE);
case ACTION_MENU_X: return KeyReleased('R');
case ACTION_MENU_Y: return KeyReleased('T');
case MINECRAFT_ACTION_FORWARD: return KeyReleased('W');
case MINECRAFT_ACTION_BACKWARD: return KeyReleased('S');
case MINECRAFT_ACTION_LEFT: return KeyReleased('A');
case MINECRAFT_ACTION_RIGHT: return KeyReleased('D');
case MINECRAFT_ACTION_JUMP: return KeyReleased(VK_SPACE);
case MINECRAFT_ACTION_INVENTORY: return KeyReleased('R');
case MINECRAFT_ACTION_DROP: return KeyReleased('Z');
case MINECRAFT_ACTION_ACTION: return KeyReleased(VK_LBUTTON);
case MINECRAFT_ACTION_USE: return KeyReleased(VK_RBUTTON);
//AUXILARY ACTIONS
case MINECRAFT_ACTION_RENDER_THIRD_PERSON: return KeyReleased(VK_F5);
case MINECRAFT_ACTION_PAUSEMENU: return KeyReleased(VK_ESCAPE);
case MINECRAFT_ACTION_CRAFTING: return KeyReleased('T');
case MINECRAFT_ACTION_SNEAK_TOGGLE: return KeyReleased(VK_LSHIFT);
//DPAD
case MINECRAFT_ACTION_DPAD_UP: return KeyReleased('I');
case MINECRAFT_ACTION_DPAD_DOWN: return KeyReleased('K');
case MINECRAFT_ACTION_DPAD_LEFT: return KeyReleased('J');
case MINECRAFT_ACTION_DPAD_RIGHT: return KeyReleased('L');
case MINECRAFT_ACTION_LEFT_SCROLL: return KeyReleased('1');
case MINECRAFT_ACTION_RIGHT_SCROLL: return KeyReleased('2');
case ACTION_MENU_LEFT_SCROLL: return KeyReleased('Q');
case ACTION_MENU_RIGHT_SCROLL: return KeyReleased('E');
case ACTION_MENU_OTHER_STICK_PRESS:
case ACTION_MENU_STICK_PRESS: return KeyReleased(VK_LCONTROL);
}
return false;
}
bool C_4JInput::ButtonDown(int iPad, unsigned char ucAction)
{
if (!IsWindowFocused())
return false;
switch (ucAction) {
//MAIN CONTROLS
case ACTION_MENU_UP: return KeyDown(VK_UP);
case ACTION_MENU_DOWN: return KeyDown(VK_DOWN);
case ACTION_MENU_LEFT: return KeyDown(VK_LEFT);
case ACTION_MENU_RIGHT: return KeyDown(VK_RIGHT);
case ACTION_MENU_OK:
case ACTION_MENU_A: return KeyDown(VK_RETURN);
case ACTION_MENU_CANCEL:
case ACTION_MENU_B: return KeyDown(VK_ESCAPE);
case ACTION_MENU_X: return KeyDown('R');
case ACTION_MENU_Y: return KeyDown('T');
case MINECRAFT_ACTION_FORWARD: return KeyDown('W');
case MINECRAFT_ACTION_BACKWARD: return KeyDown('S');
case MINECRAFT_ACTION_LEFT: return KeyDown('A');
case MINECRAFT_ACTION_RIGHT: return KeyDown('D');
case MINECRAFT_ACTION_JUMP: return KeyDown(VK_SPACE);
case MINECRAFT_ACTION_INVENTORY: return KeyDown('R');
case MINECRAFT_ACTION_DROP: return KeyDown('Z');
case MINECRAFT_ACTION_ACTION: return KeyDown(VK_LBUTTON);
case MINECRAFT_ACTION_USE: return KeyDown(VK_RBUTTON);
//AUXILARY ACTIONS
case MINECRAFT_ACTION_RENDER_THIRD_PERSON: return KeyDown(VK_F5);
case MINECRAFT_ACTION_PAUSEMENU: return KeyDown(VK_ESCAPE);
case MINECRAFT_ACTION_CRAFTING: return KeyDown('T');
case MINECRAFT_ACTION_SNEAK_TOGGLE: return KeyDown(VK_LSHIFT);
//DPAD
case MINECRAFT_ACTION_DPAD_UP: return KeyDown('I');
case MINECRAFT_ACTION_DPAD_DOWN: return KeyDown('K');
case MINECRAFT_ACTION_DPAD_LEFT: return KeyDown('J');
case MINECRAFT_ACTION_DPAD_RIGHT: return KeyDown('L');
case MINECRAFT_ACTION_LEFT_SCROLL: return KeyDown('1');
case MINECRAFT_ACTION_RIGHT_SCROLL: return KeyDown('2');
case ACTION_MENU_LEFT_SCROLL: return KeyDown('Q');
case ACTION_MENU_RIGHT_SCROLL: return KeyDown('E');
case ACTION_MENU_OTHER_STICK_PRESS:
case ACTION_MENU_STICK_PRESS: return KeyDown(VK_LCONTROL);
}
return false;
}
/*bool C_4JInput::ButtonPressedDirect(int key) {
return KeyPressed(key);
}
*/
unsigned int C_4JInput::GetValue(int iPad, unsigned char ucAction, bool bRepeat)
{
return ButtonDown(iPad, ucAction);
}
void C_4JInput::SetJoypadStickAxisMap(int iPad, unsigned int uiFrom, unsigned int uiTo)
{
}
void C_4JInput::SetJoypadStickTriggerMap(int iPad, unsigned int uiFrom, unsigned int uiTo)
{
}
void C_4JInput::SetKeyRepeatRate(float fRepeatDelaySecs, float fRepeatRateSecs)
{
}
void C_4JInput::SetDebugSequence(const char* chSequenceA, int(*Func)(LPVOID), LPVOID lpParam)
{
}
FLOAT C_4JInput::GetIdleSeconds(int iPad)
{
return 0.0f;
}
bool C_4JInput::IsPadConnected(int iPad)
{
if (iPad == 0) return true;
return false;
}
float C_4JInput::GetJoypadStick_LX(int iPad, bool bCheckMenuDisplay)
{
if (!IsWindowFocused())
return 0.0f;
if (KeyDown('A')) {
return -1.0f;
} else if (KeyDown('D')) {
return 1.0f;
}
return 0.0f;
}
float C_4JInput::GetJoypadStick_LY(int iPad, bool bCheckMenuDisplay)
{
if (!IsWindowFocused())
return 0.0f;
if (KeyDown('W')) {
return 1.0f;
} else if (KeyDown('S')) {
return -1.0f;
}
return 0.0f;
}
float C_4JInput::GetJoypadStick_RX(int iPad, bool bCheckMenuDisplay)
{
if (!IsWindowFocused())
return 0.0f;
return MouseEmu::GetRX();
}
float C_4JInput::GetJoypadStick_RY(int iPad, bool bCheckMenuDisplay)
{
if (!IsWindowFocused())
return 0.0f;
return MouseEmu::GetRY();
}
unsigned char C_4JInput::GetJoypadLTrigger(int iPad, bool bCheckMenuDisplay)
{
if (!IsWindowFocused())
return 0;
if (KeyDown(VK_RBUTTON)) {
return 1;
}
return 0;
}
unsigned char C_4JInput::GetJoypadRTrigger(int iPad, bool bCheckMenuDisplay)
{
if (!IsWindowFocused())
return 0;
if (KeyDown(VK_LBUTTON)) {
return 1;
}
return 0;
}
void C_4JInput::SetMenuDisplayed(int iPad, bool bVal)
{
}
EKeyboardResult C_4JInput::RequestKeyboard(LPCWSTR Title, LPCWSTR Text, DWORD dwPad, UINT uiMaxChars, int( *Func)(LPVOID,const bool),LPVOID lpParam,C_4JInput::EKeyboardMode eMode)
{
return EKeyboard_Pending;
}
void C_4JInput::GetText(uint16_t* UTF16String)
{
}
bool C_4JInput::VerifyStrings(
WCHAR** pwStringA,
int iStringC,
int(*Func)(LPVOID, STRING_VERIFY_RESPONSE*),
LPVOID lpParam)
{
return false;
}
void C_4JInput::CancelQueuedVerifyStrings(
int(*Func)(LPVOID, STRING_VERIFY_RESPONSE*),
LPVOID lpParam)
{
}
void C_4JInput::CancelAllVerifyInProgress(void)
{
}
@Kaloyan501
Copy link
Author

On Windows 11, go to View -> Show -> File Name Extensions. Then, go into the Minecraft.Client folder, make a new file, name it input_keyboard.cpp, paste the content of the code block above. Compile, and it should run. Mouse doesnt work all the time, if it doesnt work, alt tab out of the game and then into it.

@Kaloyan501
Copy link
Author

Controls are as follows:

  • WASD for movement
  • Mouse for camera
  • 1 for hotbar left
  • 2 for hotbar right
  • Q for menu scroll left
  • E for menu scroll right
  • R for inventory
  • T for crafting
  • Shift for crouch
  • I for debug flight
  • K for debug menu
  • Esc for menu close / B on controller / Pause

@Kaloyan501
Copy link
Author

Based on original by @nommiin

@LingonJR
Copy link

LingonJR commented Mar 4, 2026

Thank you so much!

@miniskitz0
Copy link

how do you scroll down on the menu

@LingonJR
Copy link

LingonJR commented Mar 4, 2026

how do you scroll down on the menu

You use the arrows to navigate the start menu

@miniskitz0
Copy link

mb i meant like the creative menu

@feryatii
Copy link

feryatii commented Mar 5, 2026

what do you mean with compile?

@feryatii
Copy link

feryatii commented Mar 5, 2026

could not open source file "Windows64\4JLibs\inc\4J_Input.h" (no directories in search list). says it in vs code

@feryatii
Copy link

feryatii commented Mar 5, 2026

cannot open source file "stdafx.h"

@Crafter2908
Copy link

in the main menu the cursor skips down two steps instead of one, making some options impossible to access, also the sensitivity of the hotbar is very high, but otherwise this works really nicely. also maybe seperate the binds for the pause menu and b and such

@Crafter2908
Copy link

also thanks for making this

@Kaloyan501
Copy link
Author

mb i meant like the creative menu

For the controlls shown as Controller button left and Controller button right, you use q and e respectively.

@Kaloyan501
Copy link
Author

could not open source file "Windows64\4JLibs\inc\4J_Input.h" (no directories in search list). says it in vs code

Wait VS Code? You're supposed to compile this in Visual Studio 2012 in C++ mode with the Windows 8 SDK installed

@Kaloyan501
Copy link
Author

in the main menu the cursor skips down two steps instead of one, making some options impossible to access, also the sensitivity of the hotbar is very high, but otherwise this works really nicely. also maybe seperate the binds for the pause menu and b and such

I have noticed that, I may fix it, but considering smartcmd has a fork of the source with WAY more advanced mouse support, I'll even wanna bother. Link to that other fork is https://github.com/smartcmd/MinecraftConsoles

@Kaloyan501
Copy link
Author

also thanks for making this

No problem! I like messing around with random stuff like this.

@LuBart08
Copy link

LuBart08 commented Mar 6, 2026

Where I need to place that file

@Kaloyan501
Copy link
Author

Where I need to place that file

Everything is explained in the first message below the code, open Minecraft.Client, make a new file named input_keyboard.cpp there, paste the code in, save and compile

@ingfar77-lab
Copy link

What is the button for a?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment