Fix save list, delete save, exit without saving, and blank username on Windows64 (#539)
* Fix world save rename not applying new name
KeyboardCompleteWorldNameCallback had no _WINDOWS64 branch, so the
typed name was validated then silently discarded on every rename attempt.
Write the new name to a worldname.txt sidecar file next to the save
(Windows64\GameHDD\{folder}\worldname.txt) and update the in-memory
display name immediately. ReadLevelNameFromSaveFile now checks for this
sidecar first so renamed saves persist correctly across restarts.
* Fixed gamertag being blank upon renaming and re-joining a save
* Save deletion fix, exiting without saving fix
* Add native in-game keyboard UI for world naming and renaming
This commit is contained in:
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
};
|
};
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "UI.h"
|
#include "UI.h"
|
||||||
#include "UIScene_LoadMenu.h"
|
#include "UIScene_LoadMenu.h"
|
||||||
#include "..\..\Minecraft.h"
|
#include "..\..\Minecraft.h"
|
||||||
|
#include "..\..\User.h"
|
||||||
#include "..\..\TexturePackRepository.h"
|
#include "..\..\TexturePackRepository.h"
|
||||||
#include "..\..\Options.h"
|
#include "..\..\Options.h"
|
||||||
#include "..\..\MinecraftServer.h"
|
#include "..\..\MinecraftServer.h"
|
||||||
@@ -273,7 +274,7 @@ UIScene_LoadMenu::UIScene_LoadMenu(int iPad, void *initData, UILayer *parentLaye
|
|||||||
m_bIgnoreInput = false;
|
m_bIgnoreInput = false;
|
||||||
|
|
||||||
Minecraft *pMinecraft = Minecraft::GetInstance();
|
Minecraft *pMinecraft = Minecraft::GetInstance();
|
||||||
int texturePacksCount = pMinecraft->skins->getTexturePackCount();
|
unsigned int texturePacksCount = (unsigned int)pMinecraft->skins->getTexturePackCount();
|
||||||
for(unsigned int i = 0; i < texturePacksCount; ++i)
|
for(unsigned int i = 0; i < texturePacksCount; ++i)
|
||||||
{
|
{
|
||||||
TexturePack *tp = pMinecraft->skins->getTexturePackByIndex(i);
|
TexturePack *tp = pMinecraft->skins->getTexturePackByIndex(i);
|
||||||
@@ -1629,6 +1630,12 @@ void UIScene_LoadMenu::StartGameFromSave(UIScene_LoadMenu* pClass, DWORD dwLocal
|
|||||||
|
|
||||||
param->settings = app.GetGameHostOption( eGameHostOption_All );
|
param->settings = app.GetGameHostOption( eGameHostOption_All );
|
||||||
|
|
||||||
|
#ifdef _WINDOWS64
|
||||||
|
{
|
||||||
|
extern wchar_t g_Win64UsernameW[17];
|
||||||
|
Minecraft::GetInstance()->user->name = g_Win64UsernameW;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#ifndef _XBOX
|
#ifndef _XBOX
|
||||||
g_NetworkManager.FakeLocalPlayerJoined();
|
g_NetworkManager.FakeLocalPlayerJoined();
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -31,6 +31,32 @@
|
|||||||
|
|
||||||
static wstring ReadLevelNameFromSaveFile(const wstring& filePath)
|
static wstring ReadLevelNameFromSaveFile(const wstring& filePath)
|
||||||
{
|
{
|
||||||
|
// Check for a worldname.txt sidecar written by the rename feature first
|
||||||
|
size_t slashPos = filePath.rfind(L'\\');
|
||||||
|
if (slashPos != wstring::npos)
|
||||||
|
{
|
||||||
|
wstring sidecarPath = filePath.substr(0, slashPos + 1) + L"worldname.txt";
|
||||||
|
FILE *fr = NULL;
|
||||||
|
if (_wfopen_s(&fr, sidecarPath.c_str(), L"r") == 0 && fr)
|
||||||
|
{
|
||||||
|
char buf[128] = {};
|
||||||
|
if (fgets(buf, sizeof(buf), fr))
|
||||||
|
{
|
||||||
|
int len = (int)strlen(buf);
|
||||||
|
while (len > 0 && (buf[len-1] == '\n' || buf[len-1] == '\r' || buf[len-1] == ' '))
|
||||||
|
buf[--len] = '\0';
|
||||||
|
fclose(fr);
|
||||||
|
if (len > 0)
|
||||||
|
{
|
||||||
|
wchar_t wbuf[128] = {};
|
||||||
|
mbstowcs(wbuf, buf, 127);
|
||||||
|
return wstring(wbuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else fclose(fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
HANDLE hFile = CreateFileW(filePath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
|
HANDLE hFile = CreateFileW(filePath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
|
||||||
if (hFile == INVALID_HANDLE_VALUE) return L"";
|
if (hFile == INVALID_HANDLE_VALUE) return L"";
|
||||||
|
|
||||||
@@ -110,8 +136,8 @@ static wstring ReadLevelNameFromSaveFile(const wstring& filePath)
|
|||||||
|
|
||||||
if (freeSaveData) delete[] saveData;
|
if (freeSaveData) delete[] saveData;
|
||||||
delete[] rawData;
|
delete[] rawData;
|
||||||
// "world" is the engine default — it means no real name was ever set, so
|
// "world" is the engine default - it means no real name was ever set,
|
||||||
// return empty to let the caller fall back to the save filename (timestamp).
|
// so return empty to let the caller fall back to the save filename (timestamp).
|
||||||
if (result == L"world") result = L"";
|
if (result == L"world") result = L"";
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -644,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
|
||||||
{
|
{
|
||||||
@@ -1335,7 +1359,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)
|
||||||
@@ -1343,6 +1371,38 @@ int UIScene_LoadOrJoinMenu::KeyboardCompleteWorldNameCallback(LPVOID lpParam,boo
|
|||||||
#if (defined __PS3__ || defined __ORBIS__ || defined _DURANGO || defined(__PSVITA__))
|
#if (defined __PS3__ || defined __ORBIS__ || defined _DURANGO || defined(__PSVITA__))
|
||||||
// open the save and overwrite the metadata
|
// open the save and overwrite the metadata
|
||||||
StorageManager.RenameSaveData(pClass->m_iSaveListIndex - pClass->m_iDefaultButtonsC, ui16Text,&UIScene_LoadOrJoinMenu::RenameSaveDataReturned,pClass);
|
StorageManager.RenameSaveData(pClass->m_iSaveListIndex - pClass->m_iDefaultButtonsC, ui16Text,&UIScene_LoadOrJoinMenu::RenameSaveDataReturned,pClass);
|
||||||
|
#elif defined(_WINDOWS64)
|
||||||
|
{
|
||||||
|
int listPos = pClass->m_iSaveListIndex - pClass->m_iDefaultButtonsC;
|
||||||
|
|
||||||
|
// Convert the ui16Text input to a wide string
|
||||||
|
wchar_t wNewName[128] = {};
|
||||||
|
for (int k = 0; k < 127 && ui16Text[k]; k++)
|
||||||
|
wNewName[k] = (wchar_t)ui16Text[k];
|
||||||
|
|
||||||
|
// Convert to narrow for storage and in-memory update
|
||||||
|
char narrowName[128] = {};
|
||||||
|
wcstombs(narrowName, wNewName, 127);
|
||||||
|
|
||||||
|
// Build the sidecar path: Windows64\GameHDD\{folder}\worldname.txt
|
||||||
|
wchar_t wFilename[MAX_SAVEFILENAME_LENGTH] = {};
|
||||||
|
mbstowcs(wFilename, pClass->m_saveDetails[listPos].UTF8SaveFilename, MAX_SAVEFILENAME_LENGTH - 1);
|
||||||
|
wstring sidecarPath = wstring(L"Windows64\\GameHDD\\") + wstring(wFilename) + wstring(L"\\worldname.txt");
|
||||||
|
|
||||||
|
FILE *fw = NULL;
|
||||||
|
if (_wfopen_s(&fw, sidecarPath.c_str(), L"w") == 0 && fw)
|
||||||
|
{
|
||||||
|
fputs(narrowName, fw);
|
||||||
|
fclose(fw);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the in-memory display name so the list reflects it immediately
|
||||||
|
strncpy_s(pClass->m_saveDetails[listPos].UTF8SaveName, narrowName, 127);
|
||||||
|
pClass->m_saveDetails[listPos].UTF8SaveName[127] = '\0';
|
||||||
|
|
||||||
|
// Reuse the existing callback to trigger the list repopulate
|
||||||
|
UIScene_LoadOrJoinMenu::RenameSaveDataReturned(pClass, true);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -2189,6 +2249,31 @@ void UIScene_LoadOrJoinMenu::LoadSaveFromCloud()
|
|||||||
|
|
||||||
#endif //SONY_REMOTE_STORAGE_DOWNLOAD
|
#endif //SONY_REMOTE_STORAGE_DOWNLOAD
|
||||||
|
|
||||||
|
#ifdef _WINDOWS64
|
||||||
|
static bool Win64_DeleteSaveDirectory(const wchar_t* wPath)
|
||||||
|
{
|
||||||
|
wchar_t wSearch[MAX_PATH];
|
||||||
|
swprintf_s(wSearch, MAX_PATH, L"%s\\*", wPath);
|
||||||
|
WIN32_FIND_DATAW fd;
|
||||||
|
HANDLE hFind = FindFirstFileW(wSearch, &fd);
|
||||||
|
if (hFind != INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (wcscmp(fd.cFileName, L".") == 0 || wcscmp(fd.cFileName, L"..") == 0) continue;
|
||||||
|
wchar_t wChild[MAX_PATH];
|
||||||
|
swprintf_s(wChild, MAX_PATH, L"%s\\%s", wPath, fd.cFileName);
|
||||||
|
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||||
|
Win64_DeleteSaveDirectory(wChild);
|
||||||
|
else
|
||||||
|
DeleteFileW(wChild);
|
||||||
|
} while (FindNextFileW(hFind, &fd));
|
||||||
|
FindClose(hFind);
|
||||||
|
}
|
||||||
|
return RemoveDirectoryW(wPath) != 0;
|
||||||
|
}
|
||||||
|
#endif // _WINDOWS64
|
||||||
|
|
||||||
int UIScene_LoadOrJoinMenu::DeleteSaveDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result)
|
int UIScene_LoadOrJoinMenu::DeleteSaveDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result)
|
||||||
{
|
{
|
||||||
UIScene_LoadOrJoinMenu* pClass = (UIScene_LoadOrJoinMenu*)pParam;
|
UIScene_LoadOrJoinMenu* pClass = (UIScene_LoadOrJoinMenu*)pParam;
|
||||||
@@ -2205,7 +2290,24 @@ int UIScene_LoadOrJoinMenu::DeleteSaveDialogReturned(void *pParam,int iPad,C4JSt
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
#ifdef _WINDOWS64
|
||||||
|
{
|
||||||
|
// Use m_saveDetails (sorted display order) so the correct folder is targeted
|
||||||
|
int displayIdx = pClass->m_iSaveListIndex - pClass->m_iDefaultButtonsC;
|
||||||
|
bool bSuccess = false;
|
||||||
|
if (pClass->m_saveDetails && displayIdx >= 0 && pClass->m_saveDetails[displayIdx].UTF8SaveFilename[0])
|
||||||
|
{
|
||||||
|
wchar_t wFilename[MAX_SAVEFILENAME_LENGTH] = {};
|
||||||
|
mbstowcs_s(NULL, wFilename, MAX_SAVEFILENAME_LENGTH, pClass->m_saveDetails[displayIdx].UTF8SaveFilename, MAX_SAVEFILENAME_LENGTH - 1);
|
||||||
|
wchar_t wFolderPath[MAX_PATH] = {};
|
||||||
|
swprintf_s(wFolderPath, MAX_PATH, L"Windows64\\GameHDD\\%s", wFilename);
|
||||||
|
bSuccess = Win64_DeleteSaveDirectory(wFolderPath);
|
||||||
|
}
|
||||||
|
UIScene_LoadOrJoinMenu::DeleteSaveDataReturned((LPVOID)pClass->GetCallbackUniqueId(), bSuccess);
|
||||||
|
}
|
||||||
|
#else
|
||||||
StorageManager.DeleteSaveData(&pClass->m_pSaveDetails->SaveInfoA[pClass->m_iSaveListIndex - pClass->m_iDefaultButtonsC], UIScene_LoadOrJoinMenu::DeleteSaveDataReturned, (LPVOID)pClass->GetCallbackUniqueId());
|
StorageManager.DeleteSaveData(&pClass->m_pSaveDetails->SaveInfoA[pClass->m_iSaveListIndex - pClass->m_iDefaultButtonsC], UIScene_LoadOrJoinMenu::DeleteSaveDataReturned, (LPVOID)pClass->GetCallbackUniqueId());
|
||||||
|
#endif
|
||||||
pClass->m_controlSavesTimer.setVisible( true );
|
pClass->m_controlSavesTimer.setVisible( true );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2281,7 +2383,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
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -329,6 +329,10 @@ void IQNet::EndGame()
|
|||||||
m_player[i].m_gamertag[0] = 0;
|
m_player[i].m_gamertag[0] = 0;
|
||||||
m_player[i].SetCustomDataValue(0);
|
m_player[i].SetCustomDataValue(0);
|
||||||
}
|
}
|
||||||
|
// Restore local player 0's gamertag so re-joining works correctly
|
||||||
|
extern wchar_t g_Win64UsernameW[17];
|
||||||
|
m_player[0].m_isHostPlayer = true;
|
||||||
|
wcscpy_s(m_player[0].m_gamertag, 32, g_Win64UsernameW);
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD MinecraftDynamicConfigurations::GetTrialTime() { return DYNAMIC_CONFIG_DEFAULT_TRIAL_TIME; }
|
DWORD MinecraftDynamicConfigurations::GetTrialTime() { return DYNAMIC_CONFIG_DEFAULT_TRIAL_TIME; }
|
||||||
|
|||||||
@@ -1230,7 +1230,11 @@ bool MinecraftServer::loadLevel(LevelStorageSource *storageSource, const wstring
|
|||||||
|
|
||||||
if( levels[0]->isNew || levels[1]->isNew || levels[2]->isNew )
|
if( levels[0]->isNew || levels[1]->isNew || levels[2]->isNew )
|
||||||
{
|
{
|
||||||
|
#ifndef _WINDOWS64
|
||||||
|
// On Windows64 we skip the automatic initial save so that choosing
|
||||||
|
// "Exit without saving" on a new world does not leave an orphaned save folder.
|
||||||
levels[0]->saveToDisc(mcprogress, false);
|
levels[0]->saveToDisc(mcprogress, false);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if( s_bServerHalted || !g_NetworkManager.IsInSession() ) return false;
|
if( s_bServerHalted || !g_NetworkManager.IsInSession() ) return false;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -84,6 +84,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;
|
||||||
|
|
||||||
@@ -129,6 +134,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;
|
||||||
|
|||||||
@@ -565,6 +565,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:
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user