mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-30 21:05:56 -05:00
feat: Added command line interface support (#1172)
System design has been discussed on discord Should fix #948 --------- Co-authored-by: WerWolv <werwolv98@gmail.com>
This commit is contained in:
@@ -357,6 +357,7 @@ namespace hex::init {
|
||||
ImHexApi::HexEditor::impl::getTooltipFunctions().clear();
|
||||
ImHexApi::System::getAdditionalFolderPaths().clear();
|
||||
ImHexApi::System::getCustomFontPath().clear();
|
||||
ImHexApi::Messaging::impl::getHandlers().clear();
|
||||
|
||||
ContentRegistry::Settings::impl::getEntries().clear();
|
||||
ContentRegistry::Settings::impl::getSettingsData().clear();
|
||||
@@ -418,11 +419,6 @@ namespace hex::init {
|
||||
}
|
||||
|
||||
bool loadPlugins() {
|
||||
// Load plugins
|
||||
for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Plugins)) {
|
||||
PluginManager::load(dir);
|
||||
}
|
||||
|
||||
// Get loaded plugins
|
||||
auto &plugins = PluginManager::getPlugins();
|
||||
|
||||
|
||||
@@ -4,20 +4,41 @@
|
||||
|
||||
#include "window.hpp"
|
||||
#include "crash_handlers.hpp"
|
||||
#include "messaging.hpp"
|
||||
|
||||
#include "init/splash_window.hpp"
|
||||
#include "init/tasks.hpp"
|
||||
|
||||
#include <hex/api/task.hpp>
|
||||
#include <hex/api/project_file_manager.hpp>
|
||||
#include <hex/api/plugin_manager.hpp>
|
||||
#include <hex/api/content_registry.hpp>
|
||||
#include <hex/helpers/fs.hpp>
|
||||
#include "hex/subcommands/subcommands.hpp"
|
||||
|
||||
#include <wolv/io/fs.hpp>
|
||||
#include <wolv/utils/guards.hpp>
|
||||
|
||||
using namespace hex;
|
||||
|
||||
void loadPlugins() {
|
||||
for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Plugins)) {
|
||||
PluginManager::load(dir);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv, char **envp) {
|
||||
using namespace hex;
|
||||
Window::initNative();
|
||||
|
||||
hex::crash::setupCrashHandlers();
|
||||
ImHexApi::System::impl::setProgramArguments(argc, argv, envp);
|
||||
hex::unused(envp);
|
||||
|
||||
std::vector<std::string> args(argv + 1, argv + argc);
|
||||
|
||||
loadPlugins();
|
||||
|
||||
hex::messaging::setupMessaging();
|
||||
hex::subcommands::processArguments(args);
|
||||
|
||||
// Check if ImHex is installed in portable mode
|
||||
{
|
||||
@@ -30,14 +51,14 @@ int main(int argc, char **argv, char **envp) {
|
||||
}
|
||||
|
||||
bool shouldRestart = false;
|
||||
// Register an event to handle restarting of ImHex
|
||||
EventManager::subscribe<RequestRestartImHex>([&]{ shouldRestart = true; });
|
||||
|
||||
do {
|
||||
// Register an event to handle restarting of ImHex
|
||||
EventManager::subscribe<RequestRestartImHex>([&]{ shouldRestart = true; });
|
||||
shouldRestart = false;
|
||||
|
||||
// Initialization
|
||||
{
|
||||
Window::initNative();
|
||||
|
||||
log::info("Welcome to ImHex {}!", ImHexApi::System::getImHexVersion());
|
||||
log::info("Compiled using commit {}@{}", ImHexApi::System::getCommitBranch(), ImHexApi::System::getCommitHash());
|
||||
@@ -69,14 +90,6 @@ int main(int argc, char **argv, char **envp) {
|
||||
// Main window
|
||||
{
|
||||
Window window;
|
||||
if (argc == 1)
|
||||
; // No arguments provided
|
||||
else if (argc >= 2) {
|
||||
for (auto i = 1; i < argc; i++) {
|
||||
if (auto argument = ImHexApi::System::getProgramArgument(i); argument.has_value())
|
||||
EventManager::post<RequestOpenFile>(argument.value());
|
||||
}
|
||||
}
|
||||
|
||||
// Open file that has been requested to be opened through other, OS-specific means
|
||||
if (auto path = hex::getInitialFilePath(); path.has_value()) {
|
||||
|
||||
33
main/source/messaging/common.cpp
Normal file
33
main/source/messaging/common.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#include <optional>
|
||||
|
||||
#include <hex/api/imhex_api.hpp>
|
||||
#include <hex/api/event.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
|
||||
#include "messaging.hpp"
|
||||
|
||||
namespace hex::messaging {
|
||||
|
||||
void messageReceived(const std::string &eventName, const std::vector<u8> &eventData) {
|
||||
log::debug("Received event '{}' with size {}", eventName, eventData.size());
|
||||
ImHexApi::Messaging::impl::runHandler(eventName, eventData);
|
||||
}
|
||||
|
||||
void setupEvents() {
|
||||
EventManager::subscribe<SendMessageToMainInstance>([](const std::string eventName, const std::vector<u8> &eventData) {
|
||||
log::debug("Forwarding message {} (maybe to us)", eventName);
|
||||
if (ImHexApi::System::isMainInstance()) {
|
||||
EventManager::subscribe<EventImHexStartupFinished>([eventName, eventData](){
|
||||
ImHexApi::Messaging::impl::runHandler(eventName, eventData);
|
||||
});
|
||||
} else {
|
||||
sendToOtherInstance(eventName, eventData);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void setupMessaging() {
|
||||
ImHexApi::System::impl::setMainInstanceStatus(setupNative());
|
||||
setupEvents();
|
||||
}
|
||||
}
|
||||
24
main/source/messaging/linux.cpp
Normal file
24
main/source/messaging/linux.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#if defined(OS_LINUX)
|
||||
|
||||
#include<stdexcept>
|
||||
|
||||
#include <hex/helpers/intrinsics.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
|
||||
#include "messaging.hpp"
|
||||
|
||||
namespace hex::messaging {
|
||||
|
||||
void sendToOtherInstance(const std::string &eventName, const std::vector<u8> &args) {
|
||||
hex::unused(eventName);
|
||||
hex::unused(args);
|
||||
log::error("Not implemented function sendToOtherInstance() called");
|
||||
}
|
||||
|
||||
// Not implemented, so lets say we are the main instance every time so events are forwarded to ourselves
|
||||
bool setupNative() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
24
main/source/messaging/macos.cpp
Normal file
24
main/source/messaging/macos.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#if defined(OS_MACOS)
|
||||
|
||||
#include<stdexcept>
|
||||
|
||||
#include <hex/helpers/intrinsics.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
|
||||
#include "messaging.hpp"
|
||||
|
||||
namespace hex::messaging {
|
||||
|
||||
void sendToOtherInstance(const std::string &eventName, const std::vector<u8> &args) {
|
||||
hex::unused(eventName);
|
||||
hex::unused(args);
|
||||
log::error("Not implemented function sendToOtherInstance() called");
|
||||
}
|
||||
|
||||
// Not implemented, so lets say we are the main instance every time so events are forwarded to ourselves
|
||||
bool setupNative() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
84
main/source/messaging/win.cpp
Normal file
84
main/source/messaging/win.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
#if defined(OS_WINDOWS)
|
||||
|
||||
#include "messaging.hpp"
|
||||
|
||||
#include <hex/api/imhex_api.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace hex::messaging {
|
||||
|
||||
std::optional<HWND> getImHexWindow() {
|
||||
|
||||
HWND imhexWindow = 0;
|
||||
|
||||
::EnumWindows([](HWND hWnd, LPARAM ret) -> BOOL {
|
||||
// Get the window name
|
||||
auto length = ::GetWindowTextLength(hWnd);
|
||||
std::string windowName(length + 1, '\x00');
|
||||
::GetWindowText(hWnd, windowName.data(), windowName.size());
|
||||
|
||||
// Check if the window is visible and if it's an ImHex window
|
||||
if (::IsWindowVisible(hWnd) == TRUE && length != 0) {
|
||||
if (windowName.starts_with("ImHex")) {
|
||||
// it's our window, return it and stop iteration
|
||||
*reinterpret_cast<HWND*>(ret) = hWnd;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// continue iteration
|
||||
return TRUE;
|
||||
|
||||
}, reinterpret_cast<LPARAM>(&imhexWindow));
|
||||
|
||||
if (imhexWindow == 0) return { };
|
||||
else return imhexWindow;
|
||||
}
|
||||
|
||||
void sendToOtherInstance(const std::string &eventName, const std::vector<u8> &eventData) {
|
||||
log::debug("Sending event {} to another instance (not us)", eventName);
|
||||
|
||||
// Get the window we want to send it to
|
||||
HWND imHexWindow = *getImHexWindow();
|
||||
|
||||
// Create the message
|
||||
// TODO actually send all arguments and not just the eventName
|
||||
|
||||
std::vector<u8> fulleventData(eventName.begin(), eventName.end());
|
||||
fulleventData.push_back('\0');
|
||||
|
||||
fulleventData.insert(fulleventData.end(), eventData.begin(), eventData.end());
|
||||
|
||||
u8 *data = &fulleventData[0];
|
||||
DWORD dataSize = static_cast<DWORD>(fulleventData.size());
|
||||
|
||||
COPYDATASTRUCT message = {
|
||||
.dwData = 0,
|
||||
.cbData = dataSize,
|
||||
.lpData = data
|
||||
};
|
||||
|
||||
// Send the message
|
||||
SendMessage(imHexWindow, WM_COPYDATA, reinterpret_cast<WPARAM>(imHexWindow), reinterpret_cast<LPARAM>(&message));
|
||||
|
||||
}
|
||||
|
||||
bool setupNative() {
|
||||
|
||||
constexpr static auto UniqueMutexId = "ImHex/a477ea68-e334-4d07-a439-4f159c683763";
|
||||
|
||||
// check if an ImHex instance is already running by opening a global mutex
|
||||
HANDLE globalMutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, UniqueMutexId);
|
||||
if (globalMutex == nullptr) {
|
||||
// If no ImHex instance is running, create a new global mutex
|
||||
globalMutex = CreateMutex(nullptr, FALSE, UniqueMutexId);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "window.hpp"
|
||||
|
||||
#include "messaging.hpp"
|
||||
|
||||
#include <hex/api/content_registry.hpp>
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
@@ -53,12 +55,28 @@ namespace hex {
|
||||
auto message = reinterpret_cast<COPYDATASTRUCT *>(lParam);
|
||||
if (message == nullptr) break;
|
||||
|
||||
auto data = reinterpret_cast<const char8_t *>(message->lpData);
|
||||
if (data == nullptr) break;
|
||||
ssize_t nullIndex = -1;
|
||||
|
||||
std::fs::path path = data;
|
||||
log::info("Opening file in existing instance: {}", wolv::util::toUTF8String(path));
|
||||
EventManager::post<RequestOpenFile>(path);
|
||||
char* messageData = reinterpret_cast<char*>(message->lpData);
|
||||
size_t messageSize = message->cbData;
|
||||
|
||||
for (size_t i = 0; i < messageSize; i++) {
|
||||
if (messageData[i] == '\0') {
|
||||
nullIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (nullIndex == -1) {
|
||||
log::warn("Received invalid forwarded event");
|
||||
break;
|
||||
}
|
||||
|
||||
std::string eventName(messageData, nullIndex);
|
||||
|
||||
std::vector<u8> eventData(messageData + nullIndex + 1, messageData + messageSize);
|
||||
|
||||
hex::messaging::messageReceived(eventName, eventData);
|
||||
break;
|
||||
}
|
||||
case WM_SETTINGCHANGE: {
|
||||
@@ -214,6 +232,24 @@ namespace hex {
|
||||
|
||||
|
||||
void Window::initNative() {
|
||||
HWND consoleWindow = ::GetConsoleWindow();
|
||||
DWORD processId = 0;
|
||||
::GetWindowThreadProcessId(consoleWindow, &processId);
|
||||
if (GetCurrentProcessId() == processId) {
|
||||
ShowWindow(consoleWindow, SW_HIDE);
|
||||
FreeConsole();
|
||||
log::impl::redirectToFile();
|
||||
} else {
|
||||
auto hConsole = ::GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (hConsole != INVALID_HANDLE_VALUE) {
|
||||
DWORD mode = 0;
|
||||
if (::GetConsoleMode(hConsole, &mode) == TRUE) {
|
||||
mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_PROCESSED_OUTPUT;
|
||||
::SetConsoleMode(hConsole, mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImHexApi::System::impl::setBorderlessWindowMode(true);
|
||||
|
||||
// Add plugin library folders to dll search path
|
||||
@@ -226,83 +262,6 @@ namespace hex {
|
||||
// We redirect stderr to NUL to prevent this
|
||||
freopen("NUL:", "w", stderr);
|
||||
setvbuf(stderr, nullptr, _IONBF, 0);
|
||||
|
||||
// Attach to parent console if one exists
|
||||
bool result = AttachConsole(ATTACH_PARENT_PROCESS) == TRUE;
|
||||
|
||||
#if defined(DEBUG)
|
||||
if (::GetLastError() == ERROR_INVALID_HANDLE) {
|
||||
result = AllocConsole() == TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (result) {
|
||||
// Redirect stdin and stdout to that new console
|
||||
freopen("CONIN$", "r", stdin);
|
||||
freopen("CONOUT$", "w", stdout);
|
||||
setvbuf(stdin, nullptr, _IONBF, 0);
|
||||
setvbuf(stdout, nullptr, _IONBF, 0);
|
||||
|
||||
fmt::print("\n");
|
||||
|
||||
// Enable color format specifiers in console
|
||||
{
|
||||
auto hConsole = ::GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (hConsole != INVALID_HANDLE_VALUE) {
|
||||
DWORD mode = 0;
|
||||
if (::GetConsoleMode(hConsole, &mode) == TRUE) {
|
||||
mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_PROCESSED_OUTPUT;
|
||||
::SetConsoleMode(hConsole, mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log::impl::redirectToFile();
|
||||
}
|
||||
|
||||
// Open new files in already existing ImHex instance
|
||||
constexpr static auto UniqueMutexId = "ImHex/a477ea68-e334-4d07-a439-4f159c683763";
|
||||
|
||||
HANDLE globalMutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, UniqueMutexId);
|
||||
if (globalMutex == nullptr) {
|
||||
// If no ImHex instance is running, create a new global mutex
|
||||
globalMutex = CreateMutex(nullptr, FALSE, UniqueMutexId);
|
||||
} else {
|
||||
// If an ImHex instance is already running, send the file path to it and exit
|
||||
|
||||
if (ImHexApi::System::getProgramArguments().argc > 1) {
|
||||
// Find the ImHex Window and send the file path as a message to it
|
||||
::EnumWindows([](HWND hWnd, LPARAM) -> BOOL {
|
||||
auto &programArgs = ImHexApi::System::getProgramArguments();
|
||||
|
||||
// Get the window name
|
||||
auto length = ::GetWindowTextLength(hWnd);
|
||||
std::string windowName(length + 1, '\x00');
|
||||
::GetWindowText(hWnd, windowName.data(), windowName.size());
|
||||
|
||||
// Check if the window is visible and if it's an ImHex window
|
||||
if (::IsWindowVisible(hWnd) == TRUE && length != 0) {
|
||||
if (windowName.starts_with("ImHex")) {
|
||||
// Create the message
|
||||
COPYDATASTRUCT message = {
|
||||
.dwData = 0,
|
||||
.cbData = static_cast<DWORD>(std::strlen(programArgs.argv[1])) + 1,
|
||||
.lpData = programArgs.argv[1]
|
||||
};
|
||||
|
||||
// Send the message
|
||||
SendMessage(hWnd, WM_COPYDATA, reinterpret_cast<WPARAM>(hWnd), reinterpret_cast<LPARAM>(&message));
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}, 0);
|
||||
|
||||
std::exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Window::setupNativeWindow() {
|
||||
|
||||
Reference in New Issue
Block a user