feat: add tui (#758)

This commit also contains an entire redesign of the GUI interface, which now contains a sidebar for all panels. Hopefully checkra1n approves :)
This commit is contained in:
C authored and GitHub committed 2026-07-06 04:05:57 -07:00
1 parent 89b6147e26
commit 18023c4037
42 files changed
+2895 -487

No files matched your search

+134
View File
@@ -0,0 +1,134 @@
#ifdef WITH_TUI
#include "TuiExploitPanel.hpp"
#include <algorithm>
#include <chrono>
#include <memory>
#include <string>
#include <thread>
#include <ncurses.h>
#include "Tui.hpp"
#include "TuiText.hpp"
#include "../m8/run.h"
static const char *buttons[] = { "Back" };
static constexpr int kExploitContentX = 2;
static constexpr int kExploitContentWidth = 60;
TuiExploitPanel::TuiExploitPanel(TuiFrame* frame)
: TuiPanel(frame) {}
const char** TuiExploitPanel::get_buttons() const {
return buttons;
}
void TuiExploitPanel::draw(int sy, int sx, int selected) {
(void)selected;
const int content_x = sx + kExploitContentX;
tui_text::draw_wrapped_text(
sy + 2,
content_x,
kExploitContentWidth,
"Installing jailbreak, this will take a moment. If the device asks for a passcode please enter it. Do not disconnect the device until finished.",
3
);
std::string status_text;
int progress = 0;
{
std::lock_guard<std::mutex> lock(m_status_mutex);
status_text = m_status_text;
progress = m_progress;
}
mvprintw(sy + 8, content_x, "%.*s", kExploitContentWidth, status_text.empty() ? stage_to_string(STAGE_RESET) : status_text.c_str());
const int bar_width = 50;
const int filled = (std::clamp(progress, 0, 100) * bar_width) / 100;
std::string bar(static_cast<size_t>(bar_width), '-');
for (int i = 0; i < filled; ++i) {
bar[static_cast<size_t>(i)] = '#';
}
mvprintw(sy + 10, sx + 2, "[%s] %3d%%", bar.c_str(), std::clamp(progress, 0, 100));
}
void TuiExploitPanel::handle_enter(int selected, int sy, int sx) {
if (selected == 0) {
GetFrame()->ShowMain(1);
}
}
void TuiExploitPanel::handle_device_update(const DeviceState& state) {
(void)state;
}
void TuiExploitPanel::on_show() {
exploit();
}
bool TuiExploitPanel::is_button_enabled(int btn_idx) const {
(void)btn_idx;
return !m_is_exploiting;
}
void TuiExploitPanel::set_status(const std::string& status, int progress) {
std::lock_guard<std::mutex> lock(m_status_mutex);
m_status_text = status;
m_progress = std::clamp(progress, 0, 100);
}
void TuiExploitPanel::exploit() {
if (m_is_exploiting) {
return;
}
m_is_exploiting = true;
set_status(stage_to_string(STAGE_RESET), 0);
std::thread([this]() {
const DeviceState deviceState = GetFrame()->GetDeviceState();
if (!deviceState.connected || deviceState.mode != DeviceMode::DFU) {
set_status("Device is not in DFU mode.", 0);
m_is_exploiting = false;
return;
}
auto sharedState = std::make_shared<shared_t>();
atomic_init(&sharedState->stage, STAGE_RESET);
atomic_init(&sharedState->stop, false);
atomic_init(&sharedState->result, 0);
auto isFinished = std::make_shared<std::atomic<bool>>(false);
std::thread worker([sharedState, isFinished]() {
::exploit(sharedState.get());
isFinished->store(true);
});
for (;;) {
int stage = atomic_load(&sharedState->stage);
int progress = stage_to_progress(stage);
bool finished = isFinished->load();
set_status(stage_to_string(stage), progress);
if (stage == STAGE_DONE || finished) {
atomic_store(&sharedState->stop, true);
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
if (worker.joinable()) {
worker.join();
}
m_is_exploiting = false;
}).detach();
}
#endif // WITH_TUI