diff --git a/lib/libimhex/include/hex/helpers/utils.hpp b/lib/libimhex/include/hex/helpers/utils.hpp index bec2f428c..702fe0e57 100644 --- a/lib/libimhex/include/hex/helpers/utils.hpp +++ b/lib/libimhex/include/hex/helpers/utils.hpp @@ -385,4 +385,7 @@ namespace hex { std::optional parseTime(std::string_view format, const std::string &timeString); std::optional getOSLanguage(); + + void showErrorMessageBox(const std::string &message); + void showToastMessage(const std::string &title, const std::string &message); } diff --git a/lib/libimhex/include/hex/helpers/utils_macos.hpp b/lib/libimhex/include/hex/helpers/utils_macos.hpp index 2535758e4..bc76b94db 100644 --- a/lib/libimhex/include/hex/helpers/utils_macos.hpp +++ b/lib/libimhex/include/hex/helpers/utils_macos.hpp @@ -30,6 +30,8 @@ bool macosIsMainInstance(); void macosSendMessageToMainInstance(const unsigned char *data, size_t size); void macosInstallEventListener(); + + void toastMessageMacos(const char *title, const char *message); } #endif diff --git a/lib/libimhex/source/helpers/utils.cpp b/lib/libimhex/source/helpers/utils.cpp index 9af709e2d..626f15567 100644 --- a/lib/libimhex/source/helpers/utils.cpp +++ b/lib/libimhex/source/helpers/utils.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -22,6 +23,7 @@ #if defined(OS_WINDOWS) #include #include + #include #include #elif defined(OS_LINUX) @@ -851,4 +853,81 @@ namespace hex { EventNativeMessageReceived::post(std::vector(data, data + length)); } + void showErrorMessageBox(const std::string &message) { + log::fatal("{}", message); + #if defined(OS_WINDOWS) + MessageBoxA(nullptr, message.c_str(), "Error", MB_ICONERROR | MB_OK); + #elif defined (OS_MACOS) + errorMessageMacos(message.c_str()); + #elif defined(OS_LINUX) + // Hopefully one of these commands is installed + if (isFileInPath("zenity")) { + executeCmd({"zenity", "--error", "--text", message}); + } else if (isFileInPath("notify-send")) { + executeCmd({"notify-send", "-i", "script-error", "Error", message}); + } + #elif defined(OS_WEB) + EM_ASM({ + alert(UTF8ToString($0)); + }, message.c_str()); + #endif + } + + void showToastMessage(const std::string &title, const std::string &message) { + #if defined(OS_WINDOWS) + const auto wideTitle = wolv::util::utf8ToWstring(title).value_or(L"???"); + const auto wideMessage = wolv::util::utf8ToWstring(message).value_or(L"???"); + + WNDCLASS wc = { }; + wc.lpfnWndProc = DefWindowProc; + wc.hInstance = GetModuleHandle(nullptr); + wc.lpszClassName = L"ImHex Toast"; + RegisterClass(&wc); + + HWND hwnd = CreateWindow( + wc.lpszClassName, L"", 0, + 0, 0, 0, 0, + nullptr, nullptr, wc.hInstance, nullptr); + + NOTIFYICONDATA nid = { }; + nid.cbSize = sizeof(nid); + nid.hWnd = hwnd; + nid.uID = 1; + nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_INFO; + nid.uCallbackMessage = WM_USER + 1; + nid.dwInfoFlags = NIIF_USER | NIIF_LARGE_ICON; + nid.hIcon = LoadIcon(nullptr, IDI_INFORMATION); + nid.uTimeout = 5000; + wcsncpy(nid.szTip, L"ImHex", ARRAYSIZE(nid.szTip)); + wcsncpy(nid.szInfoTitle, wideTitle.c_str(), ARRAYSIZE(nid.szInfoTitle)); + wcsncpy(nid.szInfo, wideMessage.c_str(), ARRAYSIZE(nid.szInfo)); + + nid.dwInfoFlags = NIIF_INFO; + + Shell_NotifyIcon(NIM_ADD, &nid); + #elif defined(OS_MACOS) + toastMessageMacos(title.c_str(), message.c_str()); + #elif defined(OS_LINUX) + if (std::system(fmt::format(R"(notify-send "{}" "{}")", title, message).c_str()) != 0) { + // Fallback to zenity if notify-send fails + std::system(fmt::format(R"(zenity --info --title="{}" --text="{}")", title, message).c_str()); + } + #elif defined(OS_WEB) + EM_ASM({ + const t = UTF8ToString($0); + const m = UTF8ToString($1); + + if (Notification.permission === "granted") { + new Notification(t, { body: m }); + } else if (Notification.permission !== "denied") { + Notification.requestPermission().then(function(p) { + if (p === "granted") { + new Notification(t, { body: m }); + } + }); + } + }, title.c_str(), message.c_str()); + #endif + } + } \ No newline at end of file diff --git a/lib/libimhex/source/helpers/utils_macos.m b/lib/libimhex/source/helpers/utils_macos.m index 95bff3473..db94955e9 100644 --- a/lib/libimhex/source/helpers/utils_macos.m +++ b/lib/libimhex/source/helpers/utils_macos.m @@ -363,4 +363,18 @@ } } + void toastMessageMacos(const char *title, const char *message) { + @autoreleasepool { + NSString *nsTitle = [NSString stringWithUTF8String:title]; + NSString *nsMessage = [NSString stringWithUTF8String:message]; + + NSUserNotification *notification = [[NSUserNotification alloc] init]; + notification.title = nsTitle; + notification.informativeText = nsMessage; + notification.soundName = NSUserNotificationDefaultSoundName; + + [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification]; + } + } + #endif diff --git a/main/gui/include/window.hpp b/main/gui/include/window.hpp index 1d69335f2..b4f8d026a 100644 --- a/main/gui/include/window.hpp +++ b/main/gui/include/window.hpp @@ -20,8 +20,6 @@ struct ImGuiSettingsHandler; namespace hex { - void nativeErrorMessage(const std::string &message); - class Window { public: Window(); diff --git a/main/gui/source/crash_handlers.cpp b/main/gui/source/crash_handlers.cpp index 3294c0a92..23f449b16 100644 --- a/main/gui/source/crash_handlers.cpp +++ b/main/gui/source/crash_handlers.cpp @@ -2,9 +2,7 @@ #include #include - #include -#include #include #include @@ -15,11 +13,10 @@ #include -#include - #include #include #include +#include #if defined(IMGUI_TEST_ENGINE) #include @@ -39,7 +36,7 @@ namespace hex::crash { void resetCrashHandlers(); static void sendNativeMessage(const std::string& message) { - hex::nativeErrorMessage(fmt::format("ImHex crashed during initial setup!\nError: {}", message)); + hex::showErrorMessageBox(fmt::format("ImHex crashed during initial setup!\nError: {}", message)); } // Function that decides what should happen on a crash diff --git a/main/gui/source/init/splash_window.cpp b/main/gui/source/init/splash_window.cpp index f626e2f8f..19e8e827d 100644 --- a/main/gui/source/init/splash_window.cpp +++ b/main/gui/source/init/splash_window.cpp @@ -478,7 +478,7 @@ namespace hex::init { // Create the splash screen window m_window = glfwCreateWindow(WindowSize.x, WindowSize.y, "Starting ImHex...", nullptr, nullptr); if (m_window == nullptr) { - hex::nativeErrorMessage(fmt::format( + hex::showErrorMessageBox(fmt::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" diff --git a/main/gui/source/main.cpp b/main/gui/source/main.cpp index c49cccaa8..b8031a8ce 100644 --- a/main/gui/source/main.cpp +++ b/main/gui/source/main.cpp @@ -11,6 +11,7 @@ #include #include +#include namespace hex::init { @@ -61,8 +62,7 @@ int main(int argc, char **argv) { log::info("Linux distribution: {}. Version: {}", distro->name, distro->version == "" ? "None" : distro->version); } #endif - - + // Run ImHex return init::runImHex(); } diff --git a/main/gui/source/window/platform/linux.cpp b/main/gui/source/window/platform/linux.cpp index 766cffe67..78c2707dc 100644 --- a/main/gui/source/window/platform/linux.cpp +++ b/main/gui/source/window/platform/linux.cpp @@ -44,15 +44,6 @@ namespace hex { return false; } - 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 - } - #if defined(IMHEX_HAS_FONTCONFIG) static bool enumerateFontConfig() { if (!FcInit()) diff --git a/main/gui/source/window/platform/macos.cpp b/main/gui/source/window/platform/macos.cpp index aae035aab..0947b2374 100644 --- a/main/gui/source/window/platform/macos.cpp +++ b/main/gui/source/window/platform/macos.cpp @@ -22,11 +22,6 @@ namespace hex { - void nativeErrorMessage(const std::string &message) { - log::fatal("{}", message); - errorMessageMacos(message.c_str()); - } - void Window::configureGLFW() { glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); diff --git a/main/gui/source/window/platform/web.cpp b/main/gui/source/window/platform/web.cpp index cc13c0a81..53408d9d5 100644 --- a/main/gui/source/window/platform/web.cpp +++ b/main/gui/source/window/platform/web.cpp @@ -77,13 +77,6 @@ extern "C" void enterTouchMode() { namespace hex { - void nativeErrorMessage(const std::string &message) { - log::fatal("{}", message); - EM_ASM({ - alert(UTF8ToString($0)); - }, message.c_str()); - } - void Window::configureGLFW() { glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); diff --git a/main/gui/source/window/platform/windows.cpp b/main/gui/source/window/platform/windows.cpp index f132ffe42..ab0dbf10a 100644 --- a/main/gui/source/window/platform/windows.cpp +++ b/main/gui/source/window/platform/windows.cpp @@ -56,11 +56,6 @@ namespace hex { static Microsoft::WRL::ComPtr s_taskbarList; static bool s_useLayeredWindow = true; - void nativeErrorMessage(const std::string &message) { - log::fatal("{}", message); - MessageBoxA(nullptr, 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) {