From 0ba011dbe187d676ef7137d57f6d8fd1785ecbeb Mon Sep 17 00:00:00 2001 From: iTrooz Date: Sat, 27 May 2023 17:45:41 +0200 Subject: [PATCH] feat: Add a native error message when glfw window creation fails (#1104) Draft because I want to test it again tomorrow with all OSes, first --------- Co-authored-by: Nik --- .../include/hex/helpers/utils_macos.hpp | 1 + lib/libimhex/source/helpers/utils_macos.m | 5 +++ main/include/window.hpp | 2 + main/source/init/splash_window.cpp | 21 ++++++++-- main/source/window/linux_window.cpp | 40 +++++++++++++++++++ main/source/window/macos_window.cpp | 5 +++ main/source/window/win_window.cpp | 5 +++ 7 files changed, 76 insertions(+), 3 deletions(-) diff --git a/lib/libimhex/include/hex/helpers/utils_macos.hpp b/lib/libimhex/include/hex/helpers/utils_macos.hpp index 070388df8..ed5ba72ba 100644 --- a/lib/libimhex/include/hex/helpers/utils_macos.hpp +++ b/lib/libimhex/include/hex/helpers/utils_macos.hpp @@ -4,6 +4,7 @@ extern "C" { + void errorMessageMacos(const char *message); void openWebpageMacos(const char *url); bool isMacosSystemDarkModeEnabled(); float getBackingScaleFactor(); diff --git a/lib/libimhex/source/helpers/utils_macos.m b/lib/libimhex/source/helpers/utils_macos.m index cbf19c30e..7f71d2881 100644 --- a/lib/libimhex/source/helpers/utils_macos.m +++ b/lib/libimhex/source/helpers/utils_macos.m @@ -13,6 +13,11 @@ #import #import + void errorMessageMacos(const char *cMessage) { + CFStringRef strMessage = CFStringCreateWithCString(NULL, cMessage, kCFStringEncodingUTF8); + CFUserNotificationDisplayAlert(0, kCFUserNotificationStopAlertLevel, NULL, NULL, NULL, strMessage, NULL, NULL, NULL, NULL, NULL); + } + void openFile(const char *path); void openWebpageMacos(const char *url) { diff --git a/main/include/window.hpp b/main/include/window.hpp index 6c5d6eac9..af3bba29b 100644 --- a/main/include/window.hpp +++ b/main/include/window.hpp @@ -14,6 +14,8 @@ struct ImGuiSettingsHandler; namespace hex { + void nativeErrorMessage(const std::string &message); + class Window { public: Window(); diff --git a/main/source/init/splash_window.cpp b/main/source/init/splash_window.cpp index f5324fcc4..44c1449aa 100644 --- a/main/source/init/splash_window.cpp +++ b/main/source/init/splash_window.cpp @@ -1,3 +1,4 @@ +#include "window.hpp" #include "init/splash_window.hpp" #include @@ -32,6 +33,13 @@ using namespace std::literals::chrono_literals; namespace hex::init { + struct GlfwError { + int errorCode = 0; + std::string desc; + }; + + GlfwError lastGlfwError; + WindowSplash::WindowSplash() : m_window(nullptr) { this->initGLFW(); this->initImGui(); @@ -186,8 +194,10 @@ namespace hex::init { } void WindowSplash::initGLFW() { - glfwSetErrorCallback([](int error, const char *desc) { - log::error("GLFW Error [{}] : {}", error, desc); + glfwSetErrorCallback([](int errorCode, const char *desc) { + lastGlfwError.errorCode = errorCode; + lastGlfwError.desc = std::string(desc); + log::error("GLFW Error [{}] : {}", errorCode, desc); }); if (!glfwInit()) { @@ -216,7 +226,12 @@ namespace hex::init { // Create the splash screen window this->m_window = glfwCreateWindow(1, 400, "Starting ImHex...", nullptr, nullptr); if (this->m_window == nullptr) { - log::fatal("Failed to create GLFW window!"); + 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)); exit(EXIT_FAILURE); } diff --git a/main/source/window/linux_window.cpp b/main/source/window/linux_window.cpp index ea00c1ea1..291dae7f0 100644 --- a/main/source/window/linux_window.cpp +++ b/main/source/window/linux_window.cpp @@ -17,9 +17,49 @@ #include #include + #include + #include namespace hex { + bool isFileInPath(const std::fs::path &filename) { + auto optPathVar = hex::getEnvironmentVariable("PATH"); + if (!optPathVar.has_value()) { + log::error("Could not find variable named PATH"); + return false; + } + + for (auto dir : std::views::split(optPathVar.value(), ':')) { + if (std::fs::exists(std::fs::path(std::string_view(dir)) / filename)) { + return true; + } + } + return false; + } + + void executeCmd(const std::vector &argsVector) { + std::vector cArgsVector; + for (const auto &str : argsVector) { + cArgsVector.push_back(const_cast(str.c_str())); + } + cArgsVector.push_back(nullptr); + + if (fork() == 0) { + execvp(cArgsVector[0], &cArgsVector[0]); + log::error("execvp() failed: {}", strerror(errno)); + exit(EXIT_FAILURE); + } + } + + void nativeErrorMessage(const std::string &message) { + log::fatal(message); + if (isFileInPath("zenity")) { + executeCmd({"zenity", "--error", "--text", message}); + } else if(isFileInPath("notify-send")) { + executeCmd({"notify-send", "-i", "script-error", "Error", message}); + } // hopefully one of these commands is installed + } + void Window::initNative() { // Add plugin library folders to dll search path for (const auto &path : hex::fs::getDefaultPaths(fs::ImHexPath::Libraries)) { diff --git a/main/source/window/macos_window.cpp b/main/source/window/macos_window.cpp index 36feaa664..84219c441 100644 --- a/main/source/window/macos_window.cpp +++ b/main/source/window/macos_window.cpp @@ -17,6 +17,11 @@ namespace hex { + void nativeErrorMessage(const std::string &message) { + log::fatal(message); + errorMessageMacos(message.c_str()); + } + void Window::initNative() { // Add plugin library folders to dll search path for (const auto &path : hex::fs::getDefaultPaths(fs::ImHexPath::Libraries)) { diff --git a/main/source/window/win_window.cpp b/main/source/window/win_window.cpp index 70e50eaa1..540e90d2e 100644 --- a/main/source/window/win_window.cpp +++ b/main/source/window/win_window.cpp @@ -37,6 +37,11 @@ namespace hex { static float g_titleBarHeight; static Microsoft::WRL::ComPtr g_taskbarList; + void nativeErrorMessage(const std::string &message) { + log::fatal(message); + MessageBox(NULL, message.c_str(), "Error", MB_ICONERROR | MB_OK); + } + // Custom Window procedure for receiving OS events static LRESULT commonWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) {