diff --git a/Source/Android/jni/MainAndroid.cpp b/Source/Android/jni/MainAndroid.cpp index bb148f6578..ebb062ee8e 100644 --- a/Source/Android/jni/MainAndroid.cpp +++ b/Source/Android/jni/MainAndroid.cpp @@ -94,6 +94,10 @@ void Host_NotifyMapLoaded() void Host_RefreshDSPDebuggerWindow() { } +bool Host_UIBlocksControllerState() +{ + return false; +} void Host_Message(HostMessageID id) { diff --git a/Source/Core/Core/Host.h b/Source/Core/Core/Host.h index 3777d375f5..0bea7769d5 100644 --- a/Source/Core/Core/Host.h +++ b/Source/Core/Core/Host.h @@ -33,6 +33,7 @@ enum class HostMessageID }; bool Host_UINeedsControllerState(); +bool Host_UIBlocksControllerState(); bool Host_RendererHasFocus(); bool Host_RendererIsFullscreen(); void Host_Message(HostMessageID id); diff --git a/Source/Core/DolphinNoGUI/MainNoGUI.cpp b/Source/Core/DolphinNoGUI/MainNoGUI.cpp index c5ec03430f..e4096d8501 100644 --- a/Source/Core/DolphinNoGUI/MainNoGUI.cpp +++ b/Source/Core/DolphinNoGUI/MainNoGUI.cpp @@ -52,6 +52,11 @@ void Host_RefreshDSPDebuggerWindow() { } +bool Host_UIBlocksControllerState() +{ + return false; +} + static Common::Event s_update_main_frame_event; void Host_Message(HostMessageID id) { diff --git a/Source/Core/DolphinQt/Host.cpp b/Source/Core/DolphinQt/Host.cpp index c4d3711eda..2cf602495d 100644 --- a/Source/Core/DolphinQt/Host.cpp +++ b/Source/Core/DolphinQt/Host.cpp @@ -8,6 +8,8 @@ #include #include +#include + #include "Common/Common.h" #include "Core/ConfigManager.h" @@ -149,8 +151,15 @@ void Host_RequestRenderWindowSize(int w, int h) bool Host_UINeedsControllerState() { - return Settings::Instance().IsControllerStateNeeded(); + return Settings::Instance().IsControllerStateNeeded() || + (ImGui::GetCurrentContext() && ImGui::GetIO().WantCaptureKeyboard); } + +bool Host_UIBlocksControllerState() +{ + return ImGui::GetCurrentContext() && ImGui::GetIO().WantCaptureKeyboard; +} + void Host_RefreshDSPDebuggerWindow() { } diff --git a/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp b/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp index f400ef37e2..410ce42af1 100644 --- a/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp +++ b/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp @@ -54,6 +54,8 @@ #include "UICommon/GameFile.h" #include "UICommon/UICommon.h" +#include "VideoCommon/NetPlayChatUI.h" +#include "VideoCommon/RenderBase.h" #include "VideoCommon/VideoConfig.h" NetPlayDialog::NetPlayDialog(QWidget* parent) @@ -355,6 +357,15 @@ void NetPlayDialog::ConnectWidgets() connect(m_sync_all_wii_saves_action, &QAction::toggled, this, &NetPlayDialog::SaveSettings); } +void NetPlayDialog::SendMessage(const std::string& msg) +{ + Settings::Instance().GetNetPlayClient()->SendChatMessage(msg); + + DisplayMessage(QStringLiteral("%1: %2").arg(QString::fromStdString(m_nickname).toHtmlEscaped(), + QString::fromStdString(msg).toHtmlEscaped()), + ""); +} + void NetPlayDialog::OnChat() { QueueOnObject(this, [this] { @@ -363,12 +374,9 @@ void NetPlayDialog::OnChat() if (msg.empty()) return; - Settings::Instance().GetNetPlayClient()->SendChatMessage(msg); m_chat_type_edit->clear(); - DisplayMessage(QStringLiteral("%1: %2").arg(QString::fromStdString(m_nickname).toHtmlEscaped(), - QString::fromStdString(msg).toHtmlEscaped()), - "#1d6ed8"); + SendMessage(msg); }); } @@ -762,23 +770,12 @@ void NetPlayDialog::DisplayMessage(const QString& msg, const std::string& color, QStringLiteral("%2").arg(QString::fromStdString(color), msg)); }); + QColor c(color.empty() ? QStringLiteral("white") : QString::fromStdString(color)); + if (g_ActiveConfig.bShowNetPlayMessages && Core::IsRunning()) - { - u32 osd_color; - - // Convert the color string to a OSD color - if (color == "red") - osd_color = OSD::Color::RED; - else if (color == "cyan") - osd_color = OSD::Color::CYAN; - else if (color == "green") - osd_color = OSD::Color::GREEN; - else - osd_color = OSD::Color::YELLOW; - - OSD::AddTypedMessage(OSD::MessageType::NetPlayBuffer, msg.toStdString(), OSD::Duration::NORMAL, - osd_color); - } + g_netplay_chat_ui->AppendChat(msg.toStdString(), + {static_cast(c.redF()), static_cast(c.greenF()), + static_cast(c.blueF())}); } void NetPlayDialog::AppendChat(const std::string& msg) @@ -827,8 +824,12 @@ void NetPlayDialog::OnMsgStartGame() { DisplayMessage(tr("Started game"), "green"); + g_netplay_chat_ui = + std::make_unique([this](const std::string& message) { SendMessage(message); }); + QueueOnObject(this, [this] { auto client = Settings::Instance().GetNetPlayClient(); + if (client) client->StartGame(FindGame(m_current_game)); UpdateDiscordPresence(); @@ -837,6 +838,7 @@ void NetPlayDialog::OnMsgStartGame() void NetPlayDialog::OnMsgStopGame() { + g_netplay_chat_ui.reset(); QueueOnObject(this, [this] { UpdateDiscordPresence(); }); } @@ -856,7 +858,7 @@ void NetPlayDialog::OnPadBufferChanged(u32 buffer) DisplayMessage(m_host_input_authority && !IsHosting() ? tr("Max buffer size changed to %1").arg(buffer) : tr("Buffer size changed to %1").arg(buffer), - ""); + "yellow"); m_buffer_size = static_cast(buffer); } diff --git a/Source/Core/DolphinQt/NetPlay/NetPlayDialog.h b/Source/Core/DolphinQt/NetPlay/NetPlayDialog.h index 38e28cb2d9..49acc0fd42 100644 --- a/Source/Core/DolphinQt/NetPlay/NetPlayDialog.h +++ b/Source/Core/DolphinQt/NetPlay/NetPlayDialog.h @@ -95,6 +95,8 @@ private: void SetGame(const QString& game_path); + void SendMessage(const std::string& message); + // Chat QGroupBox* m_chat_box; QTextEdit* m_chat_edit; diff --git a/Source/Core/DolphinQt/RenderWidget.cpp b/Source/Core/DolphinQt/RenderWidget.cpp index d501370843..ad6d65c252 100644 --- a/Source/Core/DolphinQt/RenderWidget.cpp +++ b/Source/Core/DolphinQt/RenderWidget.cpp @@ -324,7 +324,7 @@ void RenderWidget::SetImGuiKeyMap() {ImGuiKey_Delete, Qt::Key_Delete}, {ImGuiKey_Backspace, Qt::Key_Backspace}, {ImGuiKey_Space, Qt::Key_Space}, - {ImGuiKey_Enter, Qt::Key_Enter}, + {ImGuiKey_Enter, Qt::Key_Return}, {ImGuiKey_Escape, Qt::Key_Escape}, {ImGuiKey_A, Qt::Key_A}, {ImGuiKey_C, Qt::Key_C}, diff --git a/Source/Core/DolphinQt/Settings.cpp b/Source/Core/DolphinQt/Settings.cpp index d8abe9b723..61be8ff8c0 100644 --- a/Source/Core/DolphinQt/Settings.cpp +++ b/Source/Core/DolphinQt/Settings.cpp @@ -27,6 +27,9 @@ #include "InputCommon/ControllerInterface/ControllerInterface.h" #include "InputCommon/InputConfig.h" +#include "VideoCommon/NetPlayChatUI.h" +#include "VideoCommon/RenderBase.h" + Settings::Settings() { qRegisterMetaType(); @@ -293,6 +296,8 @@ std::shared_ptr Settings::GetNetPlayClient() void Settings::ResetNetPlayClient(NetPlay::NetPlayClient* client) { m_client.reset(client); + + g_netplay_chat_ui.reset(); } std::shared_ptr Settings::GetNetPlayServer() diff --git a/Source/Core/InputCommon/ControlReference/ControlReference.cpp b/Source/Core/InputCommon/ControlReference/ControlReference.cpp index 1f392c4e3a..cfc6fa658e 100644 --- a/Source/Core/InputCommon/ControlReference/ControlReference.cpp +++ b/Source/Core/InputCommon/ControlReference/ControlReference.cpp @@ -14,8 +14,9 @@ using namespace ciface::ExpressionParser; bool ControlReference::InputGateOn() { - return SConfig::GetInstance().m_BackgroundInput || Host_RendererHasFocus() || - Host_UINeedsControllerState(); + return (SConfig::GetInstance().m_BackgroundInput || Host_RendererHasFocus() || + Host_UINeedsControllerState()) && + !Host_UIBlocksControllerState(); } // diff --git a/Source/Core/VideoCommon/CMakeLists.txt b/Source/Core/VideoCommon/CMakeLists.txt index b118080eb5..459fbcb7aa 100644 --- a/Source/Core/VideoCommon/CMakeLists.txt +++ b/Source/Core/VideoCommon/CMakeLists.txt @@ -22,6 +22,7 @@ add_library(videocommon ImageWrite.cpp IndexGenerator.cpp LightingShaderGen.cpp + NetPlayChatUI.cpp OnScreenDisplay.cpp OpcodeDecoding.cpp PerfQueryBase.cpp diff --git a/Source/Core/VideoCommon/NetPlayChatUI.cpp b/Source/Core/VideoCommon/NetPlayChatUI.cpp new file mode 100644 index 0000000000..2388d94b91 --- /dev/null +++ b/Source/Core/VideoCommon/NetPlayChatUI.cpp @@ -0,0 +1,99 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "VideoCommon/NetPlayChatUI.h" + +#include + +constexpr float DEFAULT_WINDOW_WIDTH = 220.0f; +constexpr float DEFAULT_WINDOW_HEIGHT = 400.0f; + +constexpr size_t MAX_BACKLOG_SIZE = 100; + +std::unique_ptr g_netplay_chat_ui; + +NetPlayChatUI::NetPlayChatUI(std::function callback) +{ + m_message_callback = std::move(callback); +} + +void NetPlayChatUI::Display() +{ + const float scale = ImGui::GetIO().DisplayFramebufferScale.x; + + ImGui::SetNextWindowPos(ImVec2(10.0f * scale, 10.0f * scale), ImGuiCond_FirstUseEver); + ImGui::SetNextWindowSizeConstraints( + ImVec2(DEFAULT_WINDOW_WIDTH * scale, DEFAULT_WINDOW_HEIGHT * scale), + ImGui::GetIO().DisplaySize); + + if (!ImGui::Begin("Chat", nullptr, ImGuiWindowFlags_None)) + { + ImGui::End(); + return; + } + + ImGui::BeginChild("Scrolling", ImVec2(0, -30 * scale), true, ImGuiWindowFlags_None); + for (const auto& msg : m_messages) + { + auto c = msg.second; + ImGui::PushTextWrapPos(0.0f); + ImGui::TextColored(ImVec4(c[0], c[1], c[2], 1.0f), "%s", msg.first.c_str()); + ImGui::PopTextWrapPos(); + } + + if (m_scroll_to_bottom) + { + ImGui::SetScrollHere(1.0f); + m_scroll_to_bottom = false; + } + + m_is_scrolled_to_bottom = ImGui::GetScrollY() == ImGui::GetScrollMaxY(); + + ImGui::EndChild(); + + ImGui::Spacing(); + + ImGui::PushItemWidth(-50.0f * scale); + + if (ImGui::InputText("", m_message_buf, IM_ARRAYSIZE(m_message_buf), + ImGuiInputTextFlags_EnterReturnsTrue)) + { + SendMessage(); + ImGui::SetKeyboardFocusHere(-1); + } + + ImGui::PopItemWidth(); + + ImGui::SameLine(); + + if (ImGui::Button("Send")) + SendMessage(); + + ImGui::End(); +} + +void NetPlayChatUI::AppendChat(const std::string& message, NetPlayChatUI::Color color) +{ + if (m_messages.size() > MAX_BACKLOG_SIZE) + m_messages.pop_front(); + + m_messages.push_back({message, color}); + + // Only scroll to bottom, if we were at the bottom previously + if (m_is_scrolled_to_bottom) + m_scroll_to_bottom = true; +} + +void NetPlayChatUI::SendMessage() +{ + // Check whether the input field is empty + if (m_message_buf[0] != '\0') + { + if (m_message_callback) + m_message_callback(m_message_buf); + + // 'Empty' the buffer + m_message_buf[0] = '\0'; + } +} diff --git a/Source/Core/VideoCommon/NetPlayChatUI.h b/Source/Core/VideoCommon/NetPlayChatUI.h new file mode 100644 index 0000000000..c355aa462d --- /dev/null +++ b/Source/Core/VideoCommon/NetPlayChatUI.h @@ -0,0 +1,35 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include +#include +#include + +class NetPlayChatUI +{ +public: + explicit NetPlayChatUI(std::function callback); + ~NetPlayChatUI() = default; + + using Color = std::array; + + void Display(); + void AppendChat(const std::string& message, Color color); + void SendMessage(); + +private: + char m_message_buf[256] = {}; + bool m_scroll_to_bottom = false; + bool m_is_scrolled_to_bottom = true; + + std::deque> m_messages; + std::function m_message_callback; +}; + +extern std::unique_ptr g_netplay_chat_ui; diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 74760f57e9..1a15f0aec0 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -57,6 +57,7 @@ #include "VideoCommon/FPSCounter.h" #include "VideoCommon/FramebufferManager.h" #include "VideoCommon/ImageWrite.h" +#include "VideoCommon/NetPlayChatUI.h" #include "VideoCommon/OnScreenDisplay.h" #include "VideoCommon/PixelEngine.h" #include "VideoCommon/PixelShaderManager.h" @@ -523,6 +524,9 @@ void Renderer::DrawDebugText() if (g_ActiveConfig.bOverlayStats) Statistics::Display(); + if (g_ActiveConfig.bShowNetPlayMessages && g_netplay_chat_ui) + g_netplay_chat_ui->Display(); + if (g_ActiveConfig.bOverlayProjStats) Statistics::DisplayProj(); } @@ -979,8 +983,7 @@ bool Renderer::InitializeImGui() pconfig.vertex_format = m_imgui_vertex_format.get(); pconfig.vertex_shader = vertex_shader.get(); pconfig.pixel_shader = pixel_shader.get(); - pconfig.rasterization_state = - RenderState::GetCullBackFaceRasterizationState(PrimitiveType::Triangles); + pconfig.rasterization_state = RenderState::GetNoCullRasterizationState(PrimitiveType::Triangles); pconfig.depth_state = RenderState::GetNoDepthTestingDepthState(); pconfig.blending_state = RenderState::GetNoBlendingBlendState(); pconfig.blending_state.blendenable = true; diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index deaaa89aa9..6ee03a9156 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -41,6 +41,7 @@ class AbstractShader; class AbstractTexture; class AbstractStagingTexture; class NativeVertexFormat; +class NetPlayChatUI; struct TextureConfig; struct ComputePipelineConfig; struct AbstractPipelineConfig; @@ -384,6 +385,8 @@ private: // Ensures all encoded frames have been written to the output file. void FinishFrameData(); + + std::unique_ptr m_netplay_chat_ui; }; extern std::unique_ptr g_renderer; diff --git a/Source/Core/VideoCommon/VideoCommon.vcxproj b/Source/Core/VideoCommon/VideoCommon.vcxproj index f4ab78c032..d18f7f444e 100644 --- a/Source/Core/VideoCommon/VideoCommon.vcxproj +++ b/Source/Core/VideoCommon/VideoCommon.vcxproj @@ -57,6 +57,7 @@ + @@ -120,6 +121,7 @@ + diff --git a/Source/Core/VideoCommon/VideoCommon.vcxproj.filters b/Source/Core/VideoCommon/VideoCommon.vcxproj.filters index f804839f54..c1ecc02dbd 100644 --- a/Source/Core/VideoCommon/VideoCommon.vcxproj.filters +++ b/Source/Core/VideoCommon/VideoCommon.vcxproj.filters @@ -197,6 +197,9 @@ Base + + Util + @@ -386,6 +389,9 @@ Shader Generators + + Util + diff --git a/Source/UnitTests/StubHost.cpp b/Source/UnitTests/StubHost.cpp index 62a332e59b..fd062be732 100644 --- a/Source/UnitTests/StubHost.cpp +++ b/Source/UnitTests/StubHost.cpp @@ -35,6 +35,10 @@ bool Host_UINeedsControllerState() { return false; } +bool Host_UIBlocksControllerState() +{ + return false; +} bool Host_RendererHasFocus() { return false;