C++ Setup
A walkthrough of main.cpp and how the C++ backend is structured.
main.cpp is the entry point for the C++ backend and is where everything gets connected together. It runs through a fixed startup sequence — attach to the target process, register configs, start IPC, launch the UI, initialize the overlay — and then sits in a loop keeping everything alive until the target process exits.
Startup Sequence
1. Attaching to the Target Process
process::g_process.attach("mspaint.exe", "Untitled - Paint");The first thing that happens is attaching to the target process. The first argument is the process name and the second is the window title. If the process isn't found or access is denied, a ProcessException is thrown and the program exits early.
To target a different process, just change these two values:
process::g_process.attach("TargetProcess.exe", "Target Window Title");2. Setting the Config Directory
config::g_config.set_directory("nopjo", "configs");This sets where config files are saved on disk. The full path ends up being:
%LOCALAPPDATA%\nopjo\configs\Change "nopjo" to your own project name so config files don't conflict with other projects using the same template.
3. Registering Configs
config::g_config.register_config(
"visuals",
visuals::g_config,
nullptr,
[](const nlohmann::json &data) { Logger::info("visuals config set: {}", data.dump()); });Each config is registered with a name, a reference to the config struct, an optional load callback, and an optional set callback. The name is what the UI uses to get and set the config via IPC. The set callback fires whenever the UI pushes a new value.
The overlay config is registered without callbacks since it doesn't need them:
config::g_config.register_config("overlay", overlay::config::g_config);4. Starting IPC
ipc::ServerConfig config{.host = "127.0.0.1", .port = 14242};
ipc::g_server = std::make_unique<ipc::Server>(config);
ipc::register_default_handlers();
ipc::g_server->start();This starts the TCP server that the Tauri UI connects to. register_default_handlers wires up all the built-in handlers for get_config, set_config, config_file_list, etc. The server must be started after handlers are registered.
5. Launching the UI
#ifdef UI_RESOURCE_EMBEDDED
const auto pid = resource_manager.extract_and_launch(101, "RCDATA", ..., "ui.exe");
ipc::g_tauri_pid = *pid;
#endifWhen built with the x64-release-with-ui or x64-debug-with-ui CMake preset, the Tauri executable is embedded as a resource inside the C++ binary. At runtime it gets extracted to the temp directory and launched automatically. The PID is stored so the overlay can manage z-ordering relative to the UI window.
When built without embedding (the default x64-release preset), this block is skipped entirely and you launch the UI manually with bun run tauri dev.
6. Configuring and Starting the Overlay
overlay::config::g_config.menu_keybind = VK_INSERT;
overlay::config::g_config.hide_when_unfocused = true;
overlay::g_overlay = overlay::Overlay::create();
overlay::g_overlay->start();The overlay config is set before the overlay starts. menu_keybind controls which key toggles the menu, and hide_when_unfocused controls whether the overlay hides when the target window loses focus. After that the overlay is created and started on its own thread.
7. The Main Loop
while (overlay::g_overlay->is_running())
{
if (!process::g_process.is_alive())
{
Logger::info("Process '{}' is no longer running, shutting down", ...);
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
overlay::g_overlay->stop();
ipc::g_server->stop();The main thread sits in a loop checking whether the target process is still alive every second. When it exits, the loop breaks and everything shuts down cleanly — the overlay thread is joined and the IPC server is stopped.