Add native in-game keyboard UI for world naming and renaming

This commit is contained in:
dtentiion 2026-03-05 15:24:19 +00:00
parent 9671b36b37
commit 0391b2d556
9 changed files with 368 additions and 18 deletions

View file

@ -84,6 +84,10 @@ UIScene_CreateWorldMenu::UIScene_CreateWorldMenu(int iPad, void *initData, UILay
m_iGameModeId = GameType::SURVIVAL->getId(); m_iGameModeId = GameType::SURVIVAL->getId();
m_pDLCPack = NULL; m_pDLCPack = NULL;
m_bRebuildTouchBoxes = false; m_bRebuildTouchBoxes = false;
#ifdef _WINDOWS64
m_bDirectEditing = false;
m_iDirectEditCooldown = 0;
#endif
m_bMultiplayerAllowed = ProfileManager.IsSignedInLive( m_iPad ) && ProfileManager.AllowedToPlayMultiplayer(m_iPad); m_bMultiplayerAllowed = ProfileManager.IsSignedInLive( m_iPad ) && ProfileManager.AllowedToPlayMultiplayer(m_iPad);
// 4J-PB - read the settings for the online flag. We'll only save this setting if the user changed it. // 4J-PB - read the settings for the online flag. We'll only save this setting if the user changed it.
@ -289,6 +293,54 @@ void UIScene_CreateWorldMenu::tick()
{ {
UIScene::tick(); UIScene::tick();
#ifdef _WINDOWS64
if (m_iDirectEditCooldown > 0)
m_iDirectEditCooldown--;
if (m_bDirectEditing)
{
wchar_t ch;
bool changed = false;
while (g_KBMInput.ConsumeChar(ch))
{
if (ch == 0x08) // backspace
{
if (!m_worldName.empty())
{
m_worldName.pop_back();
changed = true;
}
}
else if (ch == 0x0D) // enter - confirm
{
m_bDirectEditing = false;
m_iDirectEditCooldown = 4; // absorb the matching ACTION_MENU_OK that follows
m_editWorldName.setLabel(m_worldName.c_str());
}
else if ((int)m_worldName.length() < 25)
{
m_worldName += ch;
changed = true;
}
}
// Escape cancels and restores the original name
if (m_bDirectEditing && g_KBMInput.IsKeyPressed(VK_ESCAPE))
{
m_worldName = m_worldNameBeforeEdit;
m_bDirectEditing = false;
m_iDirectEditCooldown = 4;
m_editWorldName.setLabel(m_worldName.c_str());
m_buttonCreateWorld.setEnable(!m_worldName.empty());
}
else if (changed)
{
m_editWorldName.setLabel(m_worldName.c_str());
m_buttonCreateWorld.setEnable(!m_worldName.empty());
}
}
#endif
if(m_iSetTexturePackDescription >= 0 ) if(m_iSetTexturePackDescription >= 0 )
{ {
UpdateTexturePackDescription( m_iSetTexturePackDescription ); UpdateTexturePackDescription( m_iSetTexturePackDescription );
@ -354,6 +406,9 @@ int UIScene_CreateWorldMenu::ContinueOffline(void *pParam,int iPad,C4JStorage::E
void UIScene_CreateWorldMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) void UIScene_CreateWorldMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled)
{ {
if(m_bIgnoreInput) return; if(m_bIgnoreInput) return;
#ifdef _WINDOWS64
if (m_bDirectEditing || m_iDirectEditCooldown > 0) { handled = true; return; }
#endif
ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released);
@ -408,6 +463,9 @@ void UIScene_CreateWorldMenu::handleInput(int iPad, int key, bool repeat, bool p
void UIScene_CreateWorldMenu::handlePress(F64 controlId, F64 childId) void UIScene_CreateWorldMenu::handlePress(F64 controlId, F64 childId)
{ {
if(m_bIgnoreInput) return; if(m_bIgnoreInput) return;
#ifdef _WINDOWS64
if (m_bDirectEditing || m_iDirectEditCooldown > 0) return;
#endif
//CD - Added for audio //CD - Added for audio
ui.PlayUISFX(eSFX_Press); ui.PlayUISFX(eSFX_Press);
@ -417,7 +475,28 @@ void UIScene_CreateWorldMenu::handlePress(F64 controlId, F64 childId)
case eControl_EditWorldName: case eControl_EditWorldName:
{ {
m_bIgnoreInput=true; m_bIgnoreInput=true;
#ifdef _WINDOWS64
if (Win64_IsControllerConnected())
{
UIKeyboardInitData kbData;
kbData.title = app.GetString(IDS_CREATE_NEW_WORLD);
kbData.defaultText = m_editWorldName.getLabel();
kbData.maxChars = 25;
kbData.callback = &UIScene_CreateWorldMenu::KeyboardCompleteWorldNameCallback;
kbData.lpParam = this;
ui.NavigateToScene(m_iPad, eUIScene_Keyboard, &kbData);
}
else
{
// PC without controller: edit the name field directly in-place.
m_bIgnoreInput = false; // Don't block input - m_bDirectEditing is the guard
m_worldNameBeforeEdit = m_worldName;
m_bDirectEditing = true;
g_KBMInput.ClearCharBuffer();
}
#else
InputManager.RequestKeyboard(app.GetString(IDS_CREATE_NEW_WORLD),m_editWorldName.getLabel(),(DWORD)0,25,&UIScene_CreateWorldMenu::KeyboardCompleteWorldNameCallback,this,C_4JInput::EKeyboardMode_Default); InputManager.RequestKeyboard(app.GetString(IDS_CREATE_NEW_WORLD),m_editWorldName.getLabel(),(DWORD)0,25,&UIScene_CreateWorldMenu::KeyboardCompleteWorldNameCallback,this,C_4JInput::EKeyboardMode_Default);
#endif
} }
break; break;
case eControl_GameModeToggle: case eControl_GameModeToggle:
@ -729,7 +808,11 @@ int UIScene_CreateWorldMenu::KeyboardCompleteWorldNameCallback(LPVOID lpParam,bo
{ {
uint16_t pchText[128]; uint16_t pchText[128];
ZeroMemory(pchText, 128 * sizeof(uint16_t) ); ZeroMemory(pchText, 128 * sizeof(uint16_t) );
#ifdef _WINDOWS64
Win64_GetKeyboardText(pchText, 128);
#else
InputManager.GetText(pchText); InputManager.GetText(pchText);
#endif
if(pchText[0]!=0) if(pchText[0]!=0)
{ {

View file

@ -51,6 +51,12 @@ private:
DLCPack * m_pDLCPack; DLCPack * m_pDLCPack;
bool m_bRebuildTouchBoxes; bool m_bRebuildTouchBoxes;
#ifdef _WINDOWS64
bool m_bDirectEditing;
wstring m_worldNameBeforeEdit;
int m_iDirectEditCooldown;
#endif
public: public:
UIScene_CreateWorldMenu(int iPad, void *initData, UILayer *parentLayer); UIScene_CreateWorldMenu(int iPad, void *initData, UILayer *parentLayer);
virtual ~UIScene_CreateWorldMenu(); virtual ~UIScene_CreateWorldMenu();

View file

@ -2,6 +2,12 @@
#include "UI.h" #include "UI.h"
#include "UIScene_Keyboard.h" #include "UIScene_Keyboard.h"
#ifdef _WINDOWS64
// Global buffer that stores the text entered in the native keyboard scene.
// Callbacks retrieve it via Win64_GetKeyboardText() declared in UIStructs.h.
wchar_t g_Win64KeyboardResult[256] = {};
#endif
#define KEYBOARD_DONE_TIMER_ID 0 #define KEYBOARD_DONE_TIMER_ID 0
#define KEYBOARD_DONE_TIMER_TIME 100 #define KEYBOARD_DONE_TIMER_TIME 100
@ -10,31 +16,103 @@ UIScene_Keyboard::UIScene_Keyboard(int iPad, void *initData, UILayer *parentLaye
// Setup all the Iggy references we need for this scene // Setup all the Iggy references we need for this scene
initialiseMovie(); initialiseMovie();
#ifdef _WINDOWS64
m_win64Callback = NULL;
m_win64CallbackParam = NULL;
m_win64TextBuffer = L"";
m_win64MaxChars = 25;
const wchar_t* titleText = L"Enter text";
const wchar_t* defaultText = L"";
m_bPCMode = false;
if (initData)
{
UIKeyboardInitData* kbData = (UIKeyboardInitData*)initData;
m_win64Callback = kbData->callback;
m_win64CallbackParam = kbData->lpParam;
if (kbData->title) titleText = kbData->title;
if (kbData->defaultText) defaultText = kbData->defaultText;
m_win64MaxChars = kbData->maxChars;
m_bPCMode = kbData->pcMode;
}
m_win64TextBuffer = defaultText;
m_EnterTextLabel.init(titleText);
m_KeyboardTextInput.init(defaultText, -1);
m_KeyboardTextInput.SetCharLimit(m_win64MaxChars);
// Clear any leftover typed characters from a previous keyboard session
g_KBMInput.ClearCharBuffer();
g_Win64KeyboardResult[0] = L'\0';
#else
m_EnterTextLabel.init(L"Enter Sign Text"); m_EnterTextLabel.init(L"Enter Sign Text");
m_KeyboardTextInput.init(L"", -1); m_KeyboardTextInput.init(L"", -1);
m_KeyboardTextInput.SetCharLimit(15); m_KeyboardTextInput.SetCharLimit(15);
#endif
m_ButtonSpace.init(L"Space", -1); m_ButtonSpace.init(L"Space", -1);
m_ButtonCursorLeft.init(L"Cursor Left", -1); m_ButtonCursorLeft.init(L"Cur L", -1);
m_ButtonCursorRight.init(L"Cursor Right", -1); m_ButtonCursorRight.init(L"Cur R", -1);
m_ButtonCaps.init(L"Caps", -1); m_ButtonCaps.init(L"Caps", -1);
m_ButtonDone.init(L"Done", 0); // only the done button needs an id, the others will never call back! m_ButtonDone.init(L"Done", 0); // only the done button needs an id, the others will never call back!
m_ButtonSymbols.init(L"Symbols", -1); m_ButtonSymbols.init(L"Symbols", -1);
m_ButtonBackspace.init(L"Backspace", -1); m_ButtonBackspace.init(L"Backspace", -1);
// Initialise function keyboard Buttons and set alternative symbol button string // Initialise function keyboard Buttons and set alternative symbol button string
wstring label = L"Abc"; #ifdef _WINDOWS64
IggyStringUTF16 stringVal; if (!m_bPCMode)
stringVal.string = (IggyUTF16*)label.c_str(); #endif
stringVal.length = label.length(); {
wstring label = L"Abc";
IggyDataValue result; IggyStringUTF16 stringVal;
IggyDataValue value[1]; stringVal.string = (IggyUTF16*)label.c_str();
value[0].type = IGGY_DATATYPE_string_UTF16; stringVal.length = label.length();
value[0].string16 = stringVal;
IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcInitFunctionButtons , 1 , value ); IggyDataValue result;
IggyDataValue value[1];
value[0].type = IGGY_DATATYPE_string_UTF16;
value[0].string16 = stringVal;
IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcInitFunctionButtons , 1 , value );
}
#ifdef _WINDOWS64
if (m_bPCMode)
{
// PC text-input mode: hide all on-screen buttons, user types with physical keyboard
// Hide the mapped function-row buttons
m_ButtonSpace.setVisible(false);
m_ButtonCursorLeft.setVisible(false);
m_ButtonCursorRight.setVisible(false);
m_ButtonCaps.setVisible(false);
m_ButtonSymbols.setVisible(false);
m_ButtonBackspace.setVisible(false);
// Hide the letter/number key grid (Flash-baked, not mapped as UIControls)
static const char* s_keyNames[] = {
"Button_q", "Button_w", "Button_e", "Button_r", "Button_t",
"Button_y", "Button_u", "Button_i", "Button_o", "Button_p",
"Button_a", "Button_s", "Button_d", "Button_f", "Button_g",
"Button_h", "Button_j", "Button_k", "Button_l", "Button_apostraphy",
"Button_z", "Button_x", "Button_c", "Button_v", "Button_b",
"Button_n", "Button_m", "Button_comma", "Button_stop", "Button_qmark",
"Button_0", "Button_1", "Button_2", "Button_3", "Button_4",
"Button_5", "Button_6", "Button_7", "Button_8", "Button_9"
};
IggyName nameVisible = registerFastName(L"visible");
IggyValuePath* root = IggyPlayerRootPath(getMovie());
for (int i = 0; i < (int)(sizeof(s_keyNames) / sizeof(s_keyNames[0])); ++i)
{
IggyValuePath keyPath;
if (IggyValuePathMakeNameRef(&keyPath, root, s_keyNames[i]))
IggyValueSetBooleanRS(&keyPath, nameVisible, NULL, false);
}
}
#endif
m_bKeyboardDonePressed = false; m_bKeyboardDonePressed = false;
@ -80,6 +158,46 @@ bool UIScene_Keyboard::allowRepeat(int key)
return true; return true;
} }
#ifdef _WINDOWS64
void UIScene_Keyboard::tick()
{
UIScene::tick();
// Accumulate physical keyboard chars into our own buffer, then push to Flash via setLabel.
// This bypasses Iggy's focus system (char events only route to the focused element).
// The char buffer is cleared on open so Enter/clicks from the triggering action don't leak in.
wchar_t ch;
bool changed = false;
while (g_KBMInput.ConsumeChar(ch))
{
if (ch == 0x08) // backspace
{
if (!m_win64TextBuffer.empty())
{
m_win64TextBuffer.pop_back();
changed = true;
}
}
else if (ch == 0x0D) // enter - confirm
{
if (!m_bKeyboardDonePressed)
{
addTimer(KEYBOARD_DONE_TIMER_ID, KEYBOARD_DONE_TIMER_TIME);
m_bKeyboardDonePressed = true;
}
}
else if ((int)m_win64TextBuffer.length() < m_win64MaxChars)
{
m_win64TextBuffer += ch;
changed = true;
}
}
if (changed)
m_KeyboardTextInput.setLabel(m_win64TextBuffer.c_str(), true /*instant*/);
}
#endif
void UIScene_Keyboard::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) void UIScene_Keyboard::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled)
{ {
IggyDataValue result; IggyDataValue result;
@ -90,7 +208,18 @@ void UIScene_Keyboard::handleInput(int iPad, int key, bool repeat, bool pressed,
switch(key) switch(key)
{ {
case ACTION_MENU_CANCEL: case ACTION_MENU_CANCEL:
#ifdef _WINDOWS64
{
// Cache before navigateBack() destroys this scene
int(*cb)(LPVOID, const bool) = m_win64Callback;
LPVOID cbParam = m_win64CallbackParam;
navigateBack();
if (cb)
cb(cbParam, false);
}
#else
navigateBack(); navigateBack();
#endif
handled = true; handled = true;
break; break;
case ACTION_MENU_X: // X case ACTION_MENU_X: // X
@ -173,9 +302,28 @@ void UIScene_Keyboard::handleTimerComplete(int id)
void UIScene_Keyboard::KeyboardDonePressed() void UIScene_Keyboard::KeyboardDonePressed()
{ {
// Debug #ifdef _WINDOWS64
app.DebugPrintf("UI Keyboard - DONE - [%ls]\n", m_KeyboardTextInput.getLabel()); // Use getLabel() here — this is a timer callback (not an Iggy callback) so it's safe.
// getLabel() reflects both physical keyboard input (pushed via setLabel) and
// on-screen button input (set directly by Flash ActionScript).
const wchar_t* finalText = m_KeyboardTextInput.getLabel();
app.DebugPrintf("UI Keyboard - DONE - [%ls]\n", finalText);
// ToDo: Keyboard can now pass on its final string value and close itself down // Store the typed text so callbacks can retrieve it via Win64_GetKeyboardText()
wcsncpy_s(g_Win64KeyboardResult, 256, finalText, _TRUNCATE);
// Cache callback and param before navigateBack() which destroys this scene
int(*cb)(LPVOID, const bool) = m_win64Callback;
LPVOID cbParam = m_win64CallbackParam;
// Navigate back so the scene stack is restored before the callback runs
navigateBack(); navigateBack();
// Fire callback: bRes=true means confirmed
if (cb)
cb(cbParam, true);
#else
app.DebugPrintf("UI Keyboard - DONE - [%ls]\n", m_KeyboardTextInput.getLabel());
navigateBack();
#endif
} }

View file

@ -7,6 +7,14 @@ class UIScene_Keyboard : public UIScene
private: private:
bool m_bKeyboardDonePressed; bool m_bKeyboardDonePressed;
#ifdef _WINDOWS64
int(*m_win64Callback)(LPVOID, const bool);
LPVOID m_win64CallbackParam;
wstring m_win64TextBuffer;
int m_win64MaxChars;
bool m_bPCMode; // Hides on-screen keyboard buttons; physical keyboard only
#endif
protected: protected:
UIControl_Label m_EnterTextLabel; UIControl_Label m_EnterTextLabel;
UIControl_TextInput m_KeyboardTextInput; UIControl_TextInput m_KeyboardTextInput;
@ -49,6 +57,10 @@ public:
// INPUT // INPUT
virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled);
#ifdef _WINDOWS64
virtual void tick();
#endif
virtual void handleTimerComplete(int id); virtual void handleTimerComplete(int id);
protected: protected:
@ -75,5 +87,9 @@ public:
#endif #endif
// Returns true if lower scenes in this scenes layer, or in any layer below this scenes layers should be hidden // Returns true if lower scenes in this scenes layer, or in any layer below this scenes layers should be hidden
#ifdef _WINDOWS64
virtual bool hidesLowerScenes() { return true; }
#else
virtual bool hidesLowerScenes() { return false; } virtual bool hidesLowerScenes() { return false; }
#endif
}; };

View file

@ -670,8 +670,6 @@ void UIScene_LoadOrJoinMenu::tick()
{ {
UIScene::tick(); UIScene::tick();
#if (defined __PS3__ || defined __ORBIS__ || defined _DURANGO || defined _WINDOWS64 || defined __PSVITA__) #if (defined __PS3__ || defined __ORBIS__ || defined _DURANGO || defined _WINDOWS64 || defined __PSVITA__)
if(m_bExitScene) // navigate forward or back if(m_bExitScene) // navigate forward or back
{ {
@ -1363,7 +1361,11 @@ int UIScene_LoadOrJoinMenu::KeyboardCompleteWorldNameCallback(LPVOID lpParam,boo
{ {
uint16_t ui16Text[128]; uint16_t ui16Text[128];
ZeroMemory(ui16Text, 128 * sizeof(uint16_t) ); ZeroMemory(ui16Text, 128 * sizeof(uint16_t) );
#ifdef _WINDOWS64
Win64_GetKeyboardText(ui16Text, 128);
#else
InputManager.GetText(ui16Text); InputManager.GetText(ui16Text);
#endif
// check the name is valid // check the name is valid
if(ui16Text[0]!=0) if(ui16Text[0]!=0)
@ -2385,7 +2387,21 @@ int UIScene_LoadOrJoinMenu::SaveOptionsDialogReturned(void *pParam,int iPad,C4JS
case C4JStorage::EMessage_ResultDecline: // rename case C4JStorage::EMessage_ResultDecline: // rename
{ {
pClass->m_bIgnoreInput=true; pClass->m_bIgnoreInput=true;
#ifdef _DURANGO #ifdef _WINDOWS64
{
wchar_t wSaveName[128];
ZeroMemory(wSaveName, 128 * sizeof(wchar_t));
mbstowcs_s(NULL, wSaveName, 128, pClass->m_saveDetails[pClass->m_iSaveListIndex - pClass->m_iDefaultButtonsC].UTF8SaveName, _TRUNCATE);
UIKeyboardInitData kbData;
kbData.title = app.GetString(IDS_RENAME_WORLD_TITLE);
kbData.defaultText = wSaveName;
kbData.maxChars = 25;
kbData.callback = &UIScene_LoadOrJoinMenu::KeyboardCompleteWorldNameCallback;
kbData.lpParam = pClass;
kbData.pcMode = !Win64_IsControllerConnected();
ui.NavigateToScene(pClass->m_iPad, eUIScene_Keyboard, &kbData);
}
#elif defined _DURANGO
// bring up a keyboard // bring up a keyboard
InputManager.RequestKeyboard(app.GetString(IDS_RENAME_WORLD_TITLE), (pClass->m_saveDetails[pClass->m_iSaveListIndex-pClass->m_iDefaultButtonsC]).UTF16SaveName,(DWORD)0,25,&UIScene_LoadOrJoinMenu::KeyboardCompleteWorldNameCallback,pClass,C_4JInput::EKeyboardMode_Default); InputManager.RequestKeyboard(app.GetString(IDS_RENAME_WORLD_TITLE), (pClass->m_saveDetails[pClass->m_iSaveListIndex-pClass->m_iDefaultButtonsC]).UTF16SaveName,(DWORD)0,25,&UIScene_LoadOrJoinMenu::KeyboardCompleteWorldNameCallback,pClass,C_4JInput::EKeyboardMode_Default);
#else #else

View file

@ -282,6 +282,44 @@ typedef struct _JoinMenuInitData
int iPad; int iPad;
} JoinMenuInitData; } JoinMenuInitData;
// Native keyboard (Windows64 replacement for InputManager.RequestKeyboard WinAPI dialog)
#ifdef _WINDOWS64
typedef struct _UIKeyboardInitData
{
const wchar_t* title;
const wchar_t* defaultText;
int maxChars;
int(*callback)(LPVOID, const bool);
LPVOID lpParam;
bool pcMode; // When true, disables on-screen keyboard buttons (PC keyboard users only need the text field)
_UIKeyboardInitData() : title(nullptr), defaultText(nullptr), maxChars(25), callback(nullptr), lpParam(nullptr), pcMode(false) {}
} UIKeyboardInitData;
// Stores the text typed in UIScene_Keyboard so callbacks can retrieve it
// without calling InputManager.GetText (which shows the WinAPI dialog result).
extern wchar_t g_Win64KeyboardResult[256];
inline void Win64_GetKeyboardText(uint16_t* outBuf, int maxChars)
{
wcsncpy_s((wchar_t*)outBuf, maxChars, g_Win64KeyboardResult, _TRUNCATE);
}
// Returns true if any XInput controller is currently connected.
// Used to decide whether to show the in-game keyboard UI or fall back to PC input.
#include <Xinput.h>
inline bool Win64_IsControllerConnected()
{
XINPUT_STATE state;
for (DWORD i = 0; i < XUSER_MAX_COUNT; i++)
{
memset(&state, 0, sizeof(state));
if (XInputGetState(i, &state) == ERROR_SUCCESS)
return true;
}
return false;
}
#endif // _WINDOWS64
// More Options // More Options
typedef struct _LaunchMoreOptionsMenuInitData typedef struct _LaunchMoreOptionsMenuInitData
{ {

View file

@ -55,6 +55,8 @@ void KeyboardMouseInput::Init()
m_hasInput = false; m_hasInput = false;
m_kbmActive = true; m_kbmActive = true;
m_screenWantsCursorHidden = false; m_screenWantsCursorHidden = false;
m_charBufferHead = 0;
m_charBufferTail = 0;
RAWINPUTDEVICE rid; RAWINPUTDEVICE rid;
rid.usUsagePage = 0x01; // HID_USAGE_PAGE_GENERIC rid.usUsagePage = 0x01; // HID_USAGE_PAGE_GENERIC
@ -381,4 +383,29 @@ float KeyboardMouseInput::GetLookY(float sensitivity) const
return (float)(-m_mouseDeltaY) * sensitivity; return (float)(-m_mouseDeltaY) * sensitivity;
} }
void KeyboardMouseInput::OnChar(wchar_t c)
{
int next = (m_charBufferHead + 1) % CHAR_BUFFER_SIZE;
if (next != m_charBufferTail)
{
m_charBuffer[m_charBufferHead] = c;
m_charBufferHead = next;
}
}
bool KeyboardMouseInput::ConsumeChar(wchar_t &outChar)
{
if (m_charBufferTail == m_charBufferHead)
return false;
outChar = m_charBuffer[m_charBufferTail];
m_charBufferTail = (m_charBufferTail + 1) % CHAR_BUFFER_SIZE;
return true;
}
void KeyboardMouseInput::ClearCharBuffer()
{
m_charBufferHead = 0;
m_charBufferTail = 0;
}
#endif // _WINDOWS64 #endif // _WINDOWS64

View file

@ -83,6 +83,11 @@ public:
void SetScreenCursorHidden(bool hidden) { m_screenWantsCursorHidden = hidden; } void SetScreenCursorHidden(bool hidden) { m_screenWantsCursorHidden = hidden; }
bool IsScreenCursorHidden() const { return m_screenWantsCursorHidden; } bool IsScreenCursorHidden() const { return m_screenWantsCursorHidden; }
// Text input: buffer characters typed while the native keyboard scene is open
void OnChar(wchar_t c);
bool ConsumeChar(wchar_t &outChar);
void ClearCharBuffer();
float GetMoveX() const; float GetMoveX() const;
float GetMoveY() const; float GetMoveY() const;
@ -128,6 +133,11 @@ private:
bool m_kbmActive; bool m_kbmActive;
bool m_screenWantsCursorHidden; bool m_screenWantsCursorHidden;
static const int CHAR_BUFFER_SIZE = 32;
wchar_t m_charBuffer[CHAR_BUFFER_SIZE];
int m_charBufferHead;
int m_charBufferTail;
}; };
extern KeyboardMouseInput g_KBMInput; extern KeyboardMouseInput g_KBMInput;

View file

@ -564,6 +564,12 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
g_KBMInput.SetWindowFocused(true); g_KBMInput.SetWindowFocused(true);
break; break;
case WM_CHAR:
// Buffer typed characters so UIScene_Keyboard can dispatch them to the Iggy Flash player
if (wParam >= 0x20 || wParam == 0x08 || wParam == 0x0D) // printable chars + backspace + enter
g_KBMInput.OnChar((wchar_t)wParam);
break;
case WM_KEYDOWN: case WM_KEYDOWN:
case WM_SYSKEYDOWN: case WM_SYSKEYDOWN:
{ {