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:
parent
8c0395bc7c
commit
1ed658bcdc
@ -100,7 +100,6 @@ macro(configurePackingResources)
|
|||||||
if (WIN32)
|
if (WIN32)
|
||||||
set(APPLICATION_TYPE)
|
set(APPLICATION_TYPE)
|
||||||
set(IMHEX_ICON "${IMHEX_BASE_FOLDER}/resources/resource.rc")
|
set(IMHEX_ICON "${IMHEX_BASE_FOLDER}/resources/resource.rc")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,-subsystem,windows")
|
|
||||||
|
|
||||||
if (CREATE_PACKAGE)
|
if (CREATE_PACKAGE)
|
||||||
set(CPACK_GENERATOR "WIX")
|
set(CPACK_GENERATOR "WIX")
|
||||||
|
@ -38,6 +38,8 @@ set(LIBIMHEX_SOURCES
|
|||||||
source/ui/imgui_imhex_extensions.cpp
|
source/ui/imgui_imhex_extensions.cpp
|
||||||
source/ui/view.cpp
|
source/ui/view.cpp
|
||||||
source/ui/popup.cpp
|
source/ui/popup.cpp
|
||||||
|
|
||||||
|
source/subcommands/subcommands.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
|
@ -232,4 +232,10 @@ namespace hex {
|
|||||||
EVENT_DEF(RequestOpenInfoPopup, const std::string);
|
EVENT_DEF(RequestOpenInfoPopup, const std::string);
|
||||||
EVENT_DEF(RequestOpenErrorPopup, const std::string);
|
EVENT_DEF(RequestOpenErrorPopup, const std::string);
|
||||||
EVENT_DEF(RequestOpenFatalPopup, const std::string);
|
EVENT_DEF(RequestOpenFatalPopup, const std::string);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Send an event to the main Imhex instance
|
||||||
|
*/
|
||||||
|
EVENT_DEF(SendMessageToMainInstance, const std::string, const std::vector<u8>&);
|
||||||
}
|
}
|
@ -322,7 +322,10 @@ namespace hex {
|
|||||||
/* Functions to interact with various ImHex system settings */
|
/* Functions to interact with various ImHex system settings */
|
||||||
namespace System {
|
namespace System {
|
||||||
|
|
||||||
|
bool isMainInstance();
|
||||||
|
|
||||||
namespace impl {
|
namespace impl {
|
||||||
|
void setMainInstanceStatus(bool status);
|
||||||
|
|
||||||
void setMainWindowPosition(i32 x, i32 y);
|
void setMainWindowPosition(i32 x, i32 y);
|
||||||
void setMainWindowSize(u32 width, u32 height);
|
void setMainWindowSize(u32 width, u32 height);
|
||||||
@ -331,8 +334,6 @@ namespace hex {
|
|||||||
void setGlobalScale(float scale);
|
void setGlobalScale(float scale);
|
||||||
void setNativeScale(float scale);
|
void setNativeScale(float scale);
|
||||||
|
|
||||||
void setProgramArguments(int argc, char **argv, char **envp);
|
|
||||||
|
|
||||||
void setBorderlessWindowMode(bool enabled);
|
void setBorderlessWindowMode(bool enabled);
|
||||||
|
|
||||||
void setCustomFontPath(const std::fs::path &path);
|
void setCustomFontPath(const std::fs::path &path);
|
||||||
@ -383,20 +384,6 @@ namespace hex {
|
|||||||
void setTaskBarProgress(TaskProgressState state, TaskProgressType type, u32 progress);
|
void setTaskBarProgress(TaskProgressState state, TaskProgressType type, u32 progress);
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Gets the current program arguments
|
|
||||||
* @return The current program arguments
|
|
||||||
*/
|
|
||||||
const ProgramArguments &getProgramArguments();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Gets a program argument
|
|
||||||
* @param index The index of the argument to get
|
|
||||||
* @return The argument at the given index
|
|
||||||
*/
|
|
||||||
std::optional<std::u8string> getProgramArgument(int index);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Gets the current target FPS
|
* @brief Gets the current target FPS
|
||||||
* @return The current target FPS
|
* @return The current target FPS
|
||||||
@ -545,6 +532,26 @@ namespace hex {
|
|||||||
std::string getCommitBranch();
|
std::string getCommitBranch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Cross-instance messaging system
|
||||||
|
* This allows you to send messages to the "main" instance of ImHex running, from any other instance
|
||||||
|
*/
|
||||||
|
namespace Messaging {
|
||||||
|
|
||||||
|
namespace impl {
|
||||||
|
using MessagingHandler = std::function<void(const std::vector<u8> &)>;
|
||||||
|
|
||||||
|
std::map<std::string, MessagingHandler> &getHandlers();
|
||||||
|
|
||||||
|
void runHandler(const std::string &eventName, const std::vector<u8> &args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Register the handler for this specific event name
|
||||||
|
*/
|
||||||
|
void registerHandler(const std::string &eventName, const impl::MessagingHandler &handler);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <hex/helpers/fmt.hpp>
|
#include <hex/helpers/fmt.hpp>
|
||||||
#include <hex/helpers/fs.hpp>
|
#include <hex/helpers/fs.hpp>
|
||||||
|
|
||||||
|
#include <span>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#if defined(OS_WINDOWS)
|
#if defined(OS_WINDOWS)
|
||||||
@ -17,6 +18,17 @@ struct ImGuiContext;
|
|||||||
|
|
||||||
namespace hex {
|
namespace hex {
|
||||||
|
|
||||||
|
struct SubCommand {
|
||||||
|
std::string commandKey;
|
||||||
|
std::string commandDesc;
|
||||||
|
std::function<void(const std::vector<std::string>&)> callback;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SubCommandList {
|
||||||
|
hex::SubCommand *subCommands;
|
||||||
|
size_t size;
|
||||||
|
};
|
||||||
|
|
||||||
class Plugin {
|
class Plugin {
|
||||||
public:
|
public:
|
||||||
explicit Plugin(const std::fs::path &path);
|
explicit Plugin(const std::fs::path &path);
|
||||||
@ -36,6 +48,8 @@ namespace hex {
|
|||||||
|
|
||||||
[[nodiscard]] bool isLoaded() const;
|
[[nodiscard]] bool isLoaded() const;
|
||||||
|
|
||||||
|
[[nodiscard]] std::span<SubCommand> getSubCommands() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using InitializePluginFunc = void (*)();
|
using InitializePluginFunc = void (*)();
|
||||||
using GetPluginNameFunc = const char *(*)();
|
using GetPluginNameFunc = const char *(*)();
|
||||||
@ -44,6 +58,7 @@ namespace hex {
|
|||||||
using GetCompatibleVersionFunc = const char *(*)();
|
using GetCompatibleVersionFunc = const char *(*)();
|
||||||
using SetImGuiContextFunc = void (*)(ImGuiContext *);
|
using SetImGuiContextFunc = void (*)(ImGuiContext *);
|
||||||
using IsBuiltinPluginFunc = bool (*)();
|
using IsBuiltinPluginFunc = bool (*)();
|
||||||
|
using GetSubCommandsFunc = SubCommandList* (*)();
|
||||||
|
|
||||||
#if defined(OS_WINDOWS)
|
#if defined(OS_WINDOWS)
|
||||||
HMODULE m_handle = nullptr;
|
HMODULE m_handle = nullptr;
|
||||||
@ -61,6 +76,7 @@ namespace hex {
|
|||||||
GetCompatibleVersionFunc m_getCompatibleVersionFunction = nullptr;
|
GetCompatibleVersionFunc m_getCompatibleVersionFunction = nullptr;
|
||||||
SetImGuiContextFunc m_setImGuiContextFunction = nullptr;
|
SetImGuiContextFunc m_setImGuiContextFunction = nullptr;
|
||||||
IsBuiltinPluginFunc m_isBuiltinPluginFunction = nullptr;
|
IsBuiltinPluginFunc m_isBuiltinPluginFunction = nullptr;
|
||||||
|
GetSubCommandsFunc m_getSubCommandsFunction = nullptr;
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
[[nodiscard]] auto getPluginFunction(const std::string &symbol) {
|
[[nodiscard]] auto getPluginFunction(const std::string &symbol) {
|
||||||
|
@ -1,9 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <imgui_internal.h>
|
#include <imgui_internal.h>
|
||||||
|
|
||||||
#include <hex.hpp>
|
#include <hex.hpp>
|
||||||
|
#include <hex/api/plugin_manager.hpp>
|
||||||
|
|
||||||
|
#include <wolv/utils/string.hpp>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This macro is used to define all the required entry points for a plugin.
|
* This macro is used to define all the required entry points for a plugin.
|
||||||
@ -21,3 +26,23 @@
|
|||||||
GImGui = ctx; \
|
GImGui = ctx; \
|
||||||
} \
|
} \
|
||||||
extern "C" [[gnu::visibility("default")]] void initializePlugin()
|
extern "C" [[gnu::visibility("default")]] void initializePlugin()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This macro is used to define subcommands defined by the plugin
|
||||||
|
* A subcommand consists of a key, a description, and a callback
|
||||||
|
* The key is what the first argument to ImHex should be, prefixed by `--`
|
||||||
|
* For example, if the key if `help`, ImHex should be started with `--help` as its first argument to trigger the subcommand
|
||||||
|
* when the subcommand is triggerred, it's callback will be executed. The callback is executed BEFORE most of ImHex initialization
|
||||||
|
* so to do anything meaningful, you should subscribe to an event (like EventImHexStartupFinished) and run your code there.
|
||||||
|
*/
|
||||||
|
#define IMHEX_PLUGIN_SUBCOMMANDS() IMHEX_PLUGIN_SUBCOMMANDS_IMPL()
|
||||||
|
|
||||||
|
#define IMHEX_PLUGIN_SUBCOMMANDS_IMPL() \
|
||||||
|
extern std::vector<hex::SubCommand> g_subCommands; \
|
||||||
|
extern "C" [[gnu::visibility("default")]] hex::SubCommandList getSubCommands() { \
|
||||||
|
return hex::SubCommandList { \
|
||||||
|
g_subCommands.data(), \
|
||||||
|
g_subCommands.size() \
|
||||||
|
}; \
|
||||||
|
} \
|
||||||
|
std::vector<hex::SubCommand> g_subCommands
|
||||||
|
30
lib/libimhex/include/hex/subcommands/subcommands.hpp
Normal file
30
lib/libimhex/include/hex/subcommands/subcommands.hpp
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include<vector>
|
||||||
|
#include<string>
|
||||||
|
#include<functional>
|
||||||
|
|
||||||
|
namespace hex::subcommands {
|
||||||
|
/**
|
||||||
|
* @brief Internal method - takes all the arguments ImHex received from the command line,
|
||||||
|
* and determine which subcommands to run, with which arguments.
|
||||||
|
* In some cases, the subcommand or this function directly might exit the program
|
||||||
|
* (e.g. --help, or when forwarding providers to open to another instance)
|
||||||
|
* and so this function might not return
|
||||||
|
*/
|
||||||
|
void processArguments(const std::vector<std::string> &args);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Forward the given command to the main instance (might be this instance)
|
||||||
|
* The callback will be executed after EventImHexStartupFinished
|
||||||
|
*/
|
||||||
|
void forwardSubCommand(const std::string &cmdName, const std::vector<std::string> &args);
|
||||||
|
|
||||||
|
using ForwardCommandHandler = std::function<void(const std::vector<std::string> &)>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Register the handler for this specific command name
|
||||||
|
*/
|
||||||
|
void registerSubCommand(const std::string &cmdName, const ForwardCommandHandler &handler);
|
||||||
|
}
|
@ -335,8 +335,16 @@ namespace hex {
|
|||||||
|
|
||||||
namespace ImHexApi::System {
|
namespace ImHexApi::System {
|
||||||
|
|
||||||
|
|
||||||
namespace impl {
|
namespace impl {
|
||||||
|
|
||||||
|
// default to true means we forward to ourselves by default
|
||||||
|
static bool s_isMainInstance = true;
|
||||||
|
|
||||||
|
void setMainInstanceStatus(bool status) {
|
||||||
|
s_isMainInstance = status;
|
||||||
|
}
|
||||||
|
|
||||||
static ImVec2 s_mainWindowPos;
|
static ImVec2 s_mainWindowPos;
|
||||||
static ImVec2 s_mainWindowSize;
|
static ImVec2 s_mainWindowSize;
|
||||||
void setMainWindowPosition(i32 x, i32 y) {
|
void setMainWindowPosition(i32 x, i32 y) {
|
||||||
@ -364,13 +372,6 @@ namespace hex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static ProgramArguments s_programArguments;
|
|
||||||
void setProgramArguments(int argc, char **argv, char **envp) {
|
|
||||||
s_programArguments.argc = argc;
|
|
||||||
s_programArguments.argv = argv;
|
|
||||||
s_programArguments.envp = envp;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool s_borderlessWindowMode;
|
static bool s_borderlessWindowMode;
|
||||||
void setBorderlessWindowMode(bool enabled) {
|
void setBorderlessWindowMode(bool enabled) {
|
||||||
s_borderlessWindowMode = enabled;
|
s_borderlessWindowMode = enabled;
|
||||||
@ -405,6 +406,10 @@ namespace hex {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isMainInstance() {
|
||||||
|
return impl::s_isMainInstance;
|
||||||
|
}
|
||||||
|
|
||||||
void closeImHex(bool noQuestions) {
|
void closeImHex(bool noQuestions) {
|
||||||
EventManager::post<RequestCloseImHex>(noQuestions);
|
EventManager::post<RequestCloseImHex>(noQuestions);
|
||||||
}
|
}
|
||||||
@ -418,25 +423,6 @@ namespace hex {
|
|||||||
EventManager::post<EventSetTaskBarIconState>(u32(state), u32(type), progress);
|
EventManager::post<EventSetTaskBarIconState>(u32(state), u32(type), progress);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProgramArguments &getProgramArguments() {
|
|
||||||
return impl::s_programArguments;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::u8string> getProgramArgument(int index) {
|
|
||||||
if (index >= impl::s_programArguments.argc) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(OS_WINDOWS)
|
|
||||||
std::wstring wideArg = ::CommandLineToArgvW(::GetCommandLineW(), &impl::s_programArguments.argc)[index];
|
|
||||||
std::string byteArg = std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t>().to_bytes(wideArg);
|
|
||||||
|
|
||||||
return std::u8string(byteArg.begin(), byteArg.end());
|
|
||||||
#else
|
|
||||||
return std::u8string(reinterpret_cast<const char8_t *>(impl::s_programArguments.argv[index]));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static float s_targetFPS = 14.0F;
|
static float s_targetFPS = 14.0F;
|
||||||
|
|
||||||
@ -621,4 +607,36 @@ namespace hex {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace ImHexApi::Messaging {
|
||||||
|
|
||||||
|
namespace impl {
|
||||||
|
|
||||||
|
std::map<std::string, MessagingHandler> &getHandlers() {
|
||||||
|
static std::map<std::string, MessagingHandler> handlers;
|
||||||
|
|
||||||
|
return handlers;
|
||||||
|
}
|
||||||
|
|
||||||
|
void runHandler(const std::string &eventName, const std::vector<u8> &args) {
|
||||||
|
const auto& handlers = impl::getHandlers();
|
||||||
|
auto matchHandler = handlers.find(eventName);
|
||||||
|
|
||||||
|
if (matchHandler == handlers.end()) {
|
||||||
|
log::error("Forward event handler {} not found", eventName);
|
||||||
|
} else {
|
||||||
|
matchHandler->second(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void registerHandler(const std::string &eventName, const impl::MessagingHandler &handler) {
|
||||||
|
log::debug("Registered new forward event handler: {}", eventName);
|
||||||
|
|
||||||
|
impl::getHandlers().insert({ eventName, handler });
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@ namespace hex {
|
|||||||
this->m_getCompatibleVersionFunction = getPluginFunction<GetCompatibleVersionFunc>("getCompatibleVersion");
|
this->m_getCompatibleVersionFunction = getPluginFunction<GetCompatibleVersionFunc>("getCompatibleVersion");
|
||||||
this->m_setImGuiContextFunction = getPluginFunction<SetImGuiContextFunc>("setImGuiContext");
|
this->m_setImGuiContextFunction = getPluginFunction<SetImGuiContextFunc>("setImGuiContext");
|
||||||
this->m_isBuiltinPluginFunction = getPluginFunction<IsBuiltinPluginFunc>("isBuiltinPlugin");
|
this->m_isBuiltinPluginFunction = getPluginFunction<IsBuiltinPluginFunc>("isBuiltinPlugin");
|
||||||
|
this->m_getSubCommandsFunction = getPluginFunction<GetSubCommandsFunc>("getSubCommands");
|
||||||
}
|
}
|
||||||
|
|
||||||
Plugin::Plugin(Plugin &&other) noexcept {
|
Plugin::Plugin(Plugin &&other) noexcept {
|
||||||
@ -48,6 +49,7 @@ namespace hex {
|
|||||||
this->m_getCompatibleVersionFunction = other.m_getCompatibleVersionFunction;
|
this->m_getCompatibleVersionFunction = other.m_getCompatibleVersionFunction;
|
||||||
this->m_setImGuiContextFunction = other.m_setImGuiContextFunction;
|
this->m_setImGuiContextFunction = other.m_setImGuiContextFunction;
|
||||||
this->m_isBuiltinPluginFunction = other.m_isBuiltinPluginFunction;
|
this->m_isBuiltinPluginFunction = other.m_isBuiltinPluginFunction;
|
||||||
|
this->m_getSubCommandsFunction = other.m_getSubCommandsFunction;
|
||||||
|
|
||||||
other.m_handle = nullptr;
|
other.m_handle = nullptr;
|
||||||
other.m_initializePluginFunction = nullptr;
|
other.m_initializePluginFunction = nullptr;
|
||||||
@ -57,6 +59,7 @@ namespace hex {
|
|||||||
other.m_getCompatibleVersionFunction = nullptr;
|
other.m_getCompatibleVersionFunction = nullptr;
|
||||||
other.m_setImGuiContextFunction = nullptr;
|
other.m_setImGuiContextFunction = nullptr;
|
||||||
other.m_isBuiltinPluginFunction = nullptr;
|
other.m_isBuiltinPluginFunction = nullptr;
|
||||||
|
other.m_getSubCommandsFunction = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Plugin::~Plugin() {
|
Plugin::~Plugin() {
|
||||||
@ -141,6 +144,14 @@ namespace hex {
|
|||||||
return this->m_initialized;
|
return this->m_initialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::span<SubCommand> Plugin::getSubCommands() const {
|
||||||
|
if (this->m_getSubCommandsFunction != nullptr) {
|
||||||
|
auto result = this->m_getSubCommandsFunction();
|
||||||
|
return { result->subCommands, result->size };
|
||||||
|
} else
|
||||||
|
return { };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void *Plugin::getPluginFunction(const std::string &symbol) {
|
void *Plugin::getPluginFunction(const std::string &symbol) {
|
||||||
#if defined(OS_WINDOWS)
|
#if defined(OS_WINDOWS)
|
||||||
|
120
lib/libimhex/source/subcommands/subcommands.cpp
Normal file
120
lib/libimhex/source/subcommands/subcommands.cpp
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
#include<iostream>
|
||||||
|
#include<numeric>
|
||||||
|
#include<string_view>
|
||||||
|
#include<ranges>
|
||||||
|
#include<stdlib.h>
|
||||||
|
|
||||||
|
#include "hex/subcommands/subcommands.hpp"
|
||||||
|
|
||||||
|
#include <hex/api/event.hpp>
|
||||||
|
#include <hex/api/plugin_manager.hpp>
|
||||||
|
#include <hex/api/imhex_api.hpp>
|
||||||
|
#include <hex/helpers/logger.hpp>
|
||||||
|
|
||||||
|
namespace hex::subcommands {
|
||||||
|
|
||||||
|
std::optional<SubCommand> findSubCommand(const std::string &arg) {
|
||||||
|
for (auto &plugin : PluginManager::getPlugins()) {
|
||||||
|
for (auto &subCommand : plugin.getSubCommands()) {
|
||||||
|
if (hex::format("--{}", subCommand.commandKey) == arg) {
|
||||||
|
return subCommand;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void processArguments(const std::vector<std::string> &args) {
|
||||||
|
// If no arguments, do not even try to process arguments
|
||||||
|
// (important because this function will exit ImHex if an instance is already opened,
|
||||||
|
// and we don't want that if no arguments were provided)
|
||||||
|
if (args.empty()) return;
|
||||||
|
|
||||||
|
std::vector<std::pair<SubCommand, std::vector<std::string>>> subCommands;
|
||||||
|
|
||||||
|
auto argsIter = args.begin();
|
||||||
|
|
||||||
|
// get subcommand associated with the first argument
|
||||||
|
std::optional<SubCommand> currentSubCommand = findSubCommand(*argsIter);
|
||||||
|
|
||||||
|
if (currentSubCommand) {
|
||||||
|
argsIter++;
|
||||||
|
// if it is a valid subcommand, remove it from the argument list
|
||||||
|
} else {
|
||||||
|
// if no (valid) subcommand was provided, the default one is --open
|
||||||
|
currentSubCommand = findSubCommand("--open");
|
||||||
|
}
|
||||||
|
|
||||||
|
// arguments of the current subcommand
|
||||||
|
std::vector<std::string> currentSubCommandArgs;
|
||||||
|
|
||||||
|
// compute all subcommands to run
|
||||||
|
while (argsIter != args.end()) {
|
||||||
|
const std::string &arg = *argsIter;
|
||||||
|
|
||||||
|
if (arg == "--othercmd") {
|
||||||
|
// save command to run
|
||||||
|
if (currentSubCommand) {
|
||||||
|
subCommands.push_back({*currentSubCommand, currentSubCommandArgs});
|
||||||
|
}
|
||||||
|
|
||||||
|
currentSubCommand = std::nullopt;
|
||||||
|
currentSubCommandArgs = { };
|
||||||
|
|
||||||
|
} else if (currentSubCommand) {
|
||||||
|
// add current argument to the current command
|
||||||
|
currentSubCommandArgs.push_back(arg);
|
||||||
|
} else {
|
||||||
|
// get next subcommand from current argument
|
||||||
|
currentSubCommand = findSubCommand(arg);
|
||||||
|
if (!currentSubCommand) {
|
||||||
|
log::error("No subcommand named '{}' found", arg);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
argsIter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// save last command to run
|
||||||
|
if (currentSubCommand) {
|
||||||
|
subCommands.push_back({*currentSubCommand, currentSubCommandArgs});
|
||||||
|
}
|
||||||
|
|
||||||
|
// run the subcommands
|
||||||
|
for (auto& subCommandPair : subCommands) {
|
||||||
|
subCommandPair.first.callback(subCommandPair.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
// exit the process if its not the main instance (the commands have been forwarded to another instance)
|
||||||
|
if (!ImHexApi::System::isMainInstance()) {
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void forwardSubCommand(const std::string &cmdName, const std::vector<std::string> &args) {
|
||||||
|
log::debug("Forwarding subcommand {} (maybe to us)", cmdName);
|
||||||
|
std::string dataStr = std::accumulate(args.begin(), args.end(), std::string("\0"));
|
||||||
|
|
||||||
|
std::vector<u8> data(dataStr.begin(), dataStr.end());
|
||||||
|
|
||||||
|
EventManager::post<SendMessageToMainInstance>(hex::format("command/{}", cmdName), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void registerSubCommand(const std::string &cmdName, const ForwardCommandHandler &handler) {
|
||||||
|
log::debug("Registered new forward command handler: {}", cmdName);
|
||||||
|
|
||||||
|
ImHexApi::Messaging::impl::getHandlers().insert({ hex::format("command/{}", cmdName), [handler](const std::vector<u8> &eventData){
|
||||||
|
std::string str((const char*) eventData.data(), eventData.size());
|
||||||
|
|
||||||
|
std::vector<std::string> args;
|
||||||
|
|
||||||
|
for (const auto &arg_view : std::views::split(str, '\0')) {
|
||||||
|
std::string arg(arg_view.data(), arg_view.size());
|
||||||
|
args.push_back(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
handler(args);
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,11 @@ add_executable(main ${APPLICATION_TYPE}
|
|||||||
source/window/macos_window.cpp
|
source/window/macos_window.cpp
|
||||||
source/window/linux_window.cpp
|
source/window/linux_window.cpp
|
||||||
|
|
||||||
|
source/messaging/common.cpp
|
||||||
|
source/messaging/linux.cpp
|
||||||
|
source/messaging/macos.cpp
|
||||||
|
source/messaging/win.cpp
|
||||||
|
|
||||||
source/init/splash_window.cpp
|
source/init/splash_window.cpp
|
||||||
source/init/tasks.cpp
|
source/init/tasks.cpp
|
||||||
|
|
||||||
|
35
main/include/messaging.hpp
Normal file
35
main/include/messaging.hpp
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include<string>
|
||||||
|
#include<vector>
|
||||||
|
|
||||||
|
#include<hex/helpers/types.hpp>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cross-instance (cross-process) messaging system
|
||||||
|
* As of now, this system may not be stable for use beyond its current use:
|
||||||
|
* forwarding providers opened in new instances
|
||||||
|
*/
|
||||||
|
namespace hex::messaging {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Setup everything to be able to send/receive messages
|
||||||
|
*/
|
||||||
|
void setupMessaging();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Internal method - setup platform-specific things to be able to send messages
|
||||||
|
* @return if this instance has been determined to be the main instance
|
||||||
|
*/
|
||||||
|
bool setupNative();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Internal method - send a message to another Imhex instance in a platform-specific way
|
||||||
|
*/
|
||||||
|
void sendToOtherInstance(const std::string &eventName, const std::vector<u8> &args);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Internal method - called by platform-specific code when a event has been received
|
||||||
|
*/
|
||||||
|
void messageReceived(const std::string &eventName, const std::vector<u8> &args);
|
||||||
|
}
|
@ -357,6 +357,7 @@ namespace hex::init {
|
|||||||
ImHexApi::HexEditor::impl::getTooltipFunctions().clear();
|
ImHexApi::HexEditor::impl::getTooltipFunctions().clear();
|
||||||
ImHexApi::System::getAdditionalFolderPaths().clear();
|
ImHexApi::System::getAdditionalFolderPaths().clear();
|
||||||
ImHexApi::System::getCustomFontPath().clear();
|
ImHexApi::System::getCustomFontPath().clear();
|
||||||
|
ImHexApi::Messaging::impl::getHandlers().clear();
|
||||||
|
|
||||||
ContentRegistry::Settings::impl::getEntries().clear();
|
ContentRegistry::Settings::impl::getEntries().clear();
|
||||||
ContentRegistry::Settings::impl::getSettingsData().clear();
|
ContentRegistry::Settings::impl::getSettingsData().clear();
|
||||||
@ -418,11 +419,6 @@ namespace hex::init {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool loadPlugins() {
|
bool loadPlugins() {
|
||||||
// Load plugins
|
|
||||||
for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Plugins)) {
|
|
||||||
PluginManager::load(dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get loaded plugins
|
// Get loaded plugins
|
||||||
auto &plugins = PluginManager::getPlugins();
|
auto &plugins = PluginManager::getPlugins();
|
||||||
|
|
||||||
|
@ -4,20 +4,41 @@
|
|||||||
|
|
||||||
#include "window.hpp"
|
#include "window.hpp"
|
||||||
#include "crash_handlers.hpp"
|
#include "crash_handlers.hpp"
|
||||||
|
#include "messaging.hpp"
|
||||||
|
|
||||||
#include "init/splash_window.hpp"
|
#include "init/splash_window.hpp"
|
||||||
#include "init/tasks.hpp"
|
#include "init/tasks.hpp"
|
||||||
|
|
||||||
#include <hex/api/task.hpp>
|
#include <hex/api/task.hpp>
|
||||||
#include <hex/api/project_file_manager.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/io/fs.hpp>
|
||||||
#include <wolv/utils/guards.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) {
|
int main(int argc, char **argv, char **envp) {
|
||||||
using namespace hex;
|
Window::initNative();
|
||||||
|
|
||||||
hex::crash::setupCrashHandlers();
|
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
|
// Check if ImHex is installed in portable mode
|
||||||
{
|
{
|
||||||
@ -30,14 +51,14 @@ int main(int argc, char **argv, char **envp) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool shouldRestart = false;
|
bool shouldRestart = false;
|
||||||
|
// Register an event to handle restarting of ImHex
|
||||||
|
EventManager::subscribe<RequestRestartImHex>([&]{ shouldRestart = true; });
|
||||||
|
|
||||||
do {
|
do {
|
||||||
// Register an event to handle restarting of ImHex
|
|
||||||
EventManager::subscribe<RequestRestartImHex>([&]{ shouldRestart = true; });
|
|
||||||
shouldRestart = false;
|
shouldRestart = false;
|
||||||
|
|
||||||
// Initialization
|
// Initialization
|
||||||
{
|
{
|
||||||
Window::initNative();
|
|
||||||
|
|
||||||
log::info("Welcome to ImHex {}!", ImHexApi::System::getImHexVersion());
|
log::info("Welcome to ImHex {}!", ImHexApi::System::getImHexVersion());
|
||||||
log::info("Compiled using commit {}@{}", ImHexApi::System::getCommitBranch(), ImHexApi::System::getCommitHash());
|
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
|
// Main window
|
||||||
{
|
{
|
||||||
Window 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
|
// Open file that has been requested to be opened through other, OS-specific means
|
||||||
if (auto path = hex::getInitialFilePath(); path.has_value()) {
|
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 "window.hpp"
|
||||||
|
|
||||||
|
#include "messaging.hpp"
|
||||||
|
|
||||||
#include <hex/api/content_registry.hpp>
|
#include <hex/api/content_registry.hpp>
|
||||||
|
|
||||||
#if defined(OS_WINDOWS)
|
#if defined(OS_WINDOWS)
|
||||||
@ -53,12 +55,28 @@ namespace hex {
|
|||||||
auto message = reinterpret_cast<COPYDATASTRUCT *>(lParam);
|
auto message = reinterpret_cast<COPYDATASTRUCT *>(lParam);
|
||||||
if (message == nullptr) break;
|
if (message == nullptr) break;
|
||||||
|
|
||||||
auto data = reinterpret_cast<const char8_t *>(message->lpData);
|
ssize_t nullIndex = -1;
|
||||||
if (data == nullptr) break;
|
|
||||||
|
|
||||||
std::fs::path path = data;
|
char* messageData = reinterpret_cast<char*>(message->lpData);
|
||||||
log::info("Opening file in existing instance: {}", wolv::util::toUTF8String(path));
|
size_t messageSize = message->cbData;
|
||||||
EventManager::post<RequestOpenFile>(path);
|
|
||||||
|
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;
|
break;
|
||||||
}
|
}
|
||||||
case WM_SETTINGCHANGE: {
|
case WM_SETTINGCHANGE: {
|
||||||
@ -214,6 +232,24 @@ namespace hex {
|
|||||||
|
|
||||||
|
|
||||||
void Window::initNative() {
|
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);
|
ImHexApi::System::impl::setBorderlessWindowMode(true);
|
||||||
|
|
||||||
// Add plugin library folders to dll search path
|
// Add plugin library folders to dll search path
|
||||||
@ -226,83 +262,6 @@ namespace hex {
|
|||||||
// We redirect stderr to NUL to prevent this
|
// We redirect stderr to NUL to prevent this
|
||||||
freopen("NUL:", "w", stderr);
|
freopen("NUL:", "w", stderr);
|
||||||
setvbuf(stderr, nullptr, _IONBF, 0);
|
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() {
|
void Window::setupNativeWindow() {
|
||||||
|
21
plugins/builtin/romfs/logo.ans
Normal file
21
plugins/builtin/romfs/logo.ans
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
[1mImHex Hex Editor[0m
|
||||||
|
[3mA Hex Editor for Reverse Engineers, Programmers and people
|
||||||
|
who value their retinas when working at 3 AM.[0m
|
||||||
|
[38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m
|
||||||
|
[38;5;m [38;5;m [38;5;m [38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;062m([38;5;061m/[38;5;m [38;5;m [38;5;m [38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;062m([38;5;061m/[38;5;m [38;5;m [38;5;m
|
||||||
|
[38;5;m [38;5;m [38;5;m [38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;062m([38;5;061m/[38;5;m [38;5;m [38;5;m [38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;068m([38;5;062m([38;5;061m/[38;5;m [38;5;m [38;5;m
|
||||||
|
[38;5;m [38;5;m [38;5;m [38;5;060m*[38;5;024m,[38;5;024m,[38;5;024m,[38;5;023m,[38;5;237m,[38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;024m,[38;5;024m,[38;5;024m,[38;5;024m,[38;5;024m,[38;5;023m,[38;5;024m*[38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;024m,[38;5;024m,[38;5;024m,[38;5;024m,[38;5;023m,[38;5;024m*[38;5;m [38;5;m [38;5;m [0m[1mVersion: [0m{0}[0m
|
||||||
|
[38;5;m [38;5;m [38;5;m [38;5;068m([38;5;062m([38;5;062m([38;5;062m([38;5;062m/[38;5;061m/[38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;068m([38;5;062m([38;5;062m([38;5;062m([38;5;062m([38;5;062m/[38;5;061m/[38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;068m([38;5;062m([38;5;062m([38;5;068m([38;5;062m([38;5;061m/[38;5;m [38;5;m [38;5;m [0m[1mCommit: [0m{1}@{2}[0m
|
||||||
|
[38;5;m [38;5;m [38;5;m [38;5;062m([38;5;062m/[38;5;062m/[38;5;062m/[38;5;062m/[38;5;061m/[38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;062m([38;5;062m/[38;5;062m/[38;5;062m/[38;5;062m/[38;5;062m/[38;5;061m/[38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;062m([38;5;062m([38;5;062m([38;5;062m([38;5;062m/[38;5;061m/[38;5;m [38;5;m [38;5;m [0m[1mBuild time: [0m{3}, {4}[0m
|
||||||
|
[38;5;m [38;5;m [38;5;m [38;5;062m/[38;5;061m/[38;5;061m/[38;5;061m/[38;5;061m/[38;5;061m*[38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;062m/[38;5;062m/[38;5;062m/[38;5;062m/[38;5;062m/[38;5;061m/[38;5;061m*[38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;062m/[38;5;062m/[38;5;062m/[38;5;062m/[38;5;061m/[38;5;061m/[38;5;m [38;5;m [38;5;m [0m[1mType: [0m{5}[0m
|
||||||
|
[38;5;m [38;5;m [38;5;m [38;5;061m/[38;5;061m/[38;5;061m/[38;5;061m/[38;5;061m/[38;5;025m*[38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;061m/[38;5;061m/[38;5;061m/[38;5;061m/[38;5;061m/[38;5;061m/[38;5;025m*[38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;061m/[38;5;061m/[38;5;061m/[38;5;061m/[38;5;061m/[38;5;061m*[38;5;m [38;5;m [38;5;m
|
||||||
|
[38;5;m [38;5;m [38;5;m [38;5;061m/[38;5;061m*[38;5;025m*[38;5;025m*[38;5;025m*[38;5;024m*[38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;061m/[38;5;061m/[38;5;025m/[38;5;025m/[38;5;061m/[38;5;025m*[38;5;024m*[38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;061m/[38;5;061m/[38;5;061m/[38;5;061m/[38;5;061m/[38;5;025m*[38;5;m [38;5;m [38;5;m
|
||||||
|
[38;5;m [38;5;m [38;5;m [38;5;061m*[38;5;025m*[38;5;025m*[38;5;025m*[38;5;024m*[38;5;024m*[38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;061m*[38;5;025m*[38;5;025m*[38;5;025m*[38;5;025m*[38;5;025m*[38;5;024m*[38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;061m/[38;5;025m*[38;5;025m*[38;5;025m*[38;5;025m*[38;5;024m*[38;5;m [38;5;m [38;5;m
|
||||||
|
[38;5;m [38;5;m [38;5;m [38;5;060m*[38;5;024m*[38;5;024m*[38;5;024m*[38;5;024m*[38;5;024m,[38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;025m*[38;5;024m*[38;5;024m*[38;5;024m*[38;5;024m*[38;5;024m*[38;5;024m,[38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;061m*[38;5;025m*[38;5;025m*[38;5;025m*[38;5;024m*[38;5;024m*[38;5;m [38;5;m [38;5;m
|
||||||
|
[38;5;m [38;5;m [38;5;m [38;5;024m*[38;5;024m*[38;5;024m,[38;5;024m,[38;5;024m,[38;5;024m,[38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;024m*[38;5;024m*[38;5;024m*[38;5;024m*[38;5;024m*[38;5;024m,[38;5;024m,[38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;024m*[38;5;024m*[38;5;024m*[38;5;024m*[38;5;024m*[38;5;024m,[38;5;m [38;5;m [38;5;m
|
||||||
|
[38;5;m [38;5;m [38;5;m [38;5;024m*[38;5;024m,[38;5;024m,[38;5;024m,[38;5;024m,[38;5;023m,[38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;024m*[38;5;024m,[38;5;024m,[38;5;024m,[38;5;024m,[38;5;024m,[38;5;023m,[38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;024m*[38;5;024m*[38;5;024m*[38;5;024m*[38;5;024m,[38;5;024m,[38;5;m [38;5;m [38;5;m
|
||||||
|
[38;5;m [38;5;m [38;5;m [38;5;024m,[38;5;023m,[38;5;004m,[38;5;004m,[38;5;017m,[38;5;017m,[38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;024m,[38;5;024m,[38;5;024m,[38;5;024m,[38;5;024m,[38;5;023m,[38;5;017m,[38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;024m*[38;5;024m,[38;5;024m,[38;5;024m,[38;5;024m,[38;5;023m,[38;5;m [38;5;m [38;5;m
|
||||||
|
[38;5;m [38;5;m [38;5;m [38;5;061m/[38;5;061m/[38;5;061m/[38;5;061m/[38;5;061m/[38;5;061m/[38;5;061m/[38;5;061m/[38;5;061m/[38;5;061m/[38;5;061m/[38;5;061m/[38;5;061m/[38;5;061m/[38;5;061m/[38;5;061m/[38;5;061m/[38;5;061m/[38;5;061m/[38;5;061m/[38;5;060m*[38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;024m,[38;5;024m,[38;5;024m,[38;5;024m,[38;5;024m,[38;5;024m,[38;5;017m,[38;5;m [38;5;m [38;5;m
|
||||||
|
[38;5;m [38;5;m [38;5;m [38;5;061m*[38;5;024m*[38;5;024m*[38;5;024m*[38;5;024m*[38;5;024m*[38;5;024m*[38;5;024m*[38;5;024m*[38;5;024m*[38;5;024m*[38;5;024m*[38;5;024m*[38;5;024m*[38;5;024m*[38;5;024m*[38;5;024m*[38;5;024m*[38;5;024m*[38;5;024m*[38;5;024m*[38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;237m,[38;5;017m.[38;5;017m.[38;5;017m.[38;5;017m.[38;5;017m.[38;5;m [38;5;m [38;5;m [38;5;m
|
||||||
|
[38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m [38;5;m
|
||||||
|
[0m
|
@ -1,11 +1,15 @@
|
|||||||
#include <hex/plugin.hpp>
|
#include <hex/plugin.hpp>
|
||||||
|
#include <hex/subcommands/subcommands.hpp>
|
||||||
|
|
||||||
|
#include <hex/api/event.hpp>
|
||||||
#include <hex/api/content_registry.hpp>
|
#include <hex/api/content_registry.hpp>
|
||||||
#include <hex/helpers/logger.hpp>
|
#include <hex/helpers/logger.hpp>
|
||||||
|
|
||||||
#include <romfs/romfs.hpp>
|
#include <romfs/romfs.hpp>
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
using namespace hex;
|
||||||
|
|
||||||
namespace hex::plugin::builtin {
|
namespace hex::plugin::builtin {
|
||||||
|
|
||||||
void registerEventHandlers();
|
void registerEventHandlers();
|
||||||
@ -41,8 +45,69 @@ namespace hex::plugin::builtin {
|
|||||||
|
|
||||||
void handleBorderlessWindowMode();
|
void handleBorderlessWindowMode();
|
||||||
|
|
||||||
|
void handleSubCommands();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IMHEX_PLUGIN_SUBCOMMANDS() {
|
||||||
|
{ "version", "Print ImHex version and exit", [](const std::vector<std::string>&) {
|
||||||
|
hex::print(romfs::get("logo.ans").string(),
|
||||||
|
ImHexApi::System::getImHexVersion(),
|
||||||
|
ImHexApi::System::getCommitBranch(), ImHexApi::System::getCommitHash(),
|
||||||
|
__DATE__, __TIME__,
|
||||||
|
ImHexApi::System::isPortableVersion() ? "Portable" : "Installed");
|
||||||
|
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}},
|
||||||
|
{ "help", "Print help about this command and exit", [](const std::vector<std::string>&) {
|
||||||
|
hex::print(
|
||||||
|
"ImHex - A Hex Editor for Reverse Engineers, Programmers and people who value their retinas when working at 3 AM.\n"
|
||||||
|
"\n"
|
||||||
|
"usage: imhex [subcommand] [options]\n"
|
||||||
|
"Available subcommands:\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
size_t longestCommand = 0;
|
||||||
|
for (const auto &plugin : PluginManager::getPlugins()) {
|
||||||
|
for (const auto &subCommand : plugin.getSubCommands()) {
|
||||||
|
longestCommand = std::max(longestCommand, subCommand.commandKey.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &plugin : PluginManager::getPlugins()) {
|
||||||
|
for (const auto &subCommand : plugin.getSubCommands()) {
|
||||||
|
hex::print(" --{}{: <{}} {}\n", subCommand.commandKey, "", longestCommand - subCommand.commandKey.size(), subCommand.commandDesc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}},
|
||||||
|
{ "open", "Open files passed as argument. This is the default subcommand is none is entered",
|
||||||
|
[](const std::vector<std::string> &args) {
|
||||||
|
|
||||||
|
hex::subcommands::registerSubCommand("open", [](const std::vector<std::string> &args){
|
||||||
|
for (auto &arg : args) {
|
||||||
|
EventManager::post<RequestOpenFile>(arg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
std::vector<std::string> fullPaths;
|
||||||
|
bool doubleDashFound = false;
|
||||||
|
for (auto &arg : args) {
|
||||||
|
|
||||||
|
// Skip the first argument named `--`
|
||||||
|
if (arg == "--" && !doubleDashFound) {
|
||||||
|
doubleDashFound = true;
|
||||||
|
} else {
|
||||||
|
auto path = std::filesystem::weakly_canonical(arg);
|
||||||
|
fullPaths.push_back(wolv::util::toUTF8String(path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hex::subcommands::forwardSubCommand("open", fullPaths);
|
||||||
|
}}
|
||||||
|
};
|
||||||
|
|
||||||
IMHEX_PLUGIN_SETUP("Built-in", "WerWolv", "Default ImHex functionality") {
|
IMHEX_PLUGIN_SETUP("Built-in", "WerWolv", "Default ImHex functionality") {
|
||||||
|
|
||||||
using namespace hex::plugin::builtin;
|
using namespace hex::plugin::builtin;
|
||||||
|
Loading…
Reference in New Issue
Block a user