diff options
author | Clément Gallet <clement.gallet@ens-lyon.org> | 2024-01-03 16:59:18 +0100 |
---|---|---|
committer | Clément Gallet <clement.gallet@ens-lyon.org> | 2024-01-03 16:59:18 +0100 |
commit | 1dd0c90b8dd6be9461d3200ce9a095014472e588 (patch) | |
tree | c72c1e4a4642343f286ca4f20e49f35cf39c9ade | |
parent | bd30571f24fbbb63c555eca267db1ddb7861ba1a (diff) |
Add ImGui main menuimgui
25 files changed, 944 insertions, 599 deletions
diff --git a/src/library/Makefile.am b/src/library/Makefile.am index 2157f895..64296cd8 100644 --- a/src/library/Makefile.am +++ b/src/library/Makefile.am @@ -95,7 +95,13 @@ libtas_so_SOURCES = \ inputs/xkeyboardlayout.cpp \ inputs/xinput.cpp \ inputs/xpointer.cpp \ + renderhud/Crosshair.cpp \ + renderhud/FrameWindow.cpp \ + renderhud/InputsWindow.cpp \ renderhud/LogWindow.cpp \ + renderhud/LuaDraw.cpp \ + renderhud/MessageWindow.cpp \ + renderhud/WatchesWindow.cpp \ renderhud/RenderHUD_GL.cpp \ renderhud/RenderHUD_SDL2_renderer.cpp \ renderhud/RenderHUD_Vulkan.cpp \ diff --git a/src/library/checkpoint/SaveStateManager.cpp b/src/library/checkpoint/SaveStateManager.cpp index c7e4ba49..84474a2c 100644 --- a/src/library/checkpoint/SaveStateManager.cpp +++ b/src/library/checkpoint/SaveStateManager.cpp @@ -37,7 +37,7 @@ #include "audio/AudioPlayerCoreAudio.h" #endif #include "fileio/FileHandleList.h" -#include "renderhud/RenderHUD.h" +#include "renderhud/MessageWindow.h" #ifdef __unix__ #include "xlib/xdisplay.h" // x11::gameDisplays #endif @@ -668,7 +668,7 @@ void SaveStateManager::printError(int err) if (err < 0) { debuglogstdio(LCF_CHECKPOINT | LCF_ERROR, errors[-err]); - RenderHUD::insertMessage(errors[-err]); + MessageWindow::insert(errors[-err]); } } diff --git a/src/library/frame.cpp b/src/library/frame.cpp index b2249890..1821af1d 100644 --- a/src/library/frame.cpp +++ b/src/library/frame.cpp @@ -41,6 +41,10 @@ #include "sdl/sdlwindows.h" #include "sdl/sdlevents.h" #include "sdl/SDLEventQueue.h" +#include "renderhud/FrameWindow.h" +#include "renderhud/LuaDraw.h" +#include "renderhud/MessageWindow.h" +#include "renderhud/WatchesWindow.h" #ifdef __unix__ #include "xlib/xevents.h" @@ -343,8 +347,8 @@ void frameBoundary(std::function<void()> draw, RenderHUD& hud) sendMessage(MSGB_START_FRAMEBOUNDARY); /* Reset ramwatches and lua drawings */ - RenderHUD::resetWatches(); - RenderHUD::resetLua(); + WatchesWindow::reset(); + LuaDraw::reset(); /* Receive messages from the program */ perfTimer.switchTimer(PerfTimer::WaitTimer); @@ -356,7 +360,7 @@ void frameBoundary(std::function<void()> draw, RenderHUD& hud) { /* Get ramwatch from the program */ std::string ramwatch = receiveString(); - RenderHUD::insertWatch(ramwatch); + WatchesWindow::insert(ramwatch); break; } case MSGN_LUA_RESOLUTION: @@ -376,7 +380,7 @@ void frameBoundary(std::function<void()> draw, RenderHUD& hud) std::string text = receiveString(); uint32_t color; receiveData(&color, sizeof(uint32_t)); - RenderHUD::insertLuaText(x, y, text, color); + LuaDraw::insertText(x, y, text, color); break; } case MSGN_LUA_PIXEL: @@ -386,7 +390,7 @@ void frameBoundary(std::function<void()> draw, RenderHUD& hud) receiveData(&y, sizeof(int)); uint32_t color; receiveData(&color, sizeof(uint32_t)); - RenderHUD::insertLuaPixel(x, y, color); + LuaDraw::insertPixel(x, y, color); break; } case MSGN_LUA_RECT: @@ -400,7 +404,7 @@ void frameBoundary(std::function<void()> draw, RenderHUD& hud) uint32_t color; receiveData(&color, sizeof(uint32_t)); receiveData(&filled, sizeof(int)); - RenderHUD::insertLuaRect(x, y, w, h, thickness, color, filled); + LuaDraw::insertRect(x, y, w, h, thickness, color, filled); break; } case MSGN_LUA_LINE: @@ -412,7 +416,7 @@ void frameBoundary(std::function<void()> draw, RenderHUD& hud) receiveData(&y1, sizeof(int)); uint32_t color; receiveData(&color, sizeof(uint32_t)); - RenderHUD::insertLuaLine(x0, y0, x1, y1, color); + LuaDraw::insertLine(x0, y0, x1, y1, color); break; } case MSGN_LUA_ELLIPSE: @@ -424,7 +428,7 @@ void frameBoundary(std::function<void()> draw, RenderHUD& hud) receiveData(&radius_y, sizeof(int)); uint32_t color; receiveData(&color, sizeof(uint32_t)); - RenderHUD::insertLuaEllipse(center_x, center_y, radius_x, radius_y, color); + LuaDraw::insertEllipse(center_x, center_y, radius_x, radius_y, color); break; } } @@ -443,11 +447,17 @@ void frameBoundary(std::function<void()> draw, RenderHUD& hud) /* If we want HUD to appear in encodes, we need to draw it before saving * the window surface/texture/etc. This has the small drawback that we * won't be able to remove HUD messages during that frame. */ - if (!Global::skipping_draw && draw && Global::shared_config.osd_encode) { - AllInputs preview_ai; - preview_ai.buildAndClear(); - hud.drawAll(framecount, nondraw_framecount, ai, preview_ai); - hud.render(); + if (!Global::skipping_draw && Global::shared_config.osd_encode) { + if (draw) { + AllInputs preview_ai; + preview_ai.buildAndClear(); + hud.drawAll(framecount, nondraw_framecount, ai, preview_ai); + hud.render(); + } + else { + /* We must indicate Dear ImGui that the frame ends without drawing */ + hud.endFrame(); + } } if (!Global::skipping_draw) { @@ -479,11 +489,17 @@ void frameBoundary(std::function<void()> draw, RenderHUD& hud) } } - if (!Global::skipping_draw && draw && !Global::shared_config.osd_encode) { - AllInputs preview_ai; - preview_ai.buildAndClear(); - hud.drawAll(framecount, nondraw_framecount, ai, preview_ai); - hud.render(); + if (!Global::skipping_draw && !Global::shared_config.osd_encode) { + if (draw) { + AllInputs preview_ai; + preview_ai.buildAndClear(); + hud.drawAll(framecount, nondraw_framecount, ai, preview_ai); + hud.render(); + } + else { + /* We must indicate Dear ImGui that the frame ends without drawing */ + hud.endFrame(); + } } /* Actual draw command */ @@ -659,7 +675,7 @@ static void receive_messages(std::function<void()> draw, RenderHUD& hud) std::string msg = "State "; msg += std::to_string(slot); msg += " saved"; - RenderHUD::insertMessage(msg.c_str()); + MessageWindow::insert(msg.c_str()); screen_redraw(draw, hud, preview_ai); } @@ -701,7 +717,7 @@ static void receive_messages(std::function<void()> draw, RenderHUD& hud) std::string msg = "State "; msg += std::to_string(slot); msg += " saved"; - RenderHUD::insertMessage(msg.c_str()); + MessageWindow::insert(msg.c_str()); screen_redraw(draw, hud, preview_ai); } perfTimer.switchTimer(PerfTimer::FrameTimer); @@ -813,14 +829,12 @@ static void receive_messages(std::function<void()> draw, RenderHUD& hud) /* Print the successful message, unless we are saving in a fork */ if (!(Global::shared_config.savestate_settings & SharedConfig::SS_FORK)) { - if (Global::shared_config.osd & SharedConfig::OSD_MESSAGES) { - std::string msg; - msg = "State "; - msg += std::to_string(slot); - msg += " saved"; - RenderHUD::insertMessage(msg.c_str()); - screen_redraw(draw, hud, preview_ai); - } + std::string msg; + msg = "State "; + msg += std::to_string(slot); + msg += " saved"; + MessageWindow::insert(msg.c_str()); + screen_redraw(draw, hud, preview_ai); } } @@ -855,7 +869,7 @@ static void receive_messages(std::function<void()> draw, RenderHUD& hud) break; case MSGN_OSD_MSG: - RenderHUD::insertMessage(receiveString().c_str()); + MessageWindow::insert(receiveString().c_str()); screen_redraw(draw, hud, preview_ai); break; @@ -863,7 +877,7 @@ static void receive_messages(std::function<void()> draw, RenderHUD& hud) { /* Get marker text from the program */ std::string text = receiveString(); - RenderHUD::setMarkerText(text); + FrameWindow::setMarkerText(text); screen_redraw(draw, hud, preview_ai); break; } diff --git a/src/library/renderhud/Crosshair.cpp b/src/library/renderhud/Crosshair.cpp new file mode 100644 index 00000000..e08b6aee --- /dev/null +++ b/src/library/renderhud/Crosshair.cpp @@ -0,0 +1,33 @@ +/* + Copyright 2015-2023 Clément Gallet <clement.gallet@ens-lyon.org> + + This file is part of libTAS. + + libTAS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + libTAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with libTAS. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "Crosshair.h" + +#include "../external/imgui/imgui.h" + +namespace libtas { + +void Crosshair::draw(const AllInputs& ai) +{ + int size = 5; + ImGui::GetBackgroundDrawList()->AddLine(ImVec2(ai.pointer_x, ai.pointer_y-size), ImVec2(ai.pointer_x, ai.pointer_y+size), IM_COL32(255, 255, 255, 255)); + ImGui::GetBackgroundDrawList()->AddLine(ImVec2(ai.pointer_x-size, ai.pointer_y), ImVec2(ai.pointer_x+size, ai.pointer_y+size), IM_COL32(255, 255, 255, 255)); +} + +} diff --git a/src/library/renderhud/Crosshair.h b/src/library/renderhud/Crosshair.h new file mode 100644 index 00000000..9996a05f --- /dev/null +++ b/src/library/renderhud/Crosshair.h @@ -0,0 +1,34 @@ +/* + Copyright 2015-2023 Clément Gallet <clement.gallet@ens-lyon.org> + + This file is part of libTAS. + + libTAS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + libTAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with libTAS. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef LIBTAS_IMGUI_CROSSHAIR_H_INCL +#define LIBTAS_IMGUI_CROSSHAIR_H_INCL + +#include "../shared/inputs/AllInputs.h" + +namespace libtas { + +namespace Crosshair +{ + void draw(const AllInputs& ai); +} + +} + +#endif diff --git a/src/library/renderhud/FrameWindow.cpp b/src/library/renderhud/FrameWindow.cpp new file mode 100644 index 00000000..8a26dafc --- /dev/null +++ b/src/library/renderhud/FrameWindow.cpp @@ -0,0 +1,67 @@ +/* + Copyright 2015-2023 Clément Gallet <clement.gallet@ens-lyon.org> + + This file is part of libTAS. + + libTAS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + libTAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with libTAS. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "FrameWindow.h" + +#include "global.h" // Global::shared_config +#include "../external/imgui/imgui.h" + +#include <sstream> + +namespace libtas { + +/* Marker text to print on screen */ +static std::string marker; + +void FrameWindow::setMarkerText(std::string text) +{ + marker = text; +} + +void FrameWindow::draw(uint64_t framecount, uint64_t nondraw_framecount, bool* p_open = nullptr) +{ + std::string framestr = std::to_string(framecount); + switch (Global::shared_config.recording) { + case SharedConfig::RECORDING_READ: + framestr.append("/").append(std::to_string(Global::shared_config.movie_framecount)); + if (framecount > Global::shared_config.movie_framecount) + framestr.append(" (Finished)"); + break; + case SharedConfig::RECORDING_WRITE: + case SharedConfig::NO_RECORDING: + default: + break; + } + + ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav; + ImGui::SetNextWindowPos(ImGui::GetMainViewport()->WorkPos, ImGuiCond_Always, ImVec2(0.0f, 0.0f)); + if (ImGui::Begin("Framecount", p_open, window_flags)) + { + ImGui::Text(framestr.c_str()); + if (nondraw_framecount > 0) { + std::string nondraw_framestr = std::to_string(nondraw_framecount); + ImGui::TextColored(ImVec4(0.6f, 0.0f, 0.0f, 1.0f), nondraw_framestr.c_str()); + } + if (!marker.empty()) + ImGui::Text(marker.c_str()); + } + ImGui::End(); +} + +} diff --git a/src/library/renderhud/FrameWindow.h b/src/library/renderhud/FrameWindow.h new file mode 100644 index 00000000..2a3be1d9 --- /dev/null +++ b/src/library/renderhud/FrameWindow.h @@ -0,0 +1,36 @@ +/* + Copyright 2015-2023 Clément Gallet <clement.gallet@ens-lyon.org> + + This file is part of libTAS. + + libTAS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + libTAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with libTAS. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef LIBTAS_IMGUI_FRAMEWINDOW_H_INCL +#define LIBTAS_IMGUI_FRAMEWINDOW_H_INCL + +#include <string> + +namespace libtas { + +namespace FrameWindow +{ + void setMarkerText(std::string text); + + void draw(uint64_t framecount, uint64_t nondraw_framecount, bool* p_open); +} + +} + +#endif diff --git a/src/library/renderhud/InputsWindow.cpp b/src/library/renderhud/InputsWindow.cpp new file mode 100644 index 00000000..c0c8f77b --- /dev/null +++ b/src/library/renderhud/InputsWindow.cpp @@ -0,0 +1,123 @@ +/* + Copyright 2015-2023 Clément Gallet <clement.gallet@ens-lyon.org> + + This file is part of libTAS. + + libTAS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + libTAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with libTAS. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "InputsWindow.h" + +#include "global.h" // Global::shared_config +#include "../external/keysymdesc.h" +#include "../external/imgui/imgui.h" + +#include <sstream> + +namespace libtas { + +void InputsWindow::draw(const AllInputs& ai, const AllInputs& preview_ai, bool* p_open = nullptr) +{ + std::string inputs_str = formatInputs(ai); + std::string preview_inputs_str = formatInputs(preview_ai); + + if (!inputs_str.empty() || !preview_inputs_str.empty()) { + ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav; + + const ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + ImGui::SetNextWindowPos(ImVec2(main_viewport->WorkPos.x, main_viewport->WorkPos.y + main_viewport->WorkSize.y), ImGuiCond_Always, ImVec2(0.0f, 1.0f)); + if (ImGui::Begin("Inputs", nullptr, window_flags)) + { + if (!preview_inputs_str.empty()) { + ImGui::TextColored(ImVec4(0.6f, 0.6f, 0.6f, 1.0f), preview_inputs_str.c_str()); + } + if (!inputs_str.empty()) { + ImGui::Text(inputs_str.c_str()); + } + } + ImGui::End(); + } +} + +std::string InputsWindow::formatInputs(const AllInputs& ai) +{ + std::ostringstream oss; + + /* Flags */ + if (ai.flags & (1 << SingleInput::FLAG_RESTART)) { + oss << "[Restart] "; + } + for (int i=0; i<4; i++) { + if (ai.flags & (1 << (SingleInput::FLAG_CONTROLLER1_ADDED_REMOVED+i))) { + oss << "[J" << i << " added/removed] "; + } + } + if (ai.flags & (1 << SingleInput::FLAG_FOCUS_UNFOCUS)) { + oss << "[Un/Focus] "; + } + + /* Keyboard */ +#ifdef __unix__ + for (int i=0; i<AllInputs::MAXKEYS; i++) { + if (ai.keyboard[i]) { + const char* str = KEYSYM_TO_DESC(ai.keyboard[i]); + if (str) + oss << "[K " << str << "] "; + else + oss << "[K ???] "; + } + } +#else + /* TODO! */ +#endif + + /* Mouse */ + if (Global::shared_config.mouse_support) { + if (ai.pointer_x != -1) { + oss << "[M " << ai.pointer_x << ":" << ai.pointer_y << ":"; + oss << ((ai.pointer_mode==SingleInput::POINTER_MODE_RELATIVE)?"R":"A"); + oss << "] "; + } + if (ai.pointer_mask & (1 << SingleInput::POINTER_B1)) + oss << "[M b1] "; + if (ai.pointer_mask & (1 << SingleInput::POINTER_B2)) + oss << "[M b2] "; + if (ai.pointer_mask & (1 << SingleInput::POINTER_B3)) + oss << "[M b3] "; + if (ai.pointer_mask & (1 << SingleInput::POINTER_B4)) + oss << "[M b4] "; + if (ai.pointer_mask & (1 << SingleInput::POINTER_B5)) + oss << "[M b5] "; + } + + /* Joystick */ + for (int i=0; i<Global::shared_config.nb_controllers; i++) { + if (!ai.controllers[i]) + continue; + + for (int j=0; j<ControllerInputs::MAXAXES; j++) { + if (ai.controllers[i]->axes[j] != 0) + oss << "[J" << i << " a" << j << ":" << ai.controllers[i]->axes[j] << "] "; + } + + for (int j=0; j<16; j++) { + if (ai.controllers[i]->buttons & (1 << j)) + oss << "[J" << i << " b" << j << "] "; + } + } + + return oss.str(); +} + +} diff --git a/src/library/renderhud/InputsWindow.h b/src/library/renderhud/InputsWindow.h new file mode 100644 index 00000000..fdb55d5c --- /dev/null +++ b/src/library/renderhud/InputsWindow.h @@ -0,0 +1,38 @@ +/* + Copyright 2015-2023 Clément Gallet <clement.gallet@ens-lyon.org> + + This file is part of libTAS. + + libTAS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + libTAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with libTAS. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef LIBTAS_IMGUI_INPUTSWINDOW_H_INCL +#define LIBTAS_IMGUI_INPUTSWINDOW_H_INCL + +#include "../shared/inputs/AllInputs.h" +#include <string> + +namespace libtas { + +namespace InputsWindow +{ + void draw(const AllInputs& ai, const AllInputs& preview_ai, bool* p_open); + + std::string formatInputs(const AllInputs& ai); + +} + +} + +#endif diff --git a/src/library/renderhud/LogWindow.cpp b/src/library/renderhud/LogWindow.cpp index 6791d708..88265f0f 100644 --- a/src/library/renderhud/LogWindow.cpp +++ b/src/library/renderhud/LogWindow.cpp @@ -19,16 +19,8 @@ #include "LogWindow.h" -// #include "logging.h" -// #include "hook.h" -// #include "global.h" // Global::shared_config -// #include "screencapture/ScreenCapture.h" -// #include "GlobalState.h" -// #include "../external/keysymdesc.h" #include "../external/imgui/imgui.h" -// #include <sstream> - namespace libtas { static ImGuiTextBuffer buffer; diff --git a/src/library/renderhud/LogWindow.h b/src/library/renderhud/LogWindow.h index 7880fbd2..7f87099f 100644 --- a/src/library/renderhud/LogWindow.h +++ b/src/library/renderhud/LogWindow.h @@ -17,21 +17,8 @@ along with libTAS. If not, see <http://www.gnu.org/licenses/>. */ -// #ifdef __unix__ -// #include "config.h" -// #endif - -#ifndef LIBTAS_LOGWINDOW_H_INCL -#define LIBTAS_LOGWINDOW_H_INCL - -// #include "TimeHolder.h" -// #include "../shared/inputs/AllInputs.h" - -// #include <memory> -// #include <list> -// #include <utility> -// #include <string> -// #include <stdint.h> +#ifndef LIBTAS_IMGUI_LOGWINDOW_H_INCL +#define LIBTAS_IMGUI_LOGWINDOW_H_INCL namespace libtas { diff --git a/src/library/renderhud/LuaDraw.cpp b/src/library/renderhud/LuaDraw.cpp new file mode 100644 index 00000000..067c7e8c --- /dev/null +++ b/src/library/renderhud/LuaDraw.cpp @@ -0,0 +1,144 @@ +/* + Copyright 2015-2023 Clément Gallet <clement.gallet@ens-lyon.org> + + This file is part of libTAS. + + libTAS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + libTAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with libTAS. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "LuaDraw.h" + +#include "global.h" // Global::shared_config +#include "../external/imgui/imgui.h" +#include "TimeHolder.h" + +#include <list> +#include <utility> +#include <string> +#include <memory> + +namespace libtas { + +static std::list<std::unique_ptr<LuaDraw::LuaShape>> lua_shapes; + +void LuaDraw::LuaText::render() +{ + ImGui::GetBackgroundDrawList()->AddText(ImVec2(x, y), color, text.c_str()); +} + +void LuaDraw::LuaPixel::render() +{ + ImGui::GetBackgroundDrawList()->AddLine(ImVec2(x, y), ImVec2(x, y), color); +} + +void LuaDraw::LuaRect::render() +{ + if (filled) + ImGui::GetBackgroundDrawList()->AddRectFilled(ImVec2(x, y), ImVec2(x+w, y+h), color); + else + ImGui::GetBackgroundDrawList()->AddRect(ImVec2(x, y), ImVec2(x+w, y+h), color, 0.0f, 0, thickness); +} + +void LuaDraw::LuaLine::render() +{ + ImGui::GetBackgroundDrawList()->AddLine(ImVec2(x0, y0), ImVec2(x1, y1), color); +} + +void LuaDraw::LuaEllipse::render() +{ + ImGui::GetBackgroundDrawList()->AddEllipse(ImVec2(center_x, center_y), radius_x, radius_y, color); +} + +void LuaDraw::insertText(int x, int y, std::string text, uint32_t color) +{ + auto lt = new LuaText(); + lt->x = x; + lt->y = y; + lt->text = text; + lt->color = IM_COL32(static_cast<uint8_t>((color >> 16) & 0xff), + static_cast<uint8_t>((color >> 8) & 0xff), + static_cast<uint8_t>(color & 0xff), + static_cast<uint8_t>((color >> 24) & 0xff)); + lua_shapes.emplace_back(lt); +} + +void LuaDraw::insertPixel(int x, int y, uint32_t color) +{ + auto lp = new LuaPixel; + lp->x = x; + lp->y = y; + lp->color = IM_COL32(static_cast<uint8_t>((color >> 16) & 0xff), + static_cast<uint8_t>((color >> 8) & 0xff), + static_cast<uint8_t>(color & 0xff), + static_cast<uint8_t>((color >> 24) & 0xff)); + lua_shapes.emplace_back(lp); +} + +void LuaDraw::insertRect(int x, int y, int w, int h, int thickness, uint32_t color, int filled) +{ + auto lr = new LuaRect(); + lr->x = x; + lr->y = y; + lr->w = w; + lr->h = h; + lr->thickness = thickness; + lr->color = IM_COL32(static_cast<uint8_t>((color >> 16) & 0xff), + static_cast<uint8_t>((color >> 8) & 0xff), + static_cast<uint8_t>(color & 0xff), + static_cast<uint8_t>((color >> 24) & 0xff)); + lr->filled = filled; + lua_shapes.emplace_back(lr); +} + +void LuaDraw::insertLine(int x0, int y0, int x1, int y1, uint32_t color) +{ + auto ll = new LuaLine(); + ll->x0 = x0; + ll->y0 = y0; + ll->x1 = x1; + ll->y1 = y1; + ll->color = IM_COL32(static_cast<uint8_t>((color >> 16) & 0xff), + static_cast<uint8_t>((color >> 8) & 0xff), + static_cast<uint8_t>(color & 0xff), + static_cast<uint8_t>((color >> 24) & 0xff)); + lua_shapes.emplace_back(ll); +} + +void LuaDraw::insertEllipse(int center_x, int center_y, int radius_x, int radius_y, uint32_t color) +{ + auto le = new LuaEllipse(); + le->center_x = center_x; + le->center_y = center_y; + le->radius_x = radius_x; + le->radius_y = radius_y; + le->color = IM_COL32(static_cast<uint8_t>((color >> 16) & 0xff), + static_cast<uint8_t>((color >> 8) & 0xff), + static_cast<uint8_t>(color & 0xff), + static_cast<uint8_t>((color >> 24) & 0xff)); + lua_shapes.emplace_back(le); +} + +void LuaDraw::draw() +{ + for (auto &shape : lua_shapes) { + shape->render(); + } +} + +void LuaDraw::reset() +{ + lua_shapes.clear(); +} + +} diff --git a/src/library/renderhud/LuaDraw.h b/src/library/renderhud/LuaDraw.h new file mode 100644 index 00000000..2fe9f846 --- /dev/null +++ b/src/library/renderhud/LuaDraw.h @@ -0,0 +1,108 @@ +/* + Copyright 2015-2023 Clément Gallet <clement.gallet@ens-lyon.org> + + This file is part of libTAS. + + libTAS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + libTAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with libTAS. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef LIBTAS_IMGUI_LUADRAW_H_INCL +#define LIBTAS_IMGUI_LUADRAW_H_INCL + +#include <string> + +namespace libtas { + +namespace LuaDraw +{ + +struct LuaShape +{ + virtual void render() = 0; +}; + +struct LuaText : public LuaShape +{ + std::string text; + uint32_t color; + int x; + int y; + void render() override; +}; + +struct LuaPixel : public LuaShape +{ + int x; + int y; + uint32_t color; + void render() override; +}; + +struct LuaRect : public LuaShape +{ + int x; + int y; + int w; + int h; + int thickness; + uint32_t color; + int filled; + void render() override; +}; + +struct LuaLine : public LuaShape +{ + int x0; + int y0; + int x1; + int y1; + uint32_t color; + void render() override; +}; + +struct LuaEllipse : public LuaShape +{ + int center_x; + int center_y; + int radius_x; + int radius_y; + uint32_t color; + void render() override; +}; + +/* Insert a lua text to be displayed */ +void insertText(int x, int y, std::string text, uint32_t color); + +/* Insert a lua pixel to be displayed */ +void insertPixel(int x, int y, uint32_t color); + +/* Insert a lua rect to be displayed */ +void insertRect(int x, int y, int w, int h, int thickness, uint32_t color, int filled); + +/* Insert a lua line to be displayed */ +void insertLine(int x0, int y0, int x1, int y1, uint32_t color); + +/* Insert a lua line to be displayed */ +void insertEllipse(int center_x, int center_y, int radius_x, int radius_y, uint32_t color); + +/* Clear all lua drawings */ +void reset(); + +void draw(); + +} + +} + +#endif diff --git a/src/library/renderhud/MessageWindow.cpp b/src/library/renderhud/MessageWindow.cpp new file mode 100644 index 00000000..5f35b700 --- /dev/null +++ b/src/library/renderhud/MessageWindow.cpp @@ -0,0 +1,82 @@ +/* + Copyright 2015-2023 Clément Gallet <clement.gallet@ens-lyon.org> + + This file is part of libTAS. + + libTAS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + libTAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with libTAS. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "MessageWindow.h" + +#include "global.h" // Global::shared_config +#include "GlobalState.h" +#include "../external/imgui/imgui.h" +#include "TimeHolder.h" + +#include <list> +#include <utility> +#include <string> + +namespace libtas { + +/* Messages to print on screen with the creation time */ +static std::list<std::pair<std::string, TimeHolder>> messages; + +void MessageWindow::insert(const char* message) +{ + /* Get current time */ + TimeHolder current_time; + NATIVECALL(clock_gettime(CLOCK_MONOTONIC, ¤t_time)); + + messages.push_back(std::make_pair(std::string(message), current_time)); +} + +void MessageWindow::draw() +{ + if (messages.empty()) return; + + TimeHolder message_timeout; + message_timeout = {2, 0}; + + /* Get current time */ + TimeHolder current_time; + NATIVECALL(clock_gettime(CLOCK_MONOTONIC, ¤t_time)); + + ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav; + + const ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + ImGui::SetNextWindowPos(ImVec2(main_viewport->WorkPos.x + main_viewport->WorkSize.x, main_viewport->WorkPos.y + main_viewport->WorkSize.y), ImGuiCond_Always, ImVec2(1.0f, 1.0f)); + + if (ImGui::Begin("Messages", nullptr, window_flags)) { + auto iter = messages.begin(); + while (iter != messages.end()) { + /* Check if we must remove the message */ + if ((current_time - iter->second) > message_timeout) { + iter = messages.erase(iter); + } + else { + ImGui::Text(iter->first.c_str()); + iter++; + } + } + } + ImGui::End(); +} + +void MessageWindow::clear() +{ + messages.clear(); +} + +} diff --git a/src/library/renderhud/MessageWindow.h b/src/library/renderhud/MessageWindow.h new file mode 100644 index 00000000..6f1bba69 --- /dev/null +++ b/src/library/renderhud/MessageWindow.h @@ -0,0 +1,39 @@ +/* + Copyright 2015-2023 Clément Gallet <clement.gallet@ens-lyon.org> + + This file is part of libTAS. + + libTAS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + libTAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with libTAS. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef LIBTAS_IMGUI_MESSAGEWINDOW_H_INCL +#define LIBTAS_IMGUI_MESSAGEWINDOW_H_INCL + +#include <string> + +namespace libtas { + +namespace MessageWindow +{ + void draw(); + + void insert(const char* message); + + void clear(); + +} + +} + +#endif diff --git a/src/library/renderhud/RenderHUD.cpp b/src/library/renderhud/RenderHUD.cpp index f360cef9..1035a0ee 100644 --- a/src/library/renderhud/RenderHUD.cpp +++ b/src/library/renderhud/RenderHUD.cpp @@ -18,16 +18,18 @@ */ #include "RenderHUD.h" +#include "Crosshair.h" +#include "FrameWindow.h" +#include "InputsWindow.h" +#include "LogWindow.h" +#include "LuaDraw.h" +#include "MessageWindow.h" +#include "WatchesWindow.h" -#include "logging.h" -#include "hook.h" -#include "global.h" // Global::shared_config -#include "screencapture/ScreenCapture.h" #include "GlobalState.h" -#include "LogWindow.h" +#include "global.h" // Global::shared_config #include "xlib/xdisplay.h" // x11::gameDisplays #include "xlib/xwindows.h" // x11::gameXWindows -#include "../external/keysymdesc.h" #include "../external/imgui/imgui.h" #include "../external/imgui/imgui_impl_xlib.h" @@ -35,323 +37,6 @@ namespace libtas { -std::list<std::pair<std::string, TimeHolder>> RenderHUD::messages; -std::list<std::string> RenderHUD::watches; -std::list<std::unique_ptr<RenderHUD::LuaShape>> RenderHUD::lua_shapes; -std::string RenderHUD::marker; - -void RenderHUD::LuaText::render(RenderHUD *hud) -{ - ImGui::GetBackgroundDrawList()->AddText(ImVec2(x, y), color, text.c_str()); -} - -void RenderHUD::LuaPixel::render(RenderHUD *hud) -{ - ImGui::GetBackgroundDrawList()->AddLine(ImVec2(x, y), ImVec2(x, y), color); -} - -void RenderHUD::LuaRect::render(RenderHUD *hud) -{ - if (filled) - ImGui::GetBackgroundDrawList()->AddRectFilled(ImVec2(x, y), ImVec2(x+w, y+h), color); - else - ImGui::GetBackgroundDrawList()->AddRect(ImVec2(x, y), ImVec2(x+w, y+h), color, 0.0f, 0, thickness); -} - -void RenderHUD::LuaLine::render(RenderHUD *hud) -{ - ImGui::GetBackgroundDrawList()->AddLine(ImVec2(x0, y0), ImVec2(x1, y1), color); -} - -void RenderHUD::LuaEllipse::render(RenderHUD *hud) -{ - ImGui::GetBackgroundDrawList()->AddEllipse(ImVec2(center_x, center_y), radius_x, radius_y, color); -} - -void RenderHUD::drawFrame(uint64_t framecount, uint64_t nondraw_framecount) -{ - std::string framestr = std::to_string(framecount); - switch (Global::shared_config.recording) { - case SharedConfig::RECORDING_READ: - framestr.append("/").append(std::to_string(Global::shared_config.movie_framecount)); - if (framecount > Global::shared_config.movie_framecount) - framestr.append(" (Finished)"); - break; - case SharedConfig::RECORDING_WRITE: - case SharedConfig::NO_RECORDING: - default: - break; - } - - ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav; - ImGui::SetNextWindowPos(ImGui::GetMainViewport()->WorkPos, ImGuiCond_Once, ImVec2(0.0f, 0.0f)); - if (ImGui::Begin("Framecount", nullptr, window_flags)) - { - ImGui::Text(framestr.c_str()); - if (nondraw_framecount > 0) { - std::string nondraw_framestr = std::to_string(nondraw_framecount); - ImGui::TextColored(ImVec4(0.6f, 0.0f, 0.0f, 1.0f), nondraw_framestr.c_str()); - } - if (!marker.empty()) - ImGui::Text(marker.c_str()); - } - ImGui::End(); -} - -void RenderHUD::drawInputs(const AllInputs& ai, const AllInputs& preview_ai) -{ - std::string inputs_str = formatInputs(ai); - std::string preview_inputs_str = formatInputs(preview_ai); - - if (!inputs_str.empty() || !preview_inputs_str.empty()) { - ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav; - - const ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - ImGui::SetNextWindowPos(ImVec2(main_viewport->WorkPos.x, main_viewport->WorkPos.y + main_viewport->WorkSize.y), ImGuiCond_Always, ImVec2(0.0f, 1.0f)); - if (ImGui::Begin("Inputs", nullptr, window_flags)) - { - if (!preview_inputs_str.empty()) { - ImGui::TextColored(ImVec4(0.6f, 0.6f, 0.6f, 1.0f), preview_inputs_str.c_str()); - } - if (!inputs_str.empty()) { - ImGui::Text(inputs_str.c_str()); - } - } - ImGui::End(); - } -} - -std::string RenderHUD::formatInputs(const AllInputs& ai) -{ - std::ostringstream oss; - - /* Flags */ - if (ai.flags & (1 << SingleInput::FLAG_RESTART)) { - oss << "[Restart] "; - } - for (int i=0; i<4; i++) { - if (ai.flags & (1 << (SingleInput::FLAG_CONTROLLER1_ADDED_REMOVED+i))) { - oss << "[J" << i << " added/removed] "; - } - } - if (ai.flags & (1 << SingleInput::FLAG_FOCUS_UNFOCUS)) { - oss << "[Un/Focus] "; - } - - /* Keyboard */ -#ifdef __unix__ - for (int i=0; i<AllInputs::MAXKEYS; i++) { - if (ai.keyboard[i]) { - const char* str = KEYSYM_TO_DESC(ai.keyboard[i]); - if (str) - oss << "[K " << str << "] "; - else - oss << "[K ???] "; - } - } -#else - /* TODO! */ -#endif - - /* Mouse */ - if (Global::shared_config.mouse_support) { - if (ai.pointer_x != -1) { - oss << "[M " << ai.pointer_x << ":" << ai.pointer_y << ":"; - oss << ((ai.pointer_mode==SingleInput::POINTER_MODE_RELATIVE)?"R":"A"); - oss << "] "; - } - if (ai.pointer_mask & (1 << SingleInput::POINTER_B1)) - oss << "[M b1] "; - if (ai.pointer_mask & (1 << SingleInput::POINTER_B2)) - oss << "[M b2] "; - if (ai.pointer_mask & (1 << SingleInput::POINTER_B3)) - oss << "[M b3] "; - if (ai.pointer_mask & (1 << SingleInput::POINTER_B4)) - oss << "[M b4] "; - if (ai.pointer_mask & (1 << SingleInput::POINTER_B5)) - oss << "[M b5] "; - } - - /* Joystick */ - for (int i=0; i<Global::shared_config.nb_controllers; i++) { - if (!ai.controllers[i]) - continue; - - for (int j=0; j<ControllerInputs::MAXAXES; j++) { - if (ai.controllers[i]->axes[j] != 0) - oss << "[J" << i << " a" << j << ":" << ai.controllers[i]->axes[j] << "] "; - } - - for (int j=0; j<16; j++) { - if (ai.controllers[i]->buttons & (1 << j)) - oss << "[J" << i << " b" << j << "] "; - } - } - - return oss.str(); -} - -void RenderHUD::insertMessage(const char* message) -{ - /* Get current time */ - TimeHolder current_time; - NATIVECALL(clock_gettime(CLOCK_MONOTONIC, ¤t_time)); - - messages.push_back(std::make_pair(std::string(message), current_time)); -} - -void RenderHUD::drawMessages() -{ - if (messages.empty()) return; - - TimeHolder message_timeout; - message_timeout = {2, 0}; - - /* Get current time */ - TimeHolder current_time; - NATIVECALL(clock_gettime(CLOCK_MONOTONIC, ¤t_time)); - - ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav; - - const ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - ImGui::SetNextWindowPos(ImVec2(main_viewport->WorkPos.x + main_viewport->WorkSize.x, main_viewport->WorkPos.y + main_viewport->WorkSize.y), ImGuiCond_Always, ImVec2(1.0f, 1.0f)); - - if (ImGui::Begin("Messages", nullptr, window_flags)) { - auto iter = messages.begin(); - while (iter != messages.end()) { - /* Check if we must remove the message */ - if ((current_time - iter->second) > message_timeout) { - iter = messages.erase(iter); - } - else { - ImGui::Text(iter->first.c_str()); - iter++; - } - } - } - ImGui::End(); -} - -void RenderHUD::insertWatch(std::string watch) -{ - watches.push_back(watch); -} - -void RenderHUD::resetWatches() -{ - watches.clear(); -} - -void RenderHUD::drawWatches() -{ - if (watches.empty()) return; - - ImGuiWindowFlags window_flags = ImGuiWindowFlags_AlwaysAutoResize; - - const ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - ImGui::SetNextWindowPos(ImVec2(main_viewport->WorkPos.x + main_viewport->WorkSize.x, main_viewport->WorkPos.y), ImGuiCond_Once, ImVec2(1.0f, 0.0f)); - - if (ImGui::Begin("RAM Watches", nullptr, window_flags)) { - for (auto iter = watches.begin(); iter != watches.end(); iter++) { - ImGui::Text(iter->c_str()); - } - } - ImGui::End(); -} - -void RenderHUD::drawLua() -{ - for (auto &shape : lua_shapes) { - shape->render(this); - } -} - -void RenderHUD::setMarkerText(std::string text) -{ - marker = text; -} - -void RenderHUD::drawCrosshair(const AllInputs& ai) -{ - int size = 5; - ImGui::GetBackgroundDrawList()->AddLine(ImVec2(ai.pointer_x, ai.pointer_y-size), ImVec2(ai.pointer_x, ai.pointer_y+size), IM_COL32(255, 255, 255, 255)); - ImGui::GetBackgroundDrawList()->AddLine(ImVec2(ai.pointer_x-size, ai.pointer_y), ImVec2(ai.pointer_x+size, ai.pointer_y+size), IM_COL32(255, 255, 255, 255)); -} - -void RenderHUD::insertLuaText(int x, int y, std::string text, uint32_t color) -{ - auto lt = new LuaText(); - lt->x = x; - lt->y = y; - lt->text = text; - lt->color = IM_COL32(static_cast<uint8_t>((color >> 16) & 0xff), - static_cast<uint8_t>((color >> 8) & 0xff), - static_cast<uint8_t>(color & 0xff), - static_cast<uint8_t>((color >> 24) & 0xff)); - lua_shapes.emplace_back(lt); -} - -void RenderHUD::insertLuaPixel(int x, int y, uint32_t color) -{ - auto lp = new LuaPixel; - lp->x = x; - lp->y = y; - lp->color = IM_COL32(static_cast<uint8_t>((color >> 16) & 0xff), - static_cast<uint8_t>((color >> 8) & 0xff), - static_cast<uint8_t>(color & 0xff), - static_cast<uint8_t>((color >> 24) & 0xff)); - lua_shapes.emplace_back(lp); -} - -void RenderHUD::insertLuaRect(int x, int y, int w, int h, int thickness, uint32_t color, int filled) -{ - auto lr = new LuaRect(); - lr->x = x; - lr->y = y; - lr->w = w; - lr->h = h; - lr->thickness = thickness; - lr->color = IM_COL32(static_cast<uint8_t>((color >> 16) & 0xff), - static_cast<uint8_t>((color >> 8) & 0xff), - static_cast<uint8_t>(color & 0xff), - static_cast<uint8_t>((color >> 24) & 0xff)); - lr->filled = filled; - lua_shapes.emplace_back(lr); -} - -void RenderHUD::insertLuaLine(int x0, int y0, int x1, int y1, uint32_t color) -{ - auto ll = new LuaLine(); - ll->x0 = x0; - ll->y0 = y0; - ll->x1 = x1; - ll->y1 = y1; - ll->color = IM_COL32(static_cast<uint8_t>((color >> 16) & 0xff), - static_cast<uint8_t>((color >> 8) & 0xff), - static_cast<uint8_t>(color & 0xff), - static_cast<uint8_t>((color >> 24) & 0xff)); - lua_shapes.emplace_back(ll); -} - -void RenderHUD::insertLuaEllipse(int center_x, int center_y, int radius_x, int radius_y, uint32_t color) -{ - auto le = new LuaEllipse(); - le->center_x = center_x; - le->center_y = center_y; - le->radius_x = radius_x; - le->radius_y = radius_y; - le->color = IM_COL32(static_cast<uint8_t>((color >> 16) & 0xff), - static_cast<uint8_t>((color >> 8) & 0xff), - static_cast<uint8_t>(color & 0xff), - static_cast<uint8_t>((color >> 24) & 0xff)); - lua_shapes.emplace_back(le); -} - -void RenderHUD::resetLua() -{ - lua_shapes.clear(); -} - bool RenderHUD::init() { if (!ImGui::GetCurrentContext()) { @@ -371,7 +56,6 @@ bool RenderHUD::init() return false; } - void RenderHUD::newFrame() { if (!ImGui::GetCurrentContext()) @@ -382,33 +66,74 @@ void RenderHUD::newFrame() ImGui::NewFrame(); } +void RenderHUD::endFrame() +{ + if (!ImGui::GetCurrentContext()) + return; + + ImGui::EndFrame(); +} + void RenderHUD::drawAll(uint64_t framecount, uint64_t nondraw_framecount, const AllInputs& ai, const AllInputs& preview_ai) { if (!ImGui::GetCurrentContext()) { return; } - if (Global::shared_config.osd & SharedConfig::OSD_FRAMECOUNT) - drawFrame(framecount, nondraw_framecount); - - if (Global::shared_config.osd & SharedConfig::OSD_INPUTS) - drawInputs(ai, preview_ai); + static bool show_framecount = true; + static bool show_inputs = true; + static bool show_messages = true; + static bool show_watches = true; + static bool show_lua = true; + static bool show_crosshair = false; + static bool show_log = false; + static bool show_demo = false; + + if (Global::shared_config.osd) { + if (ImGui::BeginMainMenuBar()) { + if (ImGui::BeginMenu("Display")) { + ImGui::MenuItem("Frame", nullptr, &show_framecount); + ImGui::MenuItem("Inputs", nullptr, &show_inputs); + ImGui::MenuItem("Messages", nullptr, &show_messages); + ImGui::MenuItem("Watches", nullptr, &show_watches); + ImGui::MenuItem("Lua", nullptr, &show_lua); + ImGui::MenuItem("Crosshair", nullptr, &show_crosshair); + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Debug")) { + ImGui::MenuItem("Log", nullptr, &show_log); + ImGui::MenuItem("Demo", nullptr, &show_demo); + ImGui::EndMenu(); + } + ImGui::EndMainMenuBar(); + } + } + + if (show_framecount) + FrameWindow::draw(framecount, nondraw_framecount, &show_framecount); - if (Global::shared_config.osd & SharedConfig::OSD_MESSAGES) - drawMessages(); + if (show_inputs) + InputsWindow::draw(ai, preview_ai, &show_inputs); + + if (show_messages) + MessageWindow::draw(); + else + MessageWindow::clear(); - if (Global::shared_config.osd & SharedConfig::OSD_RAMWATCHES) - drawWatches(); + if (show_watches) + WatchesWindow::draw(&show_watches); - if (Global::shared_config.osd & SharedConfig::OSD_LUA) - drawLua(); + if (show_lua) + LuaDraw::draw(); - if (Global::shared_config.osd & SharedConfig::OSD_CROSSHAIR) - drawCrosshair(ai); + if (show_crosshair) + Crosshair::draw(ai); - ImGui::ShowDemoWindow(); + if (show_log) + LogWindow::draw(&show_log); - LogWindow::draw(nullptr); + if (show_demo) + ImGui::ShowDemoWindow(&show_demo); } } diff --git a/src/library/renderhud/RenderHUD.h b/src/library/renderhud/RenderHUD.h index c1c5537c..acb2d3d4 100644 --- a/src/library/renderhud/RenderHUD.h +++ b/src/library/renderhud/RenderHUD.h @@ -24,7 +24,6 @@ #ifndef LIBTAS_RENDERHUD_H_INCL #define LIBTAS_RENDERHUD_H_INCL -#include "TimeHolder.h" #include "../shared/inputs/AllInputs.h" #include <memory> @@ -51,140 +50,15 @@ class RenderHUD public: virtual void newFrame(); + void endFrame(); + virtual void render() {} /* Display everything based on setting */ void drawAll(uint64_t framecount, uint64_t nondraw_framecount, const AllInputs& ai, const AllInputs& preview_ai); - /* Insert a message to be displayed */ - static void insertMessage(const char* message); - - /* Insert a ram watch to be displayed */ - static void insertWatch(std::string watch); - - /* Set marker text to be displayed */ - static void setMarkerText(std::string text); - - /* Clear the list of watches */ - static void resetWatches(); - - /* Insert a lua text to be displayed */ - static void insertLuaText(int x, int y, std::string text, uint32_t color); - - /* Insert a lua pixel to be displayed */ - static void insertLuaPixel(int x, int y, uint32_t color); - - /* Insert a lua rect to be displayed */ - static void insertLuaRect(int x, int y, int w, int h, int thickness, uint32_t color, int filled); - - /* Insert a lua line to be displayed */ - static void insertLuaLine(int x0, int y0, int x1, int y1, uint32_t color); - - /* Insert a lua line to be displayed */ - static void insertLuaEllipse(int center_x, int center_y, int radius_x, int radius_y, uint32_t color); - - /* Clear all lua drawings */ - static void resetLua(); - protected: bool init(); - - private: - /*** Draw specific information on screen ***/ - - /* Display the frame count on screen */ - void drawFrame(uint64_t framecount, uint64_t nondraw_framecount); - - /* Display inputs and preview inputs on screen */ - void drawInputs(const AllInputs& ai, const AllInputs& preview_ai); - - /* Generic function to format inputs */ - std::string formatInputs(const AllInputs& ai); - - /* Display messages */ - void drawMessages(); - - /* Display ram watches */ - void drawWatches(); - - /* Display lua drawings */ - void drawLua(); - - /* Display marker text */ - void drawMarkers(); - - /* Display crosshair on current pointer position */ - void drawCrosshair(const AllInputs& ai); - - /* Update cursor */ - virtual void updateCursor() {} - - /* Messages to print on screen with the creation time */ - static std::list<std::pair<std::string, TimeHolder>> messages; - - /* Ram watches to print on screen */ - static std::list<std::string> watches; - - /* Marker text to print on screen */ - static std::string marker; - - struct LuaShape - { - virtual void render(RenderHUD *hud) = 0; - }; - - struct LuaText : public LuaShape - { - std::string text; - uint32_t color; - int x; - int y; - void render(RenderHUD *hud) override; - }; - - struct LuaPixel : public LuaShape - { - int x; - int y; - uint32_t color; - void render(RenderHUD *hud) override; - }; - - struct LuaRect : public LuaShape - { - int x; - int y; - int w; - int h; - int thickness; - uint32_t color; - int filled; - void render(RenderHUD *hud) override; - }; - - struct LuaLine : public LuaShape - { - int x0; - int y0; - int x1; - int y1; - uint32_t color; - void render(RenderHUD *hud) override; - }; - - struct LuaEllipse : public LuaShape - { - int center_x; - int center_y; - int radius_x; - int radius_y; - uint32_t color; - void render(RenderHUD *hud) override; - }; - - /* Lua shapes to print on screen */ - static std::list<std::unique_ptr<LuaShape>> lua_shapes; - }; } diff --git a/src/library/renderhud/WatchesWindow.cpp b/src/library/renderhud/WatchesWindow.cpp new file mode 100644 index 00000000..aaad24eb --- /dev/null +++ b/src/library/renderhud/WatchesWindow.cpp @@ -0,0 +1,61 @@ +/* + Copyright 2015-2023 Clément Gallet <clement.gallet@ens-lyon.org> + + This file is part of libTAS. + + libTAS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + libTAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with libTAS. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "WatchesWindow.h" + +#include "global.h" // Global::shared_config +#include "TimeHolder.h" +#include "../external/imgui/imgui.h" + +#include <list> +#include <string> + +namespace libtas { + +/* Ram watches to print on screen */ +static std::list<std::string> watches; + +void WatchesWindow::insert(std::string watch) +{ + watches.push_back(watch); +} + +void WatchesWindow::reset() +{ + watches.clear(); +} + +void WatchesWindow::draw(bool* p_open = nullptr) +{ + if (watches.empty()) return; + + ImGuiWindowFlags window_flags = ImGuiWindowFlags_AlwaysAutoResize; + + const ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + ImGui::SetNextWindowPos(ImVec2(main_viewport->WorkPos.x + main_viewport->WorkSize.x, main_viewport->WorkPos.y), ImGuiCond_Once, ImVec2(1.0f, 0.0f)); + + if (ImGui::Begin("RAM Watches", p_open, window_flags)) { + for (auto iter = watches.begin(); iter != watches.end(); iter++) { + ImGui::Text(iter->c_str()); + } + } + ImGui::End(); +} + +} diff --git a/src/library/renderhud/WatchesWindow.h b/src/library/renderhud/WatchesWindow.h new file mode 100644 index 00000000..3caee6a0 --- /dev/null +++ b/src/library/renderhud/WatchesWindow.h @@ -0,0 +1,38 @@ +/* + Copyright 2015-2023 Clément Gallet <clement.gallet@ens-lyon.org> + + This file is part of libTAS. + + libTAS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + libTAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with libTAS. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef LIBTAS_IMGUI_WATCHESWINDOW_H_INCL +#define LIBTAS_IMGUI_WATCHESWINDOW_H_INCL + +#include <string> + +namespace libtas { + +namespace WatchesWindow +{ + void insert(std::string watch); + + void reset(); + + void draw(bool* p_open); +} + +} + +#endif diff --git a/src/program/GameEvents.cpp b/src/program/GameEvents.cpp index 2e15f36a..6c338a4e 100644 --- a/src/program/GameEvents.cpp +++ b/src/program/GameEvents.cpp @@ -191,7 +191,7 @@ bool GameEvents::processEvent(GameEvents::EventType type, struct HotKey &hk) /* Handle errors */ if (error == SaveState::EINVALID) { - if (!(context->config.sc.osd & SharedConfig::OSD_MESSAGES)) + if (!(context->config.sc.osd)) emit alertToShow(QString("State invalid because new threads were created")); return false; } @@ -232,7 +232,7 @@ bool GameEvents::processEvent(GameEvents::EventType type, struct HotKey &hk) } if (error == SaveState::ENOSTATE) { - if (!(context->config.sc.osd & SharedConfig::OSD_MESSAGES)) + if (!(context->config.sc.osd)) emit alertToShow(QString("There is no savestate to load in this slot")); return false; } @@ -243,7 +243,7 @@ bool GameEvents::processEvent(GameEvents::EventType type, struct HotKey &hk) } if (error == SaveState::EINPUTMISMATCH) { - if (!(context->config.sc.osd & SharedConfig::OSD_MESSAGES)) { + if (!(context->config.sc.osd)) { emit alertToShow(QString("Trying to load a state in read-only but the inputs mismatch")); } return false; diff --git a/src/program/GameLoop.cpp b/src/program/GameLoop.cpp index 1c97f27d..36b3ad88 100644 --- a/src/program/GameLoop.cpp +++ b/src/program/GameLoop.cpp @@ -562,7 +562,7 @@ bool GameLoop::startFrameMessages() movie.editor->setDraw(context->draw_frame); /* Send ram watches */ - if (context->config.sc.osd & SharedConfig::OSD_RAMWATCHES) { + if (context->config.sc.osd) { std::string ramwatch; emit getRamWatch(ramwatch); while(!ramwatch.empty()) { @@ -588,7 +588,7 @@ void GameLoop::sleepSendPreview() /* Send marker text if it has changed */ static std::string old_marker_text; - if (context->config.sc.osd & SharedConfig::OSD_MARKERS) { + if (context->config.sc.osd) { std::string text; emit getMarkerText(text); if (old_marker_text != text) { @@ -608,7 +608,7 @@ void GameLoop::sleepSendPreview() } /* Only preview if we actually print inputs */ - if (!(context->config.sc.osd & SharedConfig::OSD_INPUTS)) { + if (!(context->config.sc.osd)) { sendMessage(MSGN_EXPOSE); return; } diff --git a/src/program/SaveState.cpp b/src/program/SaveState.cpp index 52e6af3d..8478dbd1 100644 --- a/src/program/SaveState.cpp +++ b/src/program/SaveState.cpp @@ -134,7 +134,7 @@ int SaveState::save(Context* context, const MovieFile& m) op.close(); } - if (context->config.sc.osd & SharedConfig::OSD_MESSAGES) { + if (context->config.sc.osd) { sendMessage(MSGN_OSD_MSG); sendString(saving_msg); } @@ -185,7 +185,7 @@ int SaveState::load(Context* context, const MovieFile& m, bool branch, bool inpu } } - if (context->config.sc.osd & SharedConfig::OSD_MESSAGES) { + if (context->config.sc.osd) { sendMessage(MSGN_OSD_MSG); sendString(no_state_msg); } @@ -193,7 +193,7 @@ int SaveState::load(Context* context, const MovieFile& m, bool branch, bool inpu } if (invalid) { - if (context->config.sc.osd & SharedConfig::OSD_MESSAGES) { + if (context->config.sc.osd) { sendMessage(MSGN_OSD_MSG); sendString(std::string("State invalid because new threads were created")); } @@ -225,7 +225,7 @@ int SaveState::load(Context* context, const MovieFile& m, bool branch, bool inpu /* Checking if the savestate movie is a prefix of our movie */ if (!movie || !m.isPrefix(*movie)) { /* Not a prefix, we don't allow loading */ - if (context->config.sc.osd & SharedConfig::OSD_MESSAGES) { + if (context->config.sc.osd) { sendMessage(MSGN_OSD_MSG); sendString(std::string("Savestate inputs mismatch")); } @@ -233,7 +233,7 @@ int SaveState::load(Context* context, const MovieFile& m, bool branch, bool inpu } } - if (context->config.sc.osd & SharedConfig::OSD_MESSAGES) { + if (context->config.sc.osd) { std::string msg; sendMessage(MSGN_OSD_MSG); sendString(loading_msg); @@ -297,7 +297,7 @@ int SaveState::postLoad(Context* context, MovieFile& m, bool branch, bool inputE m.header->length_nsec = context->current_time_nsec; } - if (didLoad && (context->config.sc.osd & SharedConfig::OSD_MESSAGES)) { + if (didLoad && (context->config.sc.osd)) { sendMessage(MSGN_OSD_MSG); sendString(loaded_msg); } diff --git a/src/program/ui/settings/VideoPane.cpp b/src/program/ui/settings/VideoPane.cpp index 6dced18b..ecd19d77 100644 --- a/src/program/ui/settings/VideoPane.cpp +++ b/src/program/ui/settings/VideoPane.cpp @@ -84,22 +84,10 @@ void VideoPane::initLayout() QVBoxLayout* osdLayout = new QVBoxLayout; osdBox->setLayout(osdLayout); - osdFrameBox = new QCheckBox(tr("Frame count")); - osdInputsBox = new QCheckBox(tr("Inputs")); - osdMessagesBox = new QCheckBox(tr("Messages")); - osdRamBox = new QCheckBox(tr("Ram Watches")); - osdMarkersBox = new QCheckBox(tr("Markers")); - osdLuaBox = new QCheckBox(tr("Lua")); - osdCrosshairBox = new QCheckBox(tr("Crosshair")); + osdMenuBox = new QCheckBox(tr("Main Menu")); osdEncodeBox = new QCheckBox(tr("OSD on video encode")); - osdLayout->addWidget(osdFrameBox); - osdLayout->addWidget(osdInputsBox); - osdLayout->addWidget(osdMessagesBox); - osdLayout->addWidget(osdRamBox); - osdLayout->addWidget(osdMarkersBox); - osdLayout->addWidget(osdLuaBox); - osdLayout->addWidget(osdCrosshairBox); + osdLayout->addWidget(osdMenuBox); osdLayout->addWidget(osdEncodeBox); renderingBox = new QGroupBox(tr("Rendering")); @@ -144,13 +132,7 @@ void VideoPane::initSignals() screenCustomRadio->setChecked(true); saveConfig(); }); - connect(osdFrameBox, &QAbstractButton::clicked, this, &VideoPane::saveConfig); - connect(osdInputsBox, &QAbstractButton::clicked, this, &VideoPane::saveConfig); - connect(osdMessagesBox, &QAbstractButton::clicked, this, &VideoPane::saveConfig); - connect(osdRamBox, &QAbstractButton::clicked, this, &VideoPane::saveConfig); - connect(osdMarkersBox, &QAbstractButton::clicked, this, &VideoPane::saveConfig); - connect(osdLuaBox, &QAbstractButton::clicked, this, &VideoPane::saveConfig); - connect(osdCrosshairBox, &QAbstractButton::clicked, this, &VideoPane::saveConfig); + connect(osdMenuBox, &QAbstractButton::clicked, this, &VideoPane::saveConfig); connect(osdEncodeBox, &QAbstractButton::clicked, this, &VideoPane::saveConfig); connect(rendSoftBox, &QAbstractButton::clicked, this, &VideoPane::saveConfig); @@ -203,13 +185,7 @@ void VideoPane::loadConfig() } } - osdFrameBox->setChecked(context->config.sc.osd & SharedConfig::OSD_FRAMECOUNT); - osdInputsBox->setChecked(context->config.sc.osd & SharedConfig::OSD_INPUTS); - osdMessagesBox->setChecked(context->config.sc.osd & SharedConfig::OSD_MESSAGES); - osdRamBox->setChecked(context->config.sc.osd & SharedConfig::OSD_RAMWATCHES); - osdMarkersBox->setChecked(context->config.sc.osd & SharedConfig::OSD_MARKERS); - osdLuaBox->setChecked(context->config.sc.osd & SharedConfig::OSD_LUA); - osdCrosshairBox->setChecked(context->config.sc.osd & SharedConfig::OSD_CROSSHAIR); + osdMenuBox->setChecked(context->config.sc.osd); osdEncodeBox->setChecked(context->config.sc.osd_encode); rendSoftBox->setChecked(context->config.sc.opengl_soft); @@ -232,22 +208,7 @@ void VideoPane::saveConfig() context->config.sc.screen_height = heightField->value(); } - context->config.sc.osd = 0; - if (osdFrameBox->isChecked()) - context->config.sc.osd |= SharedConfig::OSD_FRAMECOUNT; - if (osdInputsBox->isChecked()) - context->config.sc.osd |= SharedConfig::OSD_INPUTS; - if (osdMessagesBox->isChecked()) - context->config.sc.osd |= SharedConfig::OSD_MESSAGES; - if (osdRamBox->isChecked()) - context->config.sc.osd |= SharedConfig::OSD_RAMWATCHES; - if (osdMarkersBox->isChecked()) - context->config.sc.osd |= SharedConfig::OSD_MARKERS; - if (osdLuaBox->isChecked()) - context->config.sc.osd |= SharedConfig::OSD_LUA; - if (osdCrosshairBox->isChecked()) - context->config.sc.osd |= SharedConfig::OSD_CROSSHAIR; - + context->config.sc.osd = osdMenuBox->isChecked(); context->config.sc.osd_encode = osdEncodeBox->isChecked(); context->config.sc.opengl_soft = rendSoftBox->isChecked(); diff --git a/src/program/ui/settings/VideoPane.h b/src/program/ui/settings/VideoPane.h index 81d70776..7f899c0d 100644 --- a/src/program/ui/settings/VideoPane.h +++ b/src/program/ui/settings/VideoPane.h @@ -56,13 +56,7 @@ private: QSpinBox* widthField; QSpinBox* heightField; - QCheckBox* osdFrameBox; - QCheckBox* osdInputsBox; - QCheckBox* osdMessagesBox; - QCheckBox* osdRamBox; - QCheckBox* osdMarkersBox; - QCheckBox* osdLuaBox; - QCheckBox* osdCrosshairBox; + QCheckBox* osdMenuBox; QCheckBox* osdEncodeBox; ToolTipCheckBox* rendSoftBox; diff --git a/src/shared/SharedConfig.h b/src/shared/SharedConfig.h index 51508ef6..6a101a81 100644 --- a/src/shared/SharedConfig.h +++ b/src/shared/SharedConfig.h @@ -76,20 +76,6 @@ struct __attribute__((packed, aligned(8))) SharedConfig { /* Number of SDL controllers to (virtually) plug in */ int nb_controllers = 0; - /* Log status */ - enum OSDFlags { - OSD_FRAMECOUNT = 0x01, - OSD_INPUTS = 0x02, - OSD_MESSAGES = 0x04, - OSD_RAMWATCHES = 0x08, - OSD_LUA = 0x10, - OSD_CROSSHAIR = 0x20, - OSD_MARKERS = 0x40, - }; - - /* Elements to be displayed on the OSD */ - int osd = OSD_FRAMECOUNT | OSD_INPUTS | OSD_MESSAGES | OSD_RAMWATCHES | OSD_LUA | OSD_CROSSHAIR | OSD_MARKERS; - /** Sound config **/ /* Bit depth of the buffer (usually 8 or 16) */ int audio_bitdepth = 16; @@ -286,6 +272,9 @@ struct __attribute__((packed, aligned(8))) SharedConfig { /* Are preventing the game from warping the cursor */ bool mouse_prevent_warp = false; + /* Display OSD main menu */ + bool osd; + /* Display OSD in the video encode */ bool osd_encode = false; |