diff --git a/CMakeLists.txt b/CMakeLists.txt index 897632130d2b..ed6f186c6b1a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -842,6 +842,7 @@ list(APPEND NativeAppSource UI/DiscordIntegration.cpp UI/NativeApp.cpp UI/BackgroundAudio.cpp + UI/ChatScreen.cpp UI/DevScreens.cpp UI/DisplayLayoutEditor.cpp UI/DisplayLayoutScreen.cpp diff --git a/Common/KeyMap.cpp b/Common/KeyMap.cpp index 8b0d840e8ce4..ec94c897974e 100644 --- a/Common/KeyMap.cpp +++ b/Common/KeyMap.cpp @@ -692,6 +692,7 @@ const KeyMap_IntStrPair psp_button_names[] = { {VIRTKEY_AXIS_RIGHT_Y_MIN, "RightAn.Down"}, {VIRTKEY_AXIS_RIGHT_X_MIN, "RightAn.Left"}, {VIRTKEY_AXIS_RIGHT_X_MAX, "RightAn.Right"}, + {VIRTKEY_OPENCHAT, "OpenChat" }, {VIRTKEY_AXIS_SWAP, "AxisSwap"}, {VIRTKEY_DEVMENU, "DevMenu"}, diff --git a/Common/KeyMap.h b/Common/KeyMap.h index da533f28da7e..87493ea415f8 100644 --- a/Common/KeyMap.h +++ b/Common/KeyMap.h @@ -58,6 +58,7 @@ enum { VIRTKEY_TEXTURE_REPLACE = 0x4000001A, VIRTKEY_SCREENSHOT = 0x4000001B, VIRTKEY_MUTE_TOGGLE = 0x4000001C, + VIRTKEY_OPENCHAT = 0x4000001D, VIRTKEY_LAST, VIRTKEY_COUNT = VIRTKEY_LAST - VIRTKEY_FIRST }; diff --git a/Core/Config.cpp b/Core/Config.cpp index 525467bf4a22..d8e03cf5abf9 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -920,7 +920,15 @@ static ConfigSetting controlSettings[] = { static ConfigSetting networkSettings[] = { ConfigSetting("EnableWlan", &g_Config.bEnableWlan, false, true, true), ConfigSetting("EnableAdhocServer", &g_Config.bEnableAdhocServer, false, true, true), - + ConfigSetting("EnableNetworkChat", &g_Config.bEnableNetworkChat, false, true, true), + ConfigSetting("ChatButtonPosition",&g_Config.iChatButtonPosition,BOTTOM_LEFT,true,true), + ConfigSetting("ChatScreenPosition",&g_Config.iChatScreenPosition,BOTTOM_LEFT,true,true), + ConfigSetting("EnableQuickChat", &g_Config.bEnableQuickChat, true, true, true), + ConfigSetting("QuickChat1", &g_Config.sQuickChat0, "Quick Chat 1", true, true), + ConfigSetting("QuickChat2", &g_Config.sQuickChat1, "Quick Chat 2", true, true), + ConfigSetting("QuickChat3", &g_Config.sQuickChat2, "Quick Chat 3", true, true), + ConfigSetting("QuickChat4", &g_Config.sQuickChat3, "Quick Chat 4", true, true), + ConfigSetting("QuickChat5", &g_Config.sQuickChat4, "Quick Chat 5", true, true), ConfigSetting(false), }; diff --git a/Core/Config.h b/Core/Config.h index a39c6b03cd9e..5a0777b0a359 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -27,6 +27,17 @@ extern const char *PPSSPP_GIT_VERSION; +enum ChatPositions { + BOTTOM_LEFT = 0, + BOTTOM_CENTER = 1, + BOTOM_RIGHT = 2, + TOP_LEFT = 3, + TOP_CENTER = 4, + TOP_RIGHT = 5, + CENTER_LEFT = 6, + CENTER_RIGHT = 7, +}; + namespace http { class Download; class Downloader; @@ -385,6 +396,17 @@ struct Config { bool bEnableAdhocServer; int iWlanAdhocChannel; bool bWlanPowerSave; + bool bEnableNetworkChat; + //for chat position , moveable buttons is better than this + int iChatButtonPosition; + int iChatScreenPosition; + + bool bEnableQuickChat; + std::string sQuickChat0; + std::string sQuickChat1; + std::string sQuickChat2; + std::string sQuickChat3; + std::string sQuickChat4; int iPSPModel; int iFirmwareVersion; diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index 6e92b7305a56..c24ea7a140fa 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -55,6 +55,13 @@ std::recursive_mutex peerlock; SceNetAdhocPdpStat * pdp[255]; SceNetAdhocPtpStat * ptp[255]; uint32_t localip; +std::vector chatLog; +std::string name = ""; +std::string incoming = ""; +std::string message = ""; +bool chatScreenVisible = false; +bool updateChatScreen = false; +int newChat = 0; int isLocalMAC(const SceNetEtherAddr * addr) { SceNetEtherAddr saddr; @@ -981,6 +988,45 @@ void freeFriendsRecursive(SceNetAdhocctlPeerInfo * node) { free(node); } +void sendChat(std::string chatString) { + SceNetAdhocctlChatPacketC2S chat; + auto n = GetI18NCategory("Networking"); + chat.base.opcode = OPCODE_CHAT; + //TODO check network inited, check send success or not, chatlog.pushback error on failed send, pushback error on not connected + if (friendFinderRunning) + { + // Send Chat to Server + if (!chatString.empty()) { + //maximum char allowed is 64 character for compability with original server (pro.coldbird.net) + message = chatString.substr(0, 60); // 64 return chat variable corrupted is it out of memory? + strcpy(chat.message, message.c_str()); + //Send Chat Messages + int chatResult = send(metasocket, (const char *)&chat, sizeof(chat), 0); + NOTICE_LOG(SCENET, "Send Chat %s to Adhoc Server", chat.message); + name = g_Config.sNickName.c_str(); + chatLog.push_back(name.substr(0, 8) + ": " + chat.message); + if (chatScreenVisible) { + updateChatScreen = true; + } + } + } + else { + chatLog.push_back(n->T("You're in Offline Mode, go to lobby or online hall")); + if (chatScreenVisible) { + updateChatScreen = true; + } + } +} + +std::vector getChatLog() { + // this log used by chat screen + if (chatLog.size() > 50) { + //erase the first 40 element limit the chatlog size + chatLog.erase(chatLog.begin(), chatLog.begin() + 40); + } + return chatLog; +} + int friendFinder(){ // Receive Buffer int rxpos = 0; @@ -1024,13 +1070,6 @@ int friendFinder(){ }*/ } - // Send Chat Messages - //while(popFromOutbox(chat.message)) - //{ - // // Send Chat to Server - // sceNetInetSend(metasocket, (const char *)&chat, sizeof(chat), 0); - //} - // Wait for Incoming Data int received = recv(metasocket, (char *)(rx + rxpos), sizeof(rx) - rxpos, 0); @@ -1078,14 +1117,23 @@ int friendFinder(){ if (rxpos >= (int)sizeof(SceNetAdhocctlChatPacketS2C)) { // Cast Packet SceNetAdhocctlChatPacketS2C * packet = (SceNetAdhocctlChatPacketS2C *)rx; - - // Fix for Idiots that try to troll the "ME" Nametag - if (strcasecmp((char *)packet->name.data, "ME") == 0) strcpy((char *)packet->name.data, "NOT ME"); - // Add Incoming Chat to HUD - //printf("Receive chat message %s", packet->base.message); - DEBUG_LOG(SCENET, "Received chat message %s", packet->base.message); - + NOTICE_LOG(SCENET, "Received chat message %s", packet->base.message); + incoming = ""; + name = (char *)packet->name.data; + incoming.append(name.substr(0, 8)); + incoming.append(": "); + incoming.append((char *)packet->base.message); + chatLog.push_back(incoming); + //im new to pointer btw :( doesn't know its safe or not this should update the chat screen when data coming + if (chatScreenVisible) { + updateChatScreen = true; + } + else { + if (newChat < 50) { + newChat += 1; + } + } // Move RX Buffer memmove(rx, rx + sizeof(SceNetAdhocctlChatPacketS2C), sizeof(rx) - sizeof(SceNetAdhocctlChatPacketS2C)); @@ -1107,7 +1155,16 @@ int friendFinder(){ // Add User addFriend(packet); - + incoming = ""; + incoming.append((char *)packet->name.data); + incoming.append(" Joined "); + //do we need ip? + //joined.append((char *)packet->ip); + chatLog.push_back(incoming); + //im new to pointer btw :( doesn't know its safe or not this should update the chat screen when data coming + if (chatScreenVisible) { + updateChatScreen = true; + } // Update HUD User Count #ifdef LOCALHOST_AS_PEER setUserCount(getActivePeerCount()); diff --git a/Core/HLE/proAdhoc.h b/Core/HLE/proAdhoc.h index 9ee5ce84e2fb..d868dd65592f 100644 --- a/Core/HLE/proAdhoc.h +++ b/Core/HLE/proAdhoc.h @@ -847,6 +847,16 @@ SceNetAdhocMatchingMemberInternal* addMember(SceNetAdhocMatchingContext * contex */ void addFriend(SceNetAdhocctlConnectPacketS2C * packet); +/** +* Send chat or get that +* @param std::string ChatString +*/ +void sendChat(std::string chatString); +std::vector getChatLog(); +extern bool chatScreenVisible; +extern bool updateChatScreen; +extern int newChat; + /* * Find a Peer/Friend by MAC address */ diff --git a/UI/ChatScreen.cpp b/UI/ChatScreen.cpp new file mode 100644 index 000000000000..f7873b65a3ca --- /dev/null +++ b/UI/ChatScreen.cpp @@ -0,0 +1,263 @@ +#include "ui/ui_context.h" +#include "ui/view.h" +#include "ui/viewgroup.h" +#include "ui/ui.h" +#include "ChatScreen.h" +#include "Core/Config.h" +#include "Core/Host.h" +#include "Core/System.h" +#include "Common/LogManager.h" +#include "Core/HLE/proAdhoc.h" +#include "i18n/i18n.h" +#include +#include "util/text/utf8.h" + + +void ChatMenu::CreatePopupContents(UI::ViewGroup *parent) { + using namespace UI; + auto n = GetI18NCategory("Networking"); + LinearLayout *outer = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT,400)); + scroll_ = outer->Add(new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0))); + LinearLayout *bottom = outer->Add(new LinearLayout(ORIENT_HORIZONTAL, new LayoutParams(FILL_PARENT, WRAP_CONTENT))); +#if defined(_WIN32) || defined(USING_QT_UI) + chatEdit_ = bottom->Add(new TextEdit("", n->T("Chat Here"), new LinearLayoutParams(1.0))); +#if defined(USING_WIN_UI) + //freeze the ui when using ctrl + C hotkey need workaround + if (g_Config.bBypassOSKWithKeyboard && !g_Config.bFullScreen) + { + std::wstring titleText = ConvertUTF8ToWString(n->T("Chat")); + std::wstring defaultText = ConvertUTF8ToWString(n->T("Chat Here")); + std::wstring inputChars; + if (System_InputBoxGetWString(titleText.c_str(), defaultText, inputChars)) { + //chatEdit_->SetText(ConvertWStringToUTF8(inputChars)); + sendChat(ConvertWStringToUTF8(inputChars)); + } + } +#endif + chatEdit_->OnEnter.Handle(this, &ChatMenu::OnSubmit); +#elif defined(__ANDROID__) + bottom->Add(new Button(n->T("Chat Here"),new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->OnClick.Handle(this, &ChatMenu::OnSubmit); + bottom->Add(new Button(n->T("Send")))->OnClick.Handle(this, &ChatMenu::OnSubmit); +#endif + + if (g_Config.bEnableQuickChat) { + LinearLayout *quickChat = outer->Add(new LinearLayout(ORIENT_HORIZONTAL, new LayoutParams(FILL_PARENT, WRAP_CONTENT))); + quickChat->Add(new Button(n->T("1"), new LinearLayoutParams(1.0)))->OnClick.Handle(this, &ChatMenu::OnQuickChat1); + quickChat->Add(new Button(n->T("2"), new LinearLayoutParams(1.0)))->OnClick.Handle(this, &ChatMenu::OnQuickChat2); + quickChat->Add(new Button(n->T("3"), new LinearLayoutParams(1.0)))->OnClick.Handle(this, &ChatMenu::OnQuickChat3); + quickChat->Add(new Button(n->T("4"), new LinearLayoutParams(1.0)))->OnClick.Handle(this, &ChatMenu::OnQuickChat4); + quickChat->Add(new Button(n->T("5"), new LinearLayoutParams(1.0)))->OnClick.Handle(this, &ChatMenu::OnQuickChat5); + } + chatVert_ = scroll_->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); + chatVert_->SetSpacing(0); + parent->Add(outer); +} + +void ChatMenu::CreateViews() { + using namespace UI; + + auto n = GetI18NCategory("Networking"); + UIContext &dc = *screenManager()->getUIContext(); + + AnchorLayout *anchor = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT)); + anchor->Overflow(false); + root_ = anchor; + + float yres = screenManager()->getUIContext()->GetBounds().h; + + switch (g_Config.iChatScreenPosition) { + // the chat screen size is still static 280x240 need a dynamic size based on device resolution + case 0: + box_ = new LinearLayout(ORIENT_VERTICAL, new AnchorLayoutParams(PopupWidth(), FillVertical() ? yres - 30 : WRAP_CONTENT, 280, NONE, NONE, 240, true)); + break; + case 1: + box_ = new LinearLayout(ORIENT_VERTICAL, new AnchorLayoutParams(PopupWidth(), FillVertical() ? yres - 30 : WRAP_CONTENT, dc.GetBounds().centerX(), NONE, NONE, 240, true)); + break; + case 2: + box_ = new LinearLayout(ORIENT_VERTICAL, new AnchorLayoutParams(PopupWidth(), FillVertical() ? yres - 30 : WRAP_CONTENT, NONE, NONE, 280, 240, true)); + break; + case 3: + box_ = new LinearLayout(ORIENT_VERTICAL, new AnchorLayoutParams(PopupWidth(), FillVertical() ? yres - 30 : WRAP_CONTENT, 280, 240, NONE, NONE, true)); + break; + case 4: + box_ = new LinearLayout(ORIENT_VERTICAL, new AnchorLayoutParams(PopupWidth(), FillVertical() ? yres - 30 : WRAP_CONTENT, dc.GetBounds().centerX(), 240, NONE, NONE, true)); + break; + case 5: + box_ = new LinearLayout(ORIENT_VERTICAL, new AnchorLayoutParams(PopupWidth(), FillVertical() ? yres - 30 : WRAP_CONTENT, NONE, 240, 280, NONE, true)); + break; + } + + root_->Add(box_); + box_->SetBG(UI::Drawable(0x99303030)); + box_->SetHasDropShadow(false); + + View *title = new PopupHeader(n->T("Chat")); + box_->Add(title); + + CreatePopupContents(box_); +#if defined(_WIN32) || defined(USING_QT_UI) + UI::EnableFocusMovement(true); + root_->SetDefaultFocusView(box_); + box_->SubviewFocused(chatEdit_); + root_->SetFocus(); +#else + //root_->SetDefaultFocusView(box_); + //box_->SubviewFocused(scroll_); + //root_->SetFocus(); +#endif + chatScreenVisible = true; + newChat = 0; + + UpdateChat(); +} + +void ChatMenu::dialogFinished(const Screen *dialog, DialogResult result) { + UpdateUIState(UISTATE_INGAME); +} + +UI::EventReturn ChatMenu::OnSubmit(UI::EventParams &e) { +#if defined(_WIN32) || defined(USING_QT_UI) + std::string chat = chatEdit_->GetText(); + chatEdit_->SetText(""); + chatEdit_->SetFocus(); + sendChat(chat); +#elif defined(__ANDROID__) + System_SendMessage("inputbox", "Chat:"); +#endif + return UI::EVENT_DONE; +} + + +UI::EventReturn ChatMenu::OnQuickChat1(UI::EventParams &e) { + sendChat(g_Config.sQuickChat0); + return UI::EVENT_DONE; +} + +UI::EventReturn ChatMenu::OnQuickChat2(UI::EventParams &e) { + sendChat(g_Config.sQuickChat1); + return UI::EVENT_DONE; +} + +UI::EventReturn ChatMenu::OnQuickChat3(UI::EventParams &e) { + sendChat(g_Config.sQuickChat2); + return UI::EVENT_DONE; +} + +UI::EventReturn ChatMenu::OnQuickChat4(UI::EventParams &e) { + sendChat(g_Config.sQuickChat3); + return UI::EVENT_DONE; +} + +UI::EventReturn ChatMenu::OnQuickChat5(UI::EventParams &e) { + sendChat(g_Config.sQuickChat4); + return UI::EVENT_DONE; +} + +/* + maximum chat length in one message from server is only 64 character + need to split the chat to fit the static chat screen size + if the chat screen size become dynamic from device resolution + we need to change split function logic also. +*/ +std::vector Split(const std::string& str) +{ + std::vector ret; + int counter = 0; + int firstSentenceEnd = 0; + int secondSentenceEnd = 0; + int spliton = 45; + + for (int i = 0; i<(int)str.length(); i++) { + if (isspace(str[i])) { + if (i < spliton) { + if(str[i-1]!=':') + firstSentenceEnd = i+1; + } + else if (i > spliton) { + firstSentenceEnd = spliton; + } + } + } + + if (firstSentenceEnd == 0) { + firstSentenceEnd = spliton; + } + ret.push_back(str.substr(0, firstSentenceEnd)); + ret.push_back(str.substr(firstSentenceEnd)); + return ret; +} + +void ChatMenu::UpdateChat() { + using namespace UI; + if (chatVert_ != NULL) { + chatVert_->Clear(); //read Access violation is proadhoc.cpp use NULL_->Clear() pointer? + std::vector chatLog = getChatLog(); + for (auto i : chatLog) { + //split long text + uint32_t namecolor = 0x29B6F6; + uint32_t textcolor = 0xFFFFFF; + uint32_t infocolor = 0xFDD835; + + std::string name = g_Config.sNickName.c_str(); + std::string displayname = i.substr(0, i.find(':')); + std::string chattext = i.substr(displayname.length()); + + if (name.substr(0, 8) == displayname) { + namecolor = 0xE53935; + } + + if (i[displayname.length()] != ':') { + TextView *v = chatVert_->Add(new TextView(i, FLAG_DYNAMIC_ASCII, true)); + v->SetTextColor(0xFF000000 | infocolor); + } + else { + LinearLayout *line = chatVert_->Add(new LinearLayout(ORIENT_HORIZONTAL, new LayoutParams(FILL_PARENT, FILL_PARENT))); + TextView *nameView = line->Add(new TextView(displayname, FLAG_DYNAMIC_ASCII, true)); + nameView->SetTextColor(0xFF000000 | namecolor); + if (chattext.length() > 45) { + std::vector splitted = Split(chattext); + std::string one = splitted[0]; + std::string two = splitted[1]; + TextView *oneview = line->Add(new TextView(one, FLAG_DYNAMIC_ASCII, true)); + oneview->SetTextColor(0xFF000000 | textcolor); + TextView *twoview = chatVert_->Add(new TextView(two, FLAG_DYNAMIC_ASCII, true)); + twoview->SetTextColor(0xFF000000 | textcolor); + } + else { + TextView *chatView = line->Add(new TextView(chattext, FLAG_DYNAMIC_ASCII, true)); + chatView->SetTextColor(0xFF000000 | textcolor); + } + } + } + toBottom_ = true; + } +} + +bool ChatMenu::touch(const TouchInput &touch) { + if (!box_ || (touch.flags & TOUCH_DOWN) == 0 || touch.id != 0) { + return UIDialogScreen::touch(touch); + } + + if (!box_->GetBounds().Contains(touch.x, touch.y)){ + screenManager()->finishDialog(this, DR_BACK); + } + + return UIDialogScreen::touch(touch); +} + +void ChatMenu::update() { + PopupScreen::update(); + if (scroll_ && toBottom_) { + toBottom_ = false; + scroll_->ScrollToBottom(); + } + + if (updateChatScreen) { + UpdateChat(); + updateChatScreen = false; + } +} + +ChatMenu::~ChatMenu() { + chatScreenVisible = false; +} diff --git a/UI/ChatScreen.h b/UI/ChatScreen.h new file mode 100644 index 000000000000..ecb6d1546e3a --- /dev/null +++ b/UI/ChatScreen.h @@ -0,0 +1,27 @@ +#pragma once +#include "file/file_util.h" +#include "ui/ui_screen.h" + +class ChatMenu : public PopupScreen { +public: + ChatMenu() : PopupScreen("Chat") , toBottom_(true) {} + ~ChatMenu(); + void CreatePopupContents(UI::ViewGroup *parent) override; + void CreateViews() override; + void dialogFinished(const Screen *dialog, DialogResult result) override; + bool touch(const TouchInput &touch) override; + void update() override; + void UpdateChat(); + bool toBottom_; +private: + UI::EventReturn OnSubmit(UI::EventParams &e); + UI::EventReturn OnQuickChat1(UI::EventParams &e); + UI::EventReturn OnQuickChat2(UI::EventParams &e); + UI::EventReturn OnQuickChat3(UI::EventParams &e); + UI::EventReturn OnQuickChat4(UI::EventParams &e); + UI::EventReturn OnQuickChat5(UI::EventParams &e); + UI::TextEdit *chatEdit_; + UI::ScrollView *scroll_; + UI::LinearLayout *chatVert_; + UI::ViewGroup *box_; +}; \ No newline at end of file diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 6e7ab1ce5306..8c5abb02b9d8 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -62,6 +62,7 @@ #include "Core/SaveState.h" #include "Core/MIPS/MIPS.h" #include "Core/HLE/__sceAudio.h" +#include "Core/HLE/proAdhoc.h" #include "UI/BackgroundAudio.h" #include "UI/OnScreenDisplay.h" @@ -78,6 +79,7 @@ #include "UI/InstallZipScreen.h" #include "UI/ProfilerDraw.h" #include "UI/DiscordIntegration.h" +#include "UI/ChatScreen.h" #if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) #include "Windows/MainWindow.h" @@ -87,6 +89,8 @@ static AVIDump avi; #endif +UI::ChoiceWithValueDisplay *chatButtons; + static bool frameStep_; static int lastNumFlips; static bool startDumping; @@ -136,6 +140,7 @@ EmuScreen::EmuScreen(const std::string &filename) coreState = CORE_STEPPING; OnDevMenu.Handle(this, &EmuScreen::OnDevTools); + OnChatMenu.Handle(this, &EmuScreen::OnChat); } bool EmuScreen::bootAllowStorage(const std::string &filename) { @@ -438,6 +443,24 @@ void EmuScreen::sendMessage(const char *message, const char *value) { } else { gstate_c.skipDrawReason &= ~SKIPDRAW_WINDOW_MINIMIZED; } + } else if (!strcmp(message, "chat screen")) { + +#if defined(USING_WIN_UI) + //temporary workaround for hotkey its freeze the ui when open chat screen using hotkey and native keyboard is enable + if (g_Config.bBypassOSKWithKeyboard) { + osm.Show("Disable windows native keyboard options to use ctrl + c hotkey", 2.0f); + } else { + if (g_Config.bEnableNetworkChat) { + UI::EventParams e{}; + OnChatMenu.Trigger(e); + } + } +#else + if (g_Config.bEnableNetworkChat) { + UI::EventParams e{}; + OnChatMenu.Trigger(e); + } +#endif } } @@ -528,6 +551,13 @@ void EmuScreen::onVKeyDown(int virtualKeyCode) { } break; + case VIRTKEY_OPENCHAT: + if (g_Config.bEnableNetworkChat) { + UI::EventParams e{}; + OnChatMenu.Trigger(e); + } + break; + case VIRTKEY_AXIS_SWAP: KeyMap::SwapAxis(); break; @@ -976,6 +1006,40 @@ void EmuScreen::CreateViews() { cardboardDisableButton_->OnClick.Handle(this, &EmuScreen::OnDisableCardboard); cardboardDisableButton_->SetVisibility(V_GONE); + if (g_Config.bEnableNetworkChat) { + switch (g_Config.iChatButtonPosition) { + case 0: + chatButtons = new ChoiceWithValueDisplay(&newChat, sc->T("Chat"), new AnchorLayoutParams(130, WRAP_CONTENT, 80, NONE, NONE, 50, true)); + break; + case 1: + chatButtons = new ChoiceWithValueDisplay(&newChat, sc->T("Chat"), new AnchorLayoutParams(130, WRAP_CONTENT, bounds.centerX(), NONE, NONE, 50, true)); + break; + case 2: + chatButtons = new ChoiceWithValueDisplay(&newChat, sc->T("Chat"), new AnchorLayoutParams(130, WRAP_CONTENT, NONE, NONE, 80, 50, true)); + break; + case 3: + chatButtons = new ChoiceWithValueDisplay(&newChat, sc->T("Chat"), new AnchorLayoutParams(130, WRAP_CONTENT, 80, 50, NONE, NONE, true)); + break; + case 4: + chatButtons = new ChoiceWithValueDisplay(&newChat, sc->T("Chat"), new AnchorLayoutParams(130, WRAP_CONTENT, bounds.centerX(), 50, NONE, NONE, true)); + break; + case 5: + chatButtons = new ChoiceWithValueDisplay(&newChat, sc->T("Chat"), new AnchorLayoutParams(130, WRAP_CONTENT, NONE, 50, 80, NONE, true)); + break; + case 6: + chatButtons = new ChoiceWithValueDisplay(&newChat, sc->T("Chat"), new AnchorLayoutParams(130, WRAP_CONTENT, 80, bounds.centerY(), NONE, NONE, true)); + break; + case 7: + chatButtons = new ChoiceWithValueDisplay(&newChat, sc->T("Chat"), new AnchorLayoutParams(130, WRAP_CONTENT, NONE, bounds.centerY(), 80, NONE, true)); + break; + default: + chatButtons = new ChoiceWithValueDisplay(&newChat, sc->T("Chat"), new AnchorLayoutParams(130, WRAP_CONTENT, 80, NONE, NONE, 50, true)); + break; + } + + root_->Add(chatButtons)->OnClick.Handle(this, &EmuScreen::OnChat); + } + saveStatePreview_ = new AsyncImageFileView("", IS_FIXED, nullptr, new AnchorLayoutParams(bounds.centerX(), 100, NONE, NONE, true)); saveStatePreview_->SetFixedSize(160, 90); saveStatePreview_->SetColor(0x90FFFFFF); @@ -1047,7 +1111,14 @@ UI::EventReturn EmuScreen::OnDisableCardboard(UI::EventParams ¶ms) { return UI::EVENT_DONE; } +UI::EventReturn EmuScreen::OnChat(UI::EventParams& params) { + if (chatButtons->GetVisibility() == UI::V_VISIBLE) chatButtons->SetVisibility(UI::V_GONE); + screenManager()->push(new ChatMenu()); + return UI::EVENT_DONE; +} + void EmuScreen::update() { + UIScreen::update(); if (bootPending_) @@ -1126,6 +1197,7 @@ void EmuScreen::update() { } } } + } void EmuScreen::checkPowerDown() { diff --git a/UI/EmuScreen.h b/UI/EmuScreen.h index 6b60e2921884..1c371140a796 100644 --- a/UI/EmuScreen.h +++ b/UI/EmuScreen.h @@ -52,6 +52,7 @@ class EmuScreen : public UIScreen { void CreateViews() override; UI::EventReturn OnDevTools(UI::EventParams ¶ms); UI::EventReturn OnDisableCardboard(UI::EventParams ¶ms); + UI::EventReturn OnChat(UI::EventParams ¶ms); private: void bootGame(const std::string &filename); @@ -71,7 +72,7 @@ class EmuScreen : public UIScreen { void checkPowerDown(); UI::Event OnDevMenu; - + UI::Event OnChatMenu; bool bootPending_; std::string gamePath_; diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 36591580f323..5b1487152223 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -669,6 +669,60 @@ void GameSettingsScreen::CreateViews() { networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.sMACAddress, n->T("Change Mac Address"), (const char *)nullptr))->OnClick.Handle(this, &GameSettingsScreen::OnChangeMacAddress); networkingSettings->Add(new PopupSliderChoice(&g_Config.iPortOffset, 0, 60000, n->T("Port offset", "Port offset(0 = PSP compatibility)"), 100, screenManager())); + + networkingSettings->Add(new ItemHeader(ms->T("Chat"))); + networkingSettings->Add(new CheckBox(&g_Config.bEnableNetworkChat, n->T("Enable network chat", "Enable network chat"))); + static const char *chatButtonPositions[] = { "Bottom Left", "Bottom Center","Bottom Right","Top Left","Top Center", "Top Right","Center Left","Center Right" }; + networkingSettings->Add(new PopupMultiChoice(&g_Config.iChatButtonPosition, n->T("Chat Button Position"), chatButtonPositions, 0, ARRAY_SIZE(chatButtonPositions), "Chat Button Position", screenManager()))->SetEnabledPtr(&g_Config.bEnableNetworkChat); + static const char *chatScreenPositions[] = { "Bottom Left", "Bottom Center","Bottom Right","Top Left","Top Center", "Top Right" }; + networkingSettings->Add(new PopupMultiChoice(&g_Config.iChatScreenPosition, n->T("Chat Screen Position"), chatScreenPositions, 0, ARRAY_SIZE(chatScreenPositions), "Chat Screen Position", screenManager()))->SetEnabledPtr(&g_Config.bEnableNetworkChat); + networkingSettings->Add(new ItemHeader(co->T("QuickChat", "Quick Chat"))); + networkingSettings->Add(new CheckBox(&g_Config.bEnableQuickChat, co->T("QuickChat", "Enable Quick Chat"))); +#if !defined(MOBILE_DEVICE) && !defined(USING_QT_UI) // TODO: Add all platforms where KEY_CHAR support is added + PopupTextInputChoice *qc1 = networkingSettings->Add(new PopupTextInputChoice(&g_Config.sQuickChat0, sy->T("Quick Chat 1"), "", 32, screenManager())); + qc1->SetEnabledPtr(&g_Config.bEnableQuickChat); + PopupTextInputChoice *qc2 = networkingSettings->Add(new PopupTextInputChoice(&g_Config.sQuickChat1, sy->T("Quick Chat 2"), "", 32, screenManager())); + qc2->SetEnabledPtr(&g_Config.bEnableQuickChat); + PopupTextInputChoice *qc3 = networkingSettings->Add(new PopupTextInputChoice(&g_Config.sQuickChat2, sy->T("Quick Chat 3"), "", 32, screenManager())); + qc3->SetEnabledPtr(&g_Config.bEnableQuickChat); + PopupTextInputChoice *qc4 = networkingSettings->Add(new PopupTextInputChoice(&g_Config.sQuickChat3, sy->T("Quick Chat 4"), "", 32, screenManager())); + qc4->SetEnabledPtr(&g_Config.bEnableQuickChat); + PopupTextInputChoice *qc5 = networkingSettings->Add(new PopupTextInputChoice(&g_Config.sQuickChat4, sy->T("Quick Chat 5"), "", 32, screenManager())); + qc5->SetEnabledPtr(&g_Config.bEnableQuickChat); +#elif defined(USING_QT_UI) + Choice *qc1 = networkingSettings->Add(new Choice(sy->T("Quick Chat 1"))); + qc1->SetEnabledPtr(&g_Config.bEnableQuickChat); + qc1->OnClick.Handle(this, &GameSettingsScreen::OnChangeQuickChat0); + Choice *qc2 = networkingSettings->Add(new Choice(sy->T("Quick Chat 2"))); + qc2->SetEnabledPtr(&g_Config.bEnableQuickChat); + qc2->OnClick.Handle(this, &GameSettingsScreen::OnChangeQuickChat1); + Choice *qc3 = networkingSettings->Add(new Choice(sy->T("Quick Chat 3"))); + qc3->SetEnabledPtr(&g_Config.bEnableQuickChat); + qc3->OnClick.Handle(this, &GameSettingsScreen::OnChangeQuickChat2); + Choice *qc4 = networkingSettings->Add(new Choice(sy->T("Quick Chat 4"))); + qc4->SetEnabledPtr(&g_Config.bEnableQuickChat); + qc4->OnClick.Handle(this, &GameSettingsScreen::OnChangeQuickChat3); + Choice *qc5 = networkingSettings->Add(new Choice(sy->T("Quick Chat 5"))); + qc5->SetEnabledPtr(&g_Config.bEnableQuickChat); + qc5->OnClick.Handle(this, &GameSettingsScreen::OnChangeQuickChat4); +#elif defined(__ANDROID__) + ChoiceWithValueDisplay *qc1 = networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.sQuickChat0, sy->T("Quick Chat 1"), (const char *)nullptr)); + qc1->OnClick.Handle(this, &GameSettingsScreen::OnChangeQuickChat0); + qc1->SetEnabledPtr(&g_Config.bEnableQuickChat); + ChoiceWithValueDisplay *qc2 = networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.sQuickChat1, sy->T("Quick Chat 2"), (const char *)nullptr)); + qc2->OnClick.Handle(this, &GameSettingsScreen::OnChangeQuickChat1); + qc2->SetEnabledPtr(&g_Config.bEnableQuickChat); + ChoiceWithValueDisplay *qc3 = networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.sQuickChat2, sy->T("Quick Chat 3"), (const char *)nullptr)); + qc3->OnClick.Handle(this, &GameSettingsScreen::OnChangeQuickChat2); + qc3->SetEnabledPtr(&g_Config.bEnableQuickChat); + ChoiceWithValueDisplay *qc4 = networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.sQuickChat3, sy->T("Quick Chat 4"), (const char *)nullptr)); + qc4->OnClick.Handle(this, &GameSettingsScreen::OnChangeQuickChat3); + qc4->SetEnabledPtr(&g_Config.bEnableQuickChat); + ChoiceWithValueDisplay *qc5 = networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.sQuickChat4, sy->T("Quick Chat 5"), (const char *)nullptr)); + qc5->OnClick.Handle(this, &GameSettingsScreen::OnChangeQuickChat4); + qc5->SetEnabledPtr(&g_Config.bEnableQuickChat); +#endif + ViewGroup *toolsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); toolsScroll->SetTag("GameSettingsTools"); LinearLayout *tools = new LinearLayout(ORIENT_VERTICAL); @@ -1233,6 +1287,86 @@ UI::EventReturn GameSettingsScreen::OnAudioDevice(UI::EventParams &e) { return UI::EVENT_DONE; } +UI::EventReturn GameSettingsScreen::OnChangeQuickChat0(UI::EventParams &e) { +#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) + const size_t chat_len = 64; + + char chat[chat_len]; + memset(chat, 0, sizeof(chat)); + + if (System_InputBoxGetString("Enter Quick Chat 1", g_Config.sQuickChat0.c_str(), chat, chat_len)) { + g_Config.sQuickChat0 = chat; + } +#elif defined(__ANDROID__) + System_SendMessage("inputbox", ("quickchat0:" + g_Config.sQuickChat0).c_str()); +#endif + return UI::EVENT_DONE; +} + +UI::EventReturn GameSettingsScreen::OnChangeQuickChat1(UI::EventParams &e) { +#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) + const size_t chat_len = 64; + + char chat[chat_len]; + memset(chat, 0, sizeof(chat)); + + if (System_InputBoxGetString("Enter Quick Chat 2", g_Config.sQuickChat1.c_str(), chat, chat_len)) { + g_Config.sQuickChat1 = chat; + } +#elif defined(__ANDROID__) + System_SendMessage("inputbox", ("quickchat1:" + g_Config.sQuickChat1).c_str()); +#endif + return UI::EVENT_DONE; +} + +UI::EventReturn GameSettingsScreen::OnChangeQuickChat2(UI::EventParams &e) { +#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) + const size_t chat_len = 64; + + char chat[chat_len]; + memset(chat, 0, sizeof(chat)); + + if (System_InputBoxGetString("Enter Quick Chat 3", g_Config.sQuickChat2.c_str(), chat, chat_len)) { + g_Config.sQuickChat2 = chat; + } +#elif defined(__ANDROID__) + System_SendMessage("inputbox", ("quickchat2:" + g_Config.sQuickChat2).c_str()); +#endif + return UI::EVENT_DONE; +} + +UI::EventReturn GameSettingsScreen::OnChangeQuickChat3(UI::EventParams &e) { +#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) + const size_t chat_len = 64; + + char chat[chat_len]; + memset(chat, 0, sizeof(chat)); + + if (System_InputBoxGetString("Enter Quick Chat 4", g_Config.sQuickChat3.c_str(), chat, chat_len)) { + g_Config.sQuickChat3 = chat; + } +#elif defined(__ANDROID__) + System_SendMessage("inputbox", ("quickchat3:" + g_Config.sQuickChat3).c_str()); +#endif + return UI::EVENT_DONE; +} + +UI::EventReturn GameSettingsScreen::OnChangeQuickChat4(UI::EventParams &e) { +#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) + const size_t chat_len = 64; + + char chat[chat_len]; + memset(chat, 0, sizeof(chat)); + + if (System_InputBoxGetString("Enter Quick Chat 5", g_Config.sQuickChat4.c_str(), chat, chat_len)) { + g_Config.sQuickChat4 = chat; + } +#elif defined(__ANDROID__) + System_SendMessage("inputbox", ("quickchat4:" + g_Config.sQuickChat4).c_str()); +#endif + return UI::EVENT_DONE; +} + UI::EventReturn GameSettingsScreen::OnChangeNickname(UI::EventParams &e) { #if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) const size_t name_len = 256; diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index 7e364cc776b3..6f56b2fbcb08 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -83,6 +83,11 @@ class GameSettingsScreen : public UIDialogScreenWithGameBackground { UI::EventReturn OnPostProcShaderChange(UI::EventParams &e); UI::EventReturn OnDeveloperTools(UI::EventParams &e); UI::EventReturn OnRemoteISO(UI::EventParams &e); + UI::EventReturn OnChangeQuickChat0(UI::EventParams &e); + UI::EventReturn OnChangeQuickChat1(UI::EventParams &e); + UI::EventReturn OnChangeQuickChat2(UI::EventParams &e); + UI::EventReturn OnChangeQuickChat3(UI::EventParams &e); + UI::EventReturn OnChangeQuickChat4(UI::EventParams &e); UI::EventReturn OnChangeNickname(UI::EventParams &e); UI::EventReturn OnChangeproAdhocServerAddress(UI::EventParams &e); UI::EventReturn OnChangeMacAddress(UI::EventParams &e); diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 1df859aa0eca..e47ff1117dea 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -86,6 +86,7 @@ #include "Core/HLE/sceCtrl.h" #include "Core/HLE/sceUsbCam.h" #include "Core/HLE/sceUsbGps.h" +#include "Core/HLE/proAdhoc.h" #include "Core/Util/GameManager.h" #include "Core/Util/AudioFormat.h" #include "Core/WebServer.h" @@ -1103,6 +1104,29 @@ void HandleGlobalMessage(const std::string &msg, const std::string &value) { g_Config.sRemoteISOSubdir = setString; else if (inputboxValue[0] == "remoteiso_server") g_Config.sLastRemoteISOServer = setString; + + if (inputboxValue[0] == "quickchat0") + g_Config.sQuickChat0 = setString; + if (inputboxValue[0] == "quickchat1") + g_Config.sQuickChat1 = setString; + if (inputboxValue[0] == "quickchat2") + g_Config.sQuickChat2 = setString; + if (inputboxValue[0] == "quickchat3") + g_Config.sQuickChat3 = setString; + if (inputboxValue[0] == "quickchat4") + g_Config.sQuickChat4 = setString; + if (inputboxValue[0] == "nickname") + g_Config.sNickName = setString; + if (inputboxValue[0] == "Chat") { + if (inputboxValue.size() > 2) + { + std::string chatString = value; + chatString.erase(0, 5); + sendChat(chatString); + } else { + sendChat(setString); + } + } inputboxValue.clear(); } if (msg == "bgImage_updated") { diff --git a/UI/UI.vcxproj b/UI/UI.vcxproj index 7c7952131c6e..1f69ed9e2700 100644 --- a/UI/UI.vcxproj +++ b/UI/UI.vcxproj @@ -36,6 +36,7 @@ + @@ -68,6 +69,7 @@ + diff --git a/UI/UI.vcxproj.filters b/UI/UI.vcxproj.filters index bd5b259df236..ea4444bb8a10 100644 --- a/UI/UI.vcxproj.filters +++ b/UI/UI.vcxproj.filters @@ -73,6 +73,9 @@ Screens + + Screens + @@ -147,6 +150,9 @@ Screens + + Screens + diff --git a/Windows/MainWindowMenu.cpp b/Windows/MainWindowMenu.cpp index 32861c90f221..7f0a13247f58 100644 --- a/Windows/MainWindowMenu.cpp +++ b/Windows/MainWindowMenu.cpp @@ -329,6 +329,7 @@ namespace MainWindow { TranslateMenuItem(menu, ID_OPTIONS_SHOWFPS); TranslateMenuItem(menu, ID_EMULATION_SOUND); TranslateMenuItem(menu, ID_EMULATION_CHEATS, L"\tCtrl+T"); + TranslateMenuItem(menu, ID_EMULATION_CHAT, L"\tCtrl+C"); // Help menu: it's translated in CreateHelpMenu. CreateHelpMenu(menu); @@ -628,7 +629,11 @@ namespace MainWindow { g_Config.bEnableCheats = !g_Config.bEnableCheats; osm.ShowOnOff(gr->T("Cheats"), g_Config.bEnableCheats); break; - + case ID_EMULATION_CHAT: + if (GetUIState() == UISTATE_INGAME) { + NativeMessageReceived("chat screen", ""); + } + break; case ID_FILE_LOADSTATEFILE: if (W32Util::BrowseForFileName(true, hWnd, L"Load state", 0, L"Save States (*.ppst)\0*.ppst\0All files\0*.*\0\0", L"ppst", fn)) { SetCursor(LoadCursor(0, IDC_WAIT)); diff --git a/Windows/ppsspp.rc b/Windows/ppsspp.rc index ad282605cceb..a09f8e4620c4 100644 --- a/Windows/ppsspp.rc +++ b/Windows/ppsspp.rc @@ -68,6 +68,7 @@ BEGIN "W", ID_EMULATION_STOP, VIRTKEY, CONTROL, NOINVERT "B", ID_EMULATION_RESET, VIRTKEY, CONTROL, NOINVERT "T", ID_EMULATION_CHEATS, VIRTKEY, CONTROL, NOINVERT + "C", ID_EMULATION_CHAT, VIRTKEY, CONTROL, NOINVERT "D", ID_DEBUG_DISASSEMBLY, VIRTKEY, CONTROL, NOINVERT "G", ID_DEBUG_GEDEBUGGER, VIRTKEY, CONTROL, NOINVERT "L", ID_DEBUG_LOG, VIRTKEY, CONTROL, NOINVERT @@ -648,6 +649,7 @@ BEGIN MENUITEM "Enable Sound", ID_EMULATION_SOUND MENUITEM "", 0, MFT_SEPARATOR MENUITEM "Enable Cheats", ID_EMULATION_CHEATS + MENUITEM "Enable Chat", ID_EMULATION_CHAT END POPUP "Help", ID_HELP_MENU diff --git a/Windows/resource.h b/Windows/resource.h index 76fb85046962..3e9cc8ad9368 100644 --- a/Windows/resource.h +++ b/Windows/resource.h @@ -370,6 +370,7 @@ #define IDC_GEDBG_STEPCOUNT_JUMP 40202 #define IDC_GEDBG_STEPCOUNT_COMBO 40203 #define ID_FILE_DUMP_VIDEO_OUTPUT 40204 +#define ID_EMULATION_CHAT 40205 // Dummy option to let the buffered rendering hotkey cycle through all the options. #define ID_OPTIONS_BUFFEREDRENDERINGDUMMY 40500 diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 2021abeeda14..d334324cf82d 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -494,6 +494,7 @@ LOCAL_SRC_FILES := \ $(SRC)/android/jni/native-audio-so.cpp \ $(SRC)/UI/BackgroundAudio.cpp \ $(SRC)/UI/DiscordIntegration.cpp \ + $(SRC)/UI/ChatScreen.cpp \ $(SRC)/UI/DevScreens.cpp \ $(SRC)/UI/DisplayLayoutEditor.cpp \ $(SRC)/UI/DisplayLayoutScreen.cpp \