2023-05-27 17:45:41 +02:00
|
|
|
#include "window.hpp"
|
2021-04-20 21:46:48 +02:00
|
|
|
#include "init/splash_window.hpp"
|
2021-04-17 15:46:26 +02:00
|
|
|
|
2022-02-01 18:09:40 +01:00
|
|
|
#include <hex/api/imhex_api.hpp>
|
2023-11-28 00:19:42 +01:00
|
|
|
#include <hex/api/event_manager.hpp>
|
2023-05-14 21:50:58 +02:00
|
|
|
|
2021-04-17 15:46:26 +02:00
|
|
|
#include <hex/helpers/utils.hpp>
|
2021-08-29 22:15:18 +02:00
|
|
|
#include <hex/helpers/fmt.hpp>
|
|
|
|
#include <hex/helpers/logger.hpp>
|
2023-12-17 20:33:17 +01:00
|
|
|
#include <fmt/chrono.h>
|
2021-12-22 15:06:16 +01:00
|
|
|
|
|
|
|
#include <romfs/romfs.hpp>
|
2021-04-17 15:46:26 +02:00
|
|
|
|
|
|
|
#include <imgui.h>
|
|
|
|
#include <imgui_internal.h>
|
2021-12-31 01:10:06 +01:00
|
|
|
#include <hex/ui/imgui_imhex_extensions.h>
|
2023-11-20 23:58:49 +01:00
|
|
|
|
2021-04-17 15:46:26 +02:00
|
|
|
#include <imgui_impl_glfw.h>
|
|
|
|
#include <imgui_impl_opengl3.h>
|
|
|
|
#include <GLFW/glfw3.h>
|
2023-11-20 23:58:49 +01:00
|
|
|
#include <opengl_support.h>
|
|
|
|
|
2023-03-12 18:27:29 +01:00
|
|
|
#include <wolv/utils/guards.hpp>
|
|
|
|
|
2021-08-29 14:18:45 +02:00
|
|
|
#include <future>
|
|
|
|
#include <numeric>
|
2023-07-22 18:21:47 +02:00
|
|
|
#include <random>
|
2024-01-09 10:39:06 +01:00
|
|
|
#include <hex/api/task_manager.hpp>
|
2023-12-17 20:33:17 +01:00
|
|
|
#include <nlohmann/json.hpp>
|
2023-10-04 12:00:32 +02:00
|
|
|
|
2021-04-17 15:46:26 +02:00
|
|
|
using namespace std::literals::chrono_literals;
|
|
|
|
|
2021-04-20 21:46:48 +02:00
|
|
|
namespace hex::init {
|
2021-04-17 15:46:26 +02:00
|
|
|
|
2023-05-27 17:45:41 +02:00
|
|
|
struct GlfwError {
|
|
|
|
int errorCode = 0;
|
|
|
|
std::string desc;
|
|
|
|
};
|
|
|
|
|
|
|
|
GlfwError lastGlfwError;
|
|
|
|
|
2021-12-30 23:21:32 +01:00
|
|
|
WindowSplash::WindowSplash() : m_window(nullptr) {
|
2021-04-17 15:46:26 +02:00
|
|
|
this->initGLFW();
|
|
|
|
this->initImGui();
|
2023-12-18 11:24:40 +01:00
|
|
|
this->loadAssets();
|
2022-02-15 22:36:36 +01:00
|
|
|
|
2022-07-02 17:53:13 +02:00
|
|
|
ImHexApi::System::impl::setGPUVendor(reinterpret_cast<const char *>(glGetString(GL_VENDOR)));
|
2023-11-28 00:19:42 +01:00
|
|
|
|
2023-12-08 10:29:44 +01:00
|
|
|
RequestAddInitTask::subscribe([this](const std::string& name, bool async, const TaskFunction &function){
|
2023-12-19 13:10:25 +01:00
|
|
|
m_tasks.push_back(Task{ name, function, async });
|
2023-11-28 00:19:42 +01:00
|
|
|
});
|
2021-04-17 15:46:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
WindowSplash::~WindowSplash() {
|
2022-01-18 00:10:10 +01:00
|
|
|
this->exitImGui();
|
|
|
|
this->exitGLFW();
|
2021-04-17 15:46:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-11-04 23:16:53 +01:00
|
|
|
static void centerWindow(GLFWwindow *window) {
|
|
|
|
// Get the primary monitor
|
|
|
|
GLFWmonitor *monitor = glfwGetPrimaryMonitor();
|
|
|
|
if (!monitor)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Get information about the monitor
|
|
|
|
const GLFWvidmode *mode = glfwGetVideoMode(monitor);
|
|
|
|
if (!mode)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Get the position of the monitor's viewport on the virtual screen
|
|
|
|
int monitorX, monitorY;
|
|
|
|
glfwGetMonitorPos(monitor, &monitorX, &monitorY);
|
|
|
|
|
|
|
|
// Get the window size
|
|
|
|
int windowWidth, windowHeight;
|
|
|
|
glfwGetWindowSize(window, &windowWidth, &windowHeight);
|
|
|
|
|
|
|
|
// Center the splash screen on the monitor
|
|
|
|
glfwSetWindowPos(window, monitorX + (mode->width - windowWidth) / 2, monitorY + (mode->height - windowHeight) / 2);
|
|
|
|
}
|
|
|
|
|
2023-12-17 20:33:17 +01:00
|
|
|
static ImColor getHighlightColor(u32 index) {
|
|
|
|
static auto highlightConfig = nlohmann::json::parse(romfs::get("splash_colors.json").string());
|
|
|
|
static std::list<nlohmann::json> selectedConfigs;
|
2023-12-20 13:42:42 +01:00
|
|
|
static nlohmann::json selectedConfig;
|
|
|
|
|
|
|
|
static std::mt19937 random(std::random_device{}());
|
2023-12-17 20:33:17 +01:00
|
|
|
|
|
|
|
if (selectedConfigs.empty()) {
|
|
|
|
const auto now = []{
|
|
|
|
const auto now = std::chrono::system_clock::now();
|
|
|
|
const auto time = std::chrono::system_clock::to_time_t(now);
|
|
|
|
|
|
|
|
return fmt::localtime(time);
|
|
|
|
}();
|
|
|
|
|
|
|
|
for (const auto &colorConfig : highlightConfig) {
|
2023-12-27 16:33:49 +01:00
|
|
|
if (!colorConfig.contains("time")) {
|
2023-12-17 20:33:17 +01:00
|
|
|
selectedConfigs.push_back(colorConfig);
|
2023-12-27 16:33:49 +01:00
|
|
|
} else {
|
2023-12-17 20:33:17 +01:00
|
|
|
const auto &time = colorConfig["time"];
|
|
|
|
const auto &start = time["start"];
|
|
|
|
const auto &end = time["end"];
|
|
|
|
|
|
|
|
if ((now.tm_mon + 1) >= start[0] && (now.tm_mon + 1) <= end[0]) {
|
|
|
|
if (now.tm_mday >= start[1] && now.tm_mday <= end[1]) {
|
|
|
|
selectedConfigs.push_back(colorConfig);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove the default color theme if there's another one available
|
|
|
|
if (selectedConfigs.size() != 1)
|
|
|
|
selectedConfigs.erase(selectedConfigs.begin());
|
|
|
|
|
2023-12-20 13:42:42 +01:00
|
|
|
selectedConfig = *std::next(selectedConfigs.begin(), random() % selectedConfigs.size());
|
2023-12-17 20:33:17 +01:00
|
|
|
|
2023-12-20 13:42:42 +01:00
|
|
|
log::debug("Using '{}' highlight color theme", selectedConfig["name"].get<std::string>());
|
|
|
|
}
|
2023-12-17 20:33:17 +01:00
|
|
|
|
|
|
|
const auto colorString = selectedConfig["colors"][index % selectedConfig["colors"].size()].get<std::string>();
|
|
|
|
|
|
|
|
if (colorString == "random") {
|
|
|
|
float r, g, b;
|
|
|
|
ImGui::ColorConvertHSVtoRGB(
|
|
|
|
float(random() % 360) / 100.0F,
|
|
|
|
float(25 + random() % 70) / 100.0F,
|
|
|
|
float(85 + random() % 10) / 100.0F,
|
|
|
|
r, g, b);
|
|
|
|
|
|
|
|
return { r, g, b, 0x50 / 255.0F };
|
|
|
|
} else if (colorString.starts_with("#")) {
|
|
|
|
u32 color = std::strtoul(colorString.substr(1).c_str(), nullptr, 16);
|
|
|
|
|
2023-12-18 11:24:40 +01:00
|
|
|
return IM_COL32((color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF, 0x50);
|
2023-12-17 20:33:17 +01:00
|
|
|
} else {
|
|
|
|
log::error("Invalid color string '{}'", colorString);
|
2023-12-18 11:24:40 +01:00
|
|
|
return IM_COL32(0xFF, 0x00, 0xFF, 0xFF);
|
2023-12-17 20:33:17 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-28 00:19:42 +01:00
|
|
|
void WindowSplash::createTask(const Task& task) {
|
|
|
|
auto runTask = [&, task] {
|
|
|
|
try {
|
|
|
|
// Save an iterator to the current task name
|
2023-12-19 13:10:25 +01:00
|
|
|
decltype(m_currTaskNames)::iterator taskNameIter;
|
2023-11-28 00:19:42 +01:00
|
|
|
{
|
2023-12-19 13:10:25 +01:00
|
|
|
std::lock_guard guard(m_progressMutex);
|
|
|
|
m_currTaskNames.push_back(task.name + "...");
|
|
|
|
taskNameIter = std::prev(m_currTaskNames.end());
|
2023-11-28 00:19:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// When the task finished, increment the progress bar
|
|
|
|
ON_SCOPE_EXIT {
|
2023-12-19 13:10:25 +01:00
|
|
|
m_completedTaskCount += 1;
|
|
|
|
m_progress = float(m_completedTaskCount) / float(m_totalTaskCount);
|
2023-11-28 00:19:42 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
// Execute the actual task and track the amount of time it took to run
|
|
|
|
auto startTime = std::chrono::high_resolution_clock::now();
|
|
|
|
bool taskStatus = task.callback();
|
|
|
|
auto endTime = std::chrono::high_resolution_clock::now();
|
|
|
|
|
2023-11-28 00:47:03 +01:00
|
|
|
auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count();
|
|
|
|
|
|
|
|
if (taskStatus)
|
|
|
|
log::info("Task '{}' finished successfully in {} ms", task.name, milliseconds);
|
|
|
|
else
|
|
|
|
log::warn("Task '{}' finished unsuccessfully in {} ms", task.name, milliseconds);
|
2023-11-28 00:19:42 +01:00
|
|
|
|
|
|
|
// Track the overall status of the tasks
|
2023-12-19 13:10:25 +01:00
|
|
|
m_taskStatus = m_taskStatus && taskStatus;
|
2023-11-28 00:19:42 +01:00
|
|
|
|
|
|
|
// Erase the task name from the list of running tasks
|
|
|
|
{
|
2023-12-19 13:10:25 +01:00
|
|
|
std::lock_guard guard(m_progressMutex);
|
|
|
|
m_currTaskNames.erase(taskNameIter);
|
2023-11-28 00:19:42 +01:00
|
|
|
}
|
|
|
|
} catch (const std::exception &e) {
|
|
|
|
log::error("Init task '{}' threw an exception: {}", task.name, e.what());
|
2023-12-19 13:10:25 +01:00
|
|
|
m_taskStatus = false;
|
2023-11-28 00:19:42 +01:00
|
|
|
} catch (...) {
|
|
|
|
log::error("Init task '{}' threw an unidentifiable exception", task.name);
|
2023-12-19 13:10:25 +01:00
|
|
|
m_taskStatus = false;
|
2023-11-28 00:19:42 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
m_totalTaskCount += 1;
|
2023-11-28 00:19:42 +01:00
|
|
|
|
|
|
|
// If the task can be run asynchronously, run it in a separate thread
|
|
|
|
// otherwise run it in this thread and wait for it to finish
|
|
|
|
if (task.async) {
|
2024-01-09 10:39:06 +01:00
|
|
|
std::thread([name = task.name, runTask = std::move(runTask)] {
|
|
|
|
TaskManager::setCurrentThreadName(name);
|
|
|
|
runTask();
|
|
|
|
}).detach();
|
2023-11-28 00:19:42 +01:00
|
|
|
} else {
|
|
|
|
runTask();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-28 00:47:03 +01:00
|
|
|
std::future<bool> WindowSplash::processTasksAsync() {
|
|
|
|
return std::async(std::launch::async, [this] {
|
2024-01-09 10:39:06 +01:00
|
|
|
TaskManager::setCurrentThreadName("Init Tasks");
|
|
|
|
|
2023-11-28 00:47:03 +01:00
|
|
|
auto startTime = std::chrono::high_resolution_clock::now();
|
|
|
|
|
|
|
|
// Loop over all registered init tasks
|
2023-12-19 13:10:25 +01:00
|
|
|
for (auto it = m_tasks.begin(); it != m_tasks.end(); ++it) {
|
2023-11-28 00:47:03 +01:00
|
|
|
// Construct a new task callback
|
2023-11-28 01:55:41 +01:00
|
|
|
this->createTask(*it);
|
2023-11-28 00:47:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check every 100ms if all tasks have run
|
|
|
|
while (true) {
|
|
|
|
{
|
2023-12-19 13:10:25 +01:00
|
|
|
std::scoped_lock lock(m_tasksMutex);
|
|
|
|
if (m_completedTaskCount >= m_totalTaskCount)
|
2023-11-28 00:47:03 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::this_thread::sleep_for(100ms);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto endTime = std::chrono::high_resolution_clock::now();
|
|
|
|
|
|
|
|
auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count();
|
|
|
|
log::info("ImHex fully started in {}ms", milliseconds);
|
|
|
|
|
|
|
|
// Small extra delay so the last progress step is visible
|
|
|
|
std::this_thread::sleep_for(100ms);
|
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
return m_taskStatus.load();
|
2023-11-28 00:47:03 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-11-28 00:19:42 +01:00
|
|
|
|
2023-09-07 20:33:49 +02:00
|
|
|
FrameResult WindowSplash::fullFrame() {
|
2023-12-19 13:10:25 +01:00
|
|
|
glfwSetWindowSize(m_window, 640_scaled, 400_scaled);
|
|
|
|
centerWindow(m_window);
|
2023-11-04 23:16:53 +01:00
|
|
|
|
2023-09-07 20:33:49 +02:00
|
|
|
glfwPollEvents();
|
2021-04-17 15:46:26 +02:00
|
|
|
|
2023-09-07 20:33:49 +02:00
|
|
|
// Start a new ImGui frame
|
|
|
|
ImGui_ImplOpenGL3_NewFrame();
|
|
|
|
ImGui_ImplGlfw_NewFrame();
|
|
|
|
ImGui::NewFrame();
|
2021-04-17 15:46:26 +02:00
|
|
|
|
2022-02-01 18:09:40 +01:00
|
|
|
auto scale = ImHexApi::System::getGlobalScale();
|
2021-08-31 15:22:00 +02:00
|
|
|
|
2023-09-07 20:33:49 +02:00
|
|
|
// Draw the splash screen background
|
|
|
|
auto drawList = ImGui::GetBackgroundDrawList();
|
|
|
|
{
|
2021-04-17 15:46:26 +02:00
|
|
|
|
2023-09-27 14:14:27 +02:00
|
|
|
// Draw the splash screen background
|
2023-09-07 20:33:49 +02:00
|
|
|
drawList->AddImage(this->splashBackgroundTexture, ImVec2(0, 0), this->splashBackgroundTexture.getSize() * scale);
|
2021-04-17 15:46:26 +02:00
|
|
|
|
|
|
|
{
|
2023-09-27 14:14:27 +02:00
|
|
|
|
|
|
|
// Function to highlight a given number of bytes at a position in the splash screen
|
2023-09-07 20:33:49 +02:00
|
|
|
const auto highlightBytes = [&](ImVec2 start, size_t count, ImColor color, float opacity) {
|
2023-09-27 14:14:27 +02:00
|
|
|
// Dimensions and number of bytes that are drawn. Taken from the splash screen image
|
2023-09-07 20:33:49 +02:00
|
|
|
const auto hexSize = ImVec2(29, 18) * scale;
|
|
|
|
const auto hexSpacing = ImVec2(17.4, 15) * scale;
|
|
|
|
const auto hexStart = ImVec2(27, 127) * scale;
|
2021-04-18 20:24:42 +02:00
|
|
|
|
2023-11-10 20:47:08 +01:00
|
|
|
constexpr auto HexCount = ImVec2(13, 7);
|
2023-07-22 18:21:47 +02:00
|
|
|
|
2023-09-07 20:33:49 +02:00
|
|
|
bool isStart = true;
|
2023-07-22 18:21:47 +02:00
|
|
|
|
2023-09-07 20:33:49 +02:00
|
|
|
color.Value.w *= opacity;
|
2023-07-22 18:21:47 +02:00
|
|
|
|
2023-09-27 14:14:27 +02:00
|
|
|
// Loop over all the bytes on the splash screen
|
2023-11-10 20:47:08 +01:00
|
|
|
for (u32 y = u32(start.y); y < u32(HexCount.y); y += 1) {
|
|
|
|
for (u32 x = u32(start.x); x < u32(HexCount.x); x += 1) {
|
2023-09-07 20:33:49 +02:00
|
|
|
if (count-- == 0)
|
|
|
|
return;
|
2023-07-22 18:21:47 +02:00
|
|
|
|
2023-09-27 14:14:27 +02:00
|
|
|
// Find the start position of the byte to draw
|
2023-09-07 20:33:49 +02:00
|
|
|
auto pos = hexStart + ImVec2(float(x), float(y)) * (hexSize + hexSpacing);
|
2023-07-22 18:21:47 +02:00
|
|
|
|
2023-09-27 14:14:27 +02:00
|
|
|
// Fill the rectangle in the byte with the given color
|
2023-09-07 20:33:49 +02:00
|
|
|
drawList->AddRectFilled(pos + ImVec2(0, -hexSpacing.y / 2), pos + hexSize + ImVec2(0, hexSpacing.y / 2), color);
|
2023-07-22 18:21:47 +02:00
|
|
|
|
2023-09-27 14:14:27 +02:00
|
|
|
// Add some extra color on the right if the current byte isn't the last byte, and we didn't reach the right side of the image
|
2023-11-10 20:47:08 +01:00
|
|
|
if (count > 0 && x != u32(HexCount.x) - 1)
|
2023-09-07 20:33:49 +02:00
|
|
|
drawList->AddRectFilled(pos + ImVec2(hexSize.x, -hexSpacing.y / 2), pos + hexSize + ImVec2(hexSpacing.x, hexSpacing.y / 2), color);
|
2023-07-22 18:21:47 +02:00
|
|
|
|
2023-09-27 14:14:27 +02:00
|
|
|
// Add some extra color on the left if this is the first byte we're highlighting
|
2023-09-07 20:33:49 +02:00
|
|
|
if (isStart) {
|
|
|
|
isStart = false;
|
|
|
|
drawList->AddRectFilled(pos - hexSpacing / 2, pos + ImVec2(0, hexSize.y + hexSpacing.y / 2), color);
|
|
|
|
}
|
2023-09-27 14:14:27 +02:00
|
|
|
|
|
|
|
// Add some extra color on the right if this is the last byte
|
2023-09-07 20:33:49 +02:00
|
|
|
if (count == 0) {
|
|
|
|
drawList->AddRectFilled(pos + ImVec2(hexSize.x, -hexSpacing.y / 2), pos + hexSize + hexSpacing / 2, color);
|
2023-07-22 18:21:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-07 20:33:49 +02:00
|
|
|
start.x = 0;
|
|
|
|
}
|
|
|
|
};
|
2023-07-22 18:21:47 +02:00
|
|
|
|
2023-09-27 14:14:27 +02:00
|
|
|
// Draw all highlights, slowly fading them in as the init tasks progress
|
2023-09-07 20:33:49 +02:00
|
|
|
for (const auto &highlight : this->highlights)
|
|
|
|
highlightBytes(highlight.start, highlight.count, highlight.color, this->progressLerp);
|
|
|
|
}
|
2023-07-22 18:21:47 +02:00
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
this->progressLerp += (m_progress - this->progressLerp) * 0.1F;
|
2023-07-22 18:21:47 +02:00
|
|
|
|
2023-09-27 14:14:27 +02:00
|
|
|
// Draw the splash screen foreground
|
2023-09-07 20:33:49 +02:00
|
|
|
drawList->AddImage(this->splashTextTexture, ImVec2(0, 0), this->splashTextTexture.getSize() * scale);
|
2021-04-21 19:27:05 +02:00
|
|
|
|
2023-09-27 14:14:27 +02:00
|
|
|
// Draw the "copyright" notice
|
2023-09-07 20:33:49 +02:00
|
|
|
drawList->AddText(ImVec2(35, 85) * scale, ImColor(0xFF, 0xFF, 0xFF, 0xFF), hex::format("WerWolv\n2020 - {0}", &__DATE__[7]).c_str());
|
2023-07-22 18:21:47 +02:00
|
|
|
|
2023-09-27 14:14:27 +02:00
|
|
|
// Draw version information
|
|
|
|
// In debug builds, also display the current commit hash and branch
|
2023-09-07 20:33:49 +02:00
|
|
|
#if defined(DEBUG)
|
2023-11-28 01:55:41 +01:00
|
|
|
const static auto VersionInfo = hex::format("{0} : {1}@{2}", ImHexApi::System::getImHexVersion(), ImHexApi::System::getCommitBranch(), ImHexApi::System::getCommitHash());
|
2023-09-07 20:33:49 +02:00
|
|
|
#else
|
|
|
|
const static auto VersionInfo = hex::format("{0}", ImHexApi::System::getImHexVersion());
|
|
|
|
#endif
|
2021-08-04 18:57:53 +02:00
|
|
|
|
2023-09-07 20:33:49 +02:00
|
|
|
drawList->AddText(ImVec2((this->splashBackgroundTexture.getSize().x * scale - ImGui::CalcTextSize(VersionInfo.c_str()).x) / 2, 105 * scale), ImColor(0xFF, 0xFF, 0xFF, 0xFF), VersionInfo.c_str());
|
|
|
|
}
|
2023-07-15 14:29:14 +02:00
|
|
|
|
2023-09-07 20:33:49 +02:00
|
|
|
// Draw the task progress bar
|
|
|
|
{
|
2023-12-19 13:10:25 +01:00
|
|
|
std::lock_guard guard(m_progressMutex);
|
2023-07-22 18:21:47 +02:00
|
|
|
|
2023-09-07 20:33:49 +02:00
|
|
|
const auto progressBackgroundStart = ImVec2(99, 357) * scale;
|
|
|
|
const auto progressBackgroundSize = ImVec2(442, 30) * scale;
|
2023-07-22 18:21:47 +02:00
|
|
|
|
2023-09-07 20:33:49 +02:00
|
|
|
const auto progressStart = progressBackgroundStart + ImVec2(0, 20) * scale;
|
2023-12-19 13:10:25 +01:00
|
|
|
const auto progressSize = ImVec2(progressBackgroundSize.x * m_progress, 10 * scale);
|
2023-09-27 14:14:27 +02:00
|
|
|
|
|
|
|
// Draw progress bar
|
2023-09-07 20:33:49 +02:00
|
|
|
drawList->AddRectFilled(progressStart, progressStart + progressSize, 0xD0FFFFFF);
|
|
|
|
|
2023-09-27 14:14:27 +02:00
|
|
|
// Draw task names separated by | characters
|
2023-12-19 13:10:25 +01:00
|
|
|
if (!m_currTaskNames.empty()) {
|
2023-09-07 20:33:49 +02:00
|
|
|
drawList->PushClipRect(progressBackgroundStart, progressBackgroundStart + progressBackgroundSize, true);
|
2023-12-19 13:10:25 +01:00
|
|
|
drawList->AddText(progressStart + ImVec2(5, -20) * scale, ImColor(0xFF, 0xFF, 0xFF, 0xFF), hex::format("{}", fmt::join(m_currTaskNames, " | ")).c_str());
|
2023-09-07 20:33:49 +02:00
|
|
|
drawList->PopClipRect();
|
2021-04-17 15:46:26 +02:00
|
|
|
}
|
2023-09-07 20:33:49 +02:00
|
|
|
}
|
2021-04-17 15:46:26 +02:00
|
|
|
|
2023-09-07 20:33:49 +02:00
|
|
|
// Render the frame
|
|
|
|
ImGui::Render();
|
|
|
|
int displayWidth, displayHeight;
|
2023-12-19 13:10:25 +01:00
|
|
|
glfwGetFramebufferSize(m_window, &displayWidth, &displayHeight);
|
2023-09-07 20:33:49 +02:00
|
|
|
glViewport(0, 0, displayWidth, displayHeight);
|
|
|
|
glClearColor(0.00F, 0.00F, 0.00F, 0.00F);
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
2021-04-17 15:46:26 +02:00
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
glfwSwapBuffers(m_window);
|
2021-04-17 15:46:26 +02:00
|
|
|
|
2023-09-07 20:33:49 +02:00
|
|
|
// Check if all background tasks have finished so the splash screen can be closed
|
|
|
|
if (this->tasksSucceeded.wait_for(0s) == std::future_status::ready) {
|
2023-09-09 12:49:15 +02:00
|
|
|
if (this->tasksSucceeded.get()) {
|
2023-12-08 10:29:44 +01:00
|
|
|
log::debug("All tasks finished successfully!");
|
|
|
|
return FrameResult::Success;
|
2023-09-09 12:49:15 +02:00
|
|
|
} else {
|
|
|
|
log::warn("All tasks finished, but some failed");
|
2023-12-08 10:29:44 +01:00
|
|
|
return FrameResult::Failure;
|
2023-09-09 12:49:15 +02:00
|
|
|
}
|
2021-04-17 15:46:26 +02:00
|
|
|
}
|
|
|
|
|
2023-12-08 10:29:44 +01:00
|
|
|
return FrameResult::Running;
|
2023-09-07 20:33:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool WindowSplash::loop() {
|
|
|
|
// Splash window rendering loop
|
|
|
|
while (true) {
|
2023-12-08 10:29:44 +01:00
|
|
|
auto frameResult = this->fullFrame();
|
|
|
|
|
|
|
|
if (frameResult == FrameResult::Success)
|
|
|
|
return true;
|
|
|
|
else if (frameResult == FrameResult::Failure)
|
|
|
|
return false;
|
2023-09-07 20:33:49 +02:00
|
|
|
}
|
2021-04-17 15:46:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void WindowSplash::initGLFW() {
|
2023-05-27 17:45:41 +02:00
|
|
|
glfwSetErrorCallback([](int errorCode, const char *desc) {
|
2024-01-30 00:47:02 +01:00
|
|
|
if (errorCode == GLFW_PLATFORM_ERROR) {
|
|
|
|
// Ignore error spam caused by Wayland not supporting moving or resizing
|
|
|
|
// windows or querying their position and size.
|
|
|
|
if (std::string_view(desc).contains("Wayland"))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-05-27 17:45:41 +02:00
|
|
|
lastGlfwError.errorCode = errorCode;
|
|
|
|
lastGlfwError.desc = std::string(desc);
|
|
|
|
log::error("GLFW Error [{}] : {}", errorCode, desc);
|
2021-04-17 15:46:26 +02:00
|
|
|
});
|
|
|
|
|
2021-04-20 21:46:48 +02:00
|
|
|
if (!glfwInit()) {
|
|
|
|
log::fatal("Failed to initialize GLFW!");
|
2023-07-23 23:38:13 +02:00
|
|
|
std::exit(EXIT_FAILURE);
|
2021-04-20 21:46:48 +02:00
|
|
|
}
|
2021-04-17 15:46:26 +02:00
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Configure used OpenGL version
|
2022-08-02 08:24:46 +02:00
|
|
|
#if defined(OS_MACOS)
|
|
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
|
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
|
|
|
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
2023-02-17 12:03:53 +01:00
|
|
|
glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_FALSE);
|
impr: Don't force using discrete graphics card on macOS (#1341)
<!--
Please provide as much information as possible about what your PR aims
to do.
PRs with no description will most likely be closed until more
information is provided.
If you're planing on changing fundamental behaviour or add big new
features, please open a GitHub Issue first before starting to work on
it.
If it's not something big and you still want to contact us about it,
feel free to do so !
-->
### Problem description
<!-- Describe the bug that you fixed/feature request that you
implemented, or link to an existing issue describing it -->
When starting ImHex on a MacBook model with both integrated and discrete
graphics, it will force the computer to use the discrete graphics card.
This causes increased power usage, meaning the fans will spin up, the
battery will drain faster, etc. This program is not very graphics
intensive, so using the discrete graphics card shouldn't be needed.
### Implementation description
<!-- Explain what you did to correct the problem -->
I changed the
[`GLFW_COCOA_GRAPHICS_SWITCHING`](https://www.glfw.org/docs/latest/window_guide.html#window_hints_osx)
setting in GLFW to not enforce using the discrete graphics.
### Screenshots
<!-- If your change is visual, take a screenshot showing it. Ideally,
make before/after sceenshots -->
### Additional things
<!-- Anything else you would like to say -->
My editor is configured to automatically remove trailing whitespace, so
I hope that those whitespace changes are ok
2023-10-05 08:39:53 +02:00
|
|
|
glfwWindowHint(GLFW_COCOA_GRAPHICS_SWITCHING, GLFW_TRUE);
|
2022-08-02 08:24:46 +02:00
|
|
|
#else
|
|
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
|
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
|
|
|
#endif
|
2021-04-21 20:06:48 +02:00
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Make splash screen non-resizable, undecorated and transparent
|
2021-04-18 20:24:42 +02:00
|
|
|
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
|
2022-08-02 08:24:46 +02:00
|
|
|
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
|
2021-04-17 15:46:26 +02:00
|
|
|
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE);
|
|
|
|
glfwWindowHint(GLFW_DECORATED, GLFW_FALSE);
|
2022-01-22 22:08:25 +01:00
|
|
|
glfwWindowHint(GLFW_FLOATING, GLFW_FALSE);
|
2023-10-04 12:00:32 +02:00
|
|
|
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
|
2021-04-17 15:46:26 +02:00
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Create the splash screen window
|
2023-12-19 13:10:25 +01:00
|
|
|
m_window = glfwCreateWindow(1, 1, "Starting ImHex...", nullptr, nullptr);
|
|
|
|
if (m_window == nullptr) {
|
2023-05-27 17:45:41 +02:00
|
|
|
hex::nativeErrorMessage(hex::format(
|
|
|
|
"Failed to create GLFW window: [{}] {}.\n"
|
|
|
|
"You may not have a renderer available.\n"
|
|
|
|
"The most common cause of this is using a virtual machine\n"
|
|
|
|
"You may want to try a release artifact ending with 'NoGPU'"
|
|
|
|
, lastGlfwError.errorCode, lastGlfwError.desc));
|
2023-07-23 23:38:13 +02:00
|
|
|
std::exit(EXIT_FAILURE);
|
2022-07-29 17:37:30 +02:00
|
|
|
}
|
|
|
|
|
2023-10-30 16:40:14 +01:00
|
|
|
// Force window to be fully opaque by default
|
2023-12-19 13:10:25 +01:00
|
|
|
glfwSetWindowOpacity(m_window, 1.0F);
|
2023-10-30 16:40:14 +01:00
|
|
|
|
2022-07-29 17:37:30 +02:00
|
|
|
// Calculate native scale factor for hidpi displays
|
|
|
|
{
|
2021-09-23 22:56:49 +02:00
|
|
|
float xScale = 0, yScale = 0;
|
2023-12-19 13:10:25 +01:00
|
|
|
glfwGetWindowContentScale(m_window, &xScale, &yScale);
|
2021-09-23 22:56:49 +02:00
|
|
|
|
2022-02-01 18:09:40 +01:00
|
|
|
auto meanScale = std::midpoint(xScale, yScale);
|
2022-11-08 18:09:48 +01:00
|
|
|
if (meanScale <= 0.0F)
|
|
|
|
meanScale = 1.0F;
|
|
|
|
|
|
|
|
#if defined(OS_MACOS)
|
2023-02-08 14:11:42 +01:00
|
|
|
meanScale /= getBackingScaleFactor();
|
2023-10-06 12:57:29 +02:00
|
|
|
#elif defined(OS_WEB)
|
|
|
|
meanScale = 1.0F;
|
2022-11-08 18:09:48 +01:00
|
|
|
#endif
|
2022-02-01 18:09:40 +01:00
|
|
|
|
|
|
|
ImHexApi::System::impl::setGlobalScale(meanScale);
|
2022-07-29 17:37:30 +02:00
|
|
|
ImHexApi::System::impl::setNativeScale(meanScale);
|
2021-08-04 18:57:53 +02:00
|
|
|
|
2022-07-30 21:25:18 +02:00
|
|
|
log::info("Native scaling set to: {:.1f}", meanScale);
|
2021-04-20 21:46:48 +02:00
|
|
|
}
|
2021-04-17 15:46:26 +02:00
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
glfwMakeContextCurrent(m_window);
|
2021-04-17 15:46:26 +02:00
|
|
|
glfwSwapInterval(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void WindowSplash::initImGui() {
|
2023-02-17 12:03:53 +01:00
|
|
|
// Initialize ImGui
|
2021-04-17 15:46:26 +02:00
|
|
|
IMGUI_CHECKVERSION();
|
|
|
|
GImGui = ImGui::CreateContext();
|
|
|
|
ImGui::StyleColorsDark();
|
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
ImGui_ImplGlfw_InitForOpenGL(m_window, true);
|
2022-08-02 13:12:12 +02:00
|
|
|
|
|
|
|
#if defined(OS_MACOS)
|
|
|
|
ImGui_ImplOpenGL3_Init("#version 150");
|
2023-10-04 12:00:32 +02:00
|
|
|
#elif defined(OS_WEB)
|
|
|
|
ImGui_ImplOpenGL3_Init();
|
2024-02-11 11:44:44 +01:00
|
|
|
ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback("#canvas");
|
2022-08-02 13:12:12 +02:00
|
|
|
#else
|
|
|
|
ImGui_ImplOpenGL3_Init("#version 130");
|
|
|
|
#endif
|
2021-04-21 19:27:05 +02:00
|
|
|
|
|
|
|
auto &io = ImGui::GetIO();
|
|
|
|
|
2022-02-01 18:09:40 +01:00
|
|
|
ImGui::GetStyle().ScaleAllSizes(ImHexApi::System::getGlobalScale());
|
2021-08-04 18:57:53 +02:00
|
|
|
|
2023-02-17 12:03:53 +01:00
|
|
|
// Load fonts necessary for the splash screen
|
|
|
|
{
|
|
|
|
io.Fonts->Clear();
|
|
|
|
|
|
|
|
ImFontConfig cfg;
|
|
|
|
cfg.OversampleH = cfg.OversampleV = 1, cfg.PixelSnapH = true;
|
|
|
|
cfg.SizePixels = 13.0_scaled;
|
|
|
|
io.Fonts->AddFontDefault(&cfg);
|
|
|
|
|
|
|
|
std::uint8_t *px;
|
|
|
|
int w, h;
|
2023-03-14 12:30:28 +01:00
|
|
|
io.Fonts->GetTexDataAsAlpha8(&px, &w, &h);
|
2023-02-17 12:03:53 +01:00
|
|
|
|
|
|
|
// Create new font atlas
|
|
|
|
GLuint tex;
|
|
|
|
glGenTextures(1, &tex);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, tex);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
2023-03-14 12:30:28 +01:00
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, w, h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, px);
|
2023-02-17 12:03:53 +01:00
|
|
|
io.Fonts->SetTexID(reinterpret_cast<ImTextureID>(tex));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Don't save window settings for the splash screen
|
2022-03-13 17:36:50 +01:00
|
|
|
io.IniFilename = nullptr;
|
2021-04-17 15:46:26 +02:00
|
|
|
}
|
|
|
|
|
2023-09-07 20:33:49 +02:00
|
|
|
/**
|
|
|
|
* @brief Initialize resources for the splash window
|
|
|
|
*/
|
2023-12-18 11:24:40 +01:00
|
|
|
void WindowSplash::loadAssets() {
|
2023-09-07 20:33:49 +02:00
|
|
|
|
|
|
|
// Load splash screen image from romfs
|
2023-12-09 22:00:35 +01:00
|
|
|
this->splashBackgroundTexture = ImGuiExt::Texture(romfs::get("splash_background.png").span(), ImGuiExt::Texture::Filter::Linear);
|
|
|
|
this->splashTextTexture = ImGuiExt::Texture(romfs::get("splash_text.png").span(), ImGuiExt::Texture::Filter::Linear);
|
2023-09-07 20:33:49 +02:00
|
|
|
|
|
|
|
// If the image couldn't be loaded correctly, something went wrong during the build process
|
|
|
|
// Close the application since this would lead to errors later on anyway.
|
|
|
|
if (!this->splashBackgroundTexture.isValid() || !this->splashTextTexture.isValid()) {
|
|
|
|
log::fatal("Could not load splash screen image!");
|
|
|
|
std::exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::mt19937 rng(std::random_device{}());
|
|
|
|
|
|
|
|
u32 lastPos = 0;
|
|
|
|
u32 lastCount = 0;
|
2023-12-18 12:43:16 +01:00
|
|
|
u32 index = 0;
|
|
|
|
for (auto &highlight : this->highlights) {
|
2023-12-17 20:33:17 +01:00
|
|
|
u32 newPos = lastPos + lastCount + (rng() % 35);
|
2023-12-08 10:29:44 +01:00
|
|
|
u32 newCount = (rng() % 7) + 3;
|
|
|
|
highlight.start.x = float(newPos % 13);
|
|
|
|
highlight.start.y = float(newPos / 13);
|
2023-09-07 20:33:49 +02:00
|
|
|
highlight.count = newCount;
|
|
|
|
|
2023-12-17 20:33:17 +01:00
|
|
|
highlight.color = getHighlightColor(index);
|
2023-09-07 20:33:49 +02:00
|
|
|
|
|
|
|
lastPos = newPos;
|
|
|
|
lastCount = newCount;
|
2023-12-18 12:43:16 +01:00
|
|
|
index += 1;
|
2023-09-07 20:33:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void WindowSplash::startStartupTasks() {
|
2023-09-27 14:14:27 +02:00
|
|
|
// Launch init tasks in the background
|
2023-09-07 20:33:49 +02:00
|
|
|
this->tasksSucceeded = processTasksAsync();
|
|
|
|
}
|
|
|
|
|
2023-12-08 10:29:44 +01:00
|
|
|
void WindowSplash::exitGLFW() const {
|
2023-12-19 13:10:25 +01:00
|
|
|
glfwDestroyWindow(m_window);
|
2021-04-17 15:46:26 +02:00
|
|
|
glfwTerminate();
|
|
|
|
}
|
|
|
|
|
2023-12-08 10:29:44 +01:00
|
|
|
void WindowSplash::exitImGui() const {
|
2021-04-17 15:46:26 +02:00
|
|
|
ImGui_ImplOpenGL3_Shutdown();
|
|
|
|
ImGui_ImplGlfw_Shutdown();
|
|
|
|
ImGui::DestroyContext();
|
|
|
|
}
|
|
|
|
|
2021-12-22 13:16:51 +01:00
|
|
|
}
|