mirror of
https://github.com/palera1n/palera1n.git
synced 2026-10-21 09:47:01 +02:00
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:
42 files changed
+2895
-487
No files matched your search
@@ -23,20 +23,22 @@ payloads:
|
||||
name=$$(basename "$$file"); \
|
||||
name=$${name%.*}; \
|
||||
echo " XXD $$file"; \
|
||||
echo "#pragma once" > "src/gen/payloads/$$name.h"; \
|
||||
xxd -i "$$file" \
|
||||
| sed 's/unsigned char/static const unsigned char/g' \
|
||||
| sed 's/unsigned int/static const size_t/g' \
|
||||
> "src/gen/payloads/$$name.h"; \
|
||||
>> "src/gen/payloads/$$name.h"; \
|
||||
done
|
||||
|
||||
@for file in images/*; do \
|
||||
name=$$(basename "$$file"); \
|
||||
name=$${name%.*}; \
|
||||
echo " XXD $$file"; \
|
||||
echo "#pragma once" > "src/gen/images/$$name.h"; \
|
||||
xxd -i "$$file" \
|
||||
| sed 's/unsigned char/static const unsigned char/g' \
|
||||
| sed 's/unsigned int/static const size_t/g' \
|
||||
> "src/gen/images/$$name.h"; \
|
||||
>> "src/gen/images/$$name.h"; \
|
||||
done
|
||||
|
||||
xxd -i -n "DFUHelperDeviceInfo" resources/DFUHelperDeviceInfo.json > src/gen/DFUHelperDeviceInfo.h
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 9.1 KiB |
@@ -82,17 +82,16 @@
|
||||
"image_name":"ipodtouch",
|
||||
"image_width":116,
|
||||
"image_height":240,
|
||||
"image_offset_x":100,
|
||||
"buttons":{
|
||||
"top":{
|
||||
"name":"- Top button",
|
||||
"x":192,
|
||||
"y":-4
|
||||
"x":237,
|
||||
"y":0
|
||||
},
|
||||
"vol_down":{
|
||||
"name":"Volume down -",
|
||||
"x":8,
|
||||
"y":62
|
||||
"x":46,
|
||||
"y":56
|
||||
}
|
||||
},
|
||||
"sequence":"ipodtouch7"
|
||||
@@ -101,17 +100,16 @@
|
||||
"image_name":"ipodtouch",
|
||||
"image_width":116,
|
||||
"image_height":240,
|
||||
"image_offset_x":100,
|
||||
"buttons":{
|
||||
"top":{
|
||||
"name":"- Top button",
|
||||
"x":196,
|
||||
"y":-4
|
||||
"x":237,
|
||||
"y":0
|
||||
},
|
||||
"home":{
|
||||
"name":"Home button -",
|
||||
"x":50,
|
||||
"y":210
|
||||
"x":89,
|
||||
"y":190
|
||||
}
|
||||
},
|
||||
"sequence":"iphone-old"
|
||||
@@ -120,17 +118,16 @@
|
||||
"image_name":"iphonese",
|
||||
"image_width":119,
|
||||
"image_height":240,
|
||||
"image_offset_x":100,
|
||||
"buttons":{
|
||||
"top":{
|
||||
"name":"- Top button",
|
||||
"x":200,
|
||||
"y":-4
|
||||
"x":237,
|
||||
"y":0
|
||||
},
|
||||
"home":{
|
||||
"name":"Home button -",
|
||||
"x":56,
|
||||
"y":210
|
||||
"x":90,
|
||||
"y":195
|
||||
}
|
||||
},
|
||||
"sequence":"iphone-old"
|
||||
@@ -139,17 +136,16 @@
|
||||
"image_name":"iphone6s",
|
||||
"image_width":123,
|
||||
"image_height":240,
|
||||
"image_offset_x":100,
|
||||
"buttons":{
|
||||
"side":{
|
||||
"name":"- Side button",
|
||||
"x":224,
|
||||
"y":58
|
||||
"x":244,
|
||||
"y":54
|
||||
},
|
||||
"home":{
|
||||
"name":"Home button -",
|
||||
"x":60,
|
||||
"y":210
|
||||
"x":90,
|
||||
"y":195
|
||||
}
|
||||
},
|
||||
"sequence":"iphone6"
|
||||
@@ -158,17 +154,16 @@
|
||||
"image_name":"iphone7",
|
||||
"image_width":123,
|
||||
"image_height":240,
|
||||
"image_offset_x":100,
|
||||
"buttons":{
|
||||
"side":{
|
||||
"name":"- Side button",
|
||||
"x":224,
|
||||
"y":58
|
||||
"x":244,
|
||||
"y":54
|
||||
},
|
||||
"vol_down":{
|
||||
"name":"Volume down -",
|
||||
"x":14,
|
||||
"y":78
|
||||
"x":44,
|
||||
"y":72
|
||||
}
|
||||
},
|
||||
"sequence":"iphone7"
|
||||
@@ -177,17 +172,16 @@
|
||||
"image_name":"iphone8",
|
||||
"image_width":120,
|
||||
"image_height":240,
|
||||
"image_offset_x":100,
|
||||
"buttons":{
|
||||
"side":{
|
||||
"name":"- Side button",
|
||||
"x":220,
|
||||
"y":58
|
||||
"x":248,
|
||||
"y":54
|
||||
},
|
||||
"vol_down":{
|
||||
"name":"Volume down -",
|
||||
"x":10,
|
||||
"y":78
|
||||
"x":44,
|
||||
"y":72
|
||||
}
|
||||
},
|
||||
"sequence":"iphone8"
|
||||
@@ -196,17 +190,16 @@
|
||||
"image_name":"iphonex",
|
||||
"image_width":121,
|
||||
"image_height":240,
|
||||
"image_offset_x":100,
|
||||
"buttons":{
|
||||
"side":{
|
||||
"name":"- Side button",
|
||||
"x":220,
|
||||
"y":68
|
||||
"x":246,
|
||||
"y":62
|
||||
},
|
||||
"vol_down":{
|
||||
"name":"Volume down -",
|
||||
"x":10,
|
||||
"y":75
|
||||
"x":44,
|
||||
"y":70
|
||||
}
|
||||
},
|
||||
"sequence":"iphone8"
|
||||
@@ -217,14 +210,14 @@
|
||||
"image_height":240,
|
||||
"buttons":{
|
||||
"top":{
|
||||
"name":"Top button",
|
||||
"x":149,
|
||||
"y":5
|
||||
"name":"- Top button",
|
||||
"x":266,
|
||||
"y":0
|
||||
},
|
||||
"home":{
|
||||
"name":"Home button",
|
||||
"x":89,
|
||||
"y":225
|
||||
"name":"Home button -",
|
||||
"x":94,
|
||||
"y":200
|
||||
}
|
||||
},
|
||||
"sequence":"iphone-old"
|
||||
@@ -235,14 +228,14 @@
|
||||
"image_height":240,
|
||||
"buttons":{
|
||||
"top":{
|
||||
"name":"Top button",
|
||||
"x":140,
|
||||
"y":4
|
||||
"name":"- Top button",
|
||||
"x":260,
|
||||
"y":0
|
||||
},
|
||||
"home":{
|
||||
"name":"Home button",
|
||||
"x":86,
|
||||
"y":224
|
||||
"name":"Home button -",
|
||||
"x":92,
|
||||
"y":200
|
||||
}
|
||||
},
|
||||
"sequence":"iphone-old"
|
||||
@@ -251,17 +244,16 @@
|
||||
"image_name":"siriremote",
|
||||
"image_width":79,
|
||||
"image_height":240,
|
||||
"image_offset_x":120,
|
||||
"buttons":{
|
||||
"menu":{
|
||||
"name":"Menu -",
|
||||
"x":84,
|
||||
"y":84
|
||||
"x":110,
|
||||
"y":74
|
||||
},
|
||||
"playpause":{
|
||||
"name":"Play/Pause -",
|
||||
"x":52,
|
||||
"y":146
|
||||
"x":80,
|
||||
"y":130
|
||||
}
|
||||
},
|
||||
"sequence":"appletv4"
|
||||
@@ -270,11 +262,10 @@
|
||||
"image_name":"atv_std_brd",
|
||||
"image_width":300,
|
||||
"image_height":240,
|
||||
"image_offset_x":0,
|
||||
"buttons":{
|
||||
"dfu":{
|
||||
"name":"-- DFU button",
|
||||
"x":120,
|
||||
"x":170,
|
||||
"y":48
|
||||
}
|
||||
},
|
||||
@@ -295,7 +286,7 @@
|
||||
"top",
|
||||
"vol_down"
|
||||
],
|
||||
"description":"Press and hold the Top and Volume down buttons together",
|
||||
"description":"HOLD Top and Volume down",
|
||||
"duration":4,
|
||||
"action":"reboot"
|
||||
},
|
||||
@@ -303,7 +294,7 @@
|
||||
"buttons":[
|
||||
"vol_down"
|
||||
],
|
||||
"description":"Release the Top button BUT KEEP HOLDING the Volume down button",
|
||||
"description":"Release Top and KEEP holding Volume down",
|
||||
"duration":10
|
||||
}
|
||||
],
|
||||
@@ -320,7 +311,7 @@
|
||||
"top",
|
||||
"home"
|
||||
],
|
||||
"description":"Press and hold the Top\n and Home buttons\n together",
|
||||
"description":"HOLD Top and Home button",
|
||||
"duration":4,
|
||||
"action":"reboot"
|
||||
},
|
||||
@@ -328,7 +319,7 @@
|
||||
"buttons":[
|
||||
"home"
|
||||
],
|
||||
"description":"Release the Top button\n BUT KEEP HOLDING the\n Home button",
|
||||
"description":"Release Top and KEEP holding Home button",
|
||||
"duration":10
|
||||
}
|
||||
],
|
||||
@@ -345,7 +336,7 @@
|
||||
"side",
|
||||
"home"
|
||||
],
|
||||
"description":"Press and hold the Side\n and Home buttons\n together",
|
||||
"description":"HOLD Side and Home button",
|
||||
"duration":4,
|
||||
"action":"reboot"
|
||||
},
|
||||
@@ -353,7 +344,7 @@
|
||||
"buttons":[
|
||||
"home"
|
||||
],
|
||||
"description":"Release the Side button\n BUT KEEP HOLDING the\n Home button",
|
||||
"description":"Release Side and KEEP holding Home button",
|
||||
"duration":10
|
||||
}
|
||||
],
|
||||
@@ -370,7 +361,7 @@
|
||||
"side",
|
||||
"vol_down"
|
||||
],
|
||||
"description":"Press and hold the Side\n and Volume down\n buttons together",
|
||||
"description":"HOLD Side and Volume down",
|
||||
"duration":4,
|
||||
"action":"reboot"
|
||||
},
|
||||
@@ -378,7 +369,7 @@
|
||||
"buttons":[
|
||||
"vol_down"
|
||||
],
|
||||
"description":"Release the Side button\n BUT KEEP HOLDING the\n Volume down button",
|
||||
"description":"Release Side and KEEP holding Volume down",
|
||||
"duration":10
|
||||
}
|
||||
],
|
||||
@@ -395,7 +386,7 @@
|
||||
"side",
|
||||
"vol_down"
|
||||
],
|
||||
"description":"Press and hold the Side\n and Volume down\n buttons together",
|
||||
"description":"HOLD Side and Volume down",
|
||||
"duration":4,
|
||||
"action":"reboot"
|
||||
},
|
||||
@@ -403,7 +394,7 @@
|
||||
"buttons":[
|
||||
"vol_down"
|
||||
],
|
||||
"description":"Release the Side button\n BUT KEEP HOLDING the\n Volume down button",
|
||||
"description":"Release Side and KEEP holding Volume down",
|
||||
"duration":10
|
||||
}
|
||||
],
|
||||
@@ -420,7 +411,7 @@
|
||||
"menu",
|
||||
"playpause"
|
||||
],
|
||||
"description":"Press and hold Menu\n and Play/Pause together\n ",
|
||||
"description":"Press and hold Menu and Play/Pause together",
|
||||
"duration":7
|
||||
},
|
||||
{
|
||||
@@ -443,7 +434,7 @@
|
||||
"buttons":[
|
||||
"dfu"
|
||||
],
|
||||
"description":"Press and hold the DFU\n button",
|
||||
"description":"Press and hold the DFU button",
|
||||
"duration":10,
|
||||
"action":"reboot"
|
||||
}
|
||||
|
||||
+322
-143
@@ -1,183 +1,362 @@
|
||||
#ifdef WITH_GUI
|
||||
#if defined(WITH_GUI) || defined(WITH_TUI)
|
||||
|
||||
#include "event.hpp"
|
||||
|
||||
#include "wx/AppFrame.hpp"
|
||||
#include "state.hpp"
|
||||
#include "sequence.hpp"
|
||||
#include "utils.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <libimobiledevice/libimobiledevice.h>
|
||||
#include <libimobiledevice/lockdown.h>
|
||||
#include <libirecovery.h>
|
||||
|
||||
static inline DeviceState make_fresh_state()
|
||||
{
|
||||
return DeviceState{};
|
||||
namespace {
|
||||
|
||||
struct ManagedDevice {
|
||||
DeviceState state;
|
||||
bool normalPresent = false;
|
||||
bool recoveryPresent = false;
|
||||
};
|
||||
|
||||
std::mutex g_mutex;
|
||||
std::vector<DeviceStateCallback> g_callbacks;
|
||||
std::unordered_map<uint64_t, ManagedDevice> g_devices; // keyed by ECID
|
||||
std::unordered_map<std::string, uint64_t> g_udidToEcid;
|
||||
uint64_t g_activeEcid = 0;
|
||||
std::once_flag g_startOnce;
|
||||
irecv_device_event_context_t g_recoveryCtx = nullptr;
|
||||
|
||||
void dispatch_callbacks(const std::vector<DeviceStateCallback>& callbacks, const DeviceState& state) {
|
||||
for (const auto& cb : callbacks) {
|
||||
if (cb)
|
||||
cb(state);
|
||||
}
|
||||
}
|
||||
|
||||
void normal_device_event_cb(
|
||||
const idevice_event_t* event,
|
||||
void* user_data)
|
||||
{
|
||||
#ifdef WITH_GUI
|
||||
auto* frame = static_cast<MainFrame*>(user_data);
|
||||
if (!frame || !event)
|
||||
return;
|
||||
#endif
|
||||
DeviceState build_public_state_locked() {
|
||||
DeviceState out;
|
||||
out.connectedDeviceCount = static_cast<uint32_t>(g_devices.size());
|
||||
out.multipleDevices = out.connectedDeviceCount > 1;
|
||||
|
||||
DeviceState state = make_fresh_state();
|
||||
|
||||
if (event->event == IDEVICE_DEVICE_REMOVE)
|
||||
{
|
||||
#ifdef WITH_GUI
|
||||
send_device_state(frame, state);
|
||||
#endif
|
||||
return;
|
||||
if (g_devices.size() != 1) {
|
||||
g_activeEcid = 0;
|
||||
return out;
|
||||
}
|
||||
|
||||
if (event->event != IDEVICE_DEVICE_ADD)
|
||||
return;
|
||||
auto it = g_devices.begin();
|
||||
if (g_activeEcid != 0) {
|
||||
auto activeIt = g_devices.find(g_activeEcid);
|
||||
if (activeIt != g_devices.end())
|
||||
it = activeIt;
|
||||
}
|
||||
|
||||
g_activeEcid = it->first;
|
||||
out = it->second.state;
|
||||
out.connected = true;
|
||||
out.connectedDeviceCount = 1;
|
||||
out.multipleDevices = false;
|
||||
return out;
|
||||
}
|
||||
|
||||
void publish_state() {
|
||||
std::vector<DeviceStateCallback> callbacks;
|
||||
DeviceState state;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_mutex);
|
||||
callbacks = g_callbacks;
|
||||
state = build_public_state_locked();
|
||||
}
|
||||
dispatch_callbacks(callbacks, state);
|
||||
}
|
||||
|
||||
bool lockdown_get_string(lockdownd_client_t client, const char* key, std::string& out) {
|
||||
plist_t value = nullptr;
|
||||
if (lockdownd_get_value(client, nullptr, key, &value) != LOCKDOWN_E_SUCCESS || !value)
|
||||
return false;
|
||||
|
||||
char* raw = nullptr;
|
||||
plist_get_string_val(value, &raw);
|
||||
if (raw) {
|
||||
out = raw;
|
||||
free(raw);
|
||||
}
|
||||
|
||||
plist_free(value);
|
||||
return !out.empty();
|
||||
}
|
||||
|
||||
bool lockdown_get_uint(lockdownd_client_t client, const char* key, uint64_t& out) {
|
||||
plist_t value = nullptr;
|
||||
if (lockdownd_get_value(client, nullptr, key, &value) != LOCKDOWN_E_SUCCESS || !value)
|
||||
return false;
|
||||
|
||||
plist_get_uint_val(value, &out);
|
||||
plist_free(value);
|
||||
return out != 0;
|
||||
}
|
||||
|
||||
bool build_normal_state(const idevice_event_t* event, DeviceState& out, std::string& connectionType) {
|
||||
idevice_t device = nullptr;
|
||||
if (idevice_new(&device, event->udid) != IDEVICE_E_SUCCESS)
|
||||
return;
|
||||
|
||||
state.connected = true;
|
||||
state.mode = DeviceMode::Normal;
|
||||
state.udid = event->udid ? event->udid : "";
|
||||
if (!event || idevice_new(&device, event->udid) != IDEVICE_E_SUCCESS || !device)
|
||||
return false;
|
||||
|
||||
lockdownd_client_t client = nullptr;
|
||||
|
||||
if (lockdownd_client_new_with_handshake(device, &client, "palera1n") == LOCKDOWN_E_SUCCESS)
|
||||
{
|
||||
plist_t product = nullptr;
|
||||
plist_t version = nullptr;
|
||||
plist_t ecid = nullptr;
|
||||
|
||||
char *p = nullptr, *v = nullptr;
|
||||
uint64_t ecid_val = 0;
|
||||
|
||||
lockdownd_get_value(client, nullptr, "ProductType", &product);
|
||||
lockdownd_get_value(client, nullptr, "ProductVersion", &version);
|
||||
lockdownd_get_value(client, nullptr, "UniqueChipID", &ecid);
|
||||
|
||||
if (product) plist_get_string_val(product, &p);
|
||||
if (version) plist_get_string_val(version, &v);
|
||||
if (ecid) plist_get_uint_val(ecid, &ecid_val);
|
||||
|
||||
state.productType = p ? p : "";
|
||||
state.productVersion = v ? v : "";
|
||||
state.ecid = ecid_val;
|
||||
|
||||
free(p);
|
||||
free(v);
|
||||
|
||||
if (product) plist_free(product);
|
||||
if (version) plist_free(version);
|
||||
if (ecid) plist_free(ecid);
|
||||
|
||||
lockdownd_client_free(client);
|
||||
if (lockdownd_client_new_with_handshake(device, &client, "palera1n") != LOCKDOWN_E_SUCCESS) {
|
||||
idevice_free(device);
|
||||
return false;
|
||||
}
|
||||
|
||||
out.connected = true;
|
||||
out.mode = DeviceMode::Normal;
|
||||
out.udid = event->udid ? event->udid : "";
|
||||
|
||||
lockdown_get_string(client, "ProductType", out.productType);
|
||||
lockdown_get_string(client, "ProductVersion", out.productVersion);
|
||||
lockdown_get_string(client, "ConnectionType", connectionType);
|
||||
lockdown_get_uint(client, "UniqueChipID", out.ecid);
|
||||
|
||||
out.isSupported = SequenceIsSupported(out.productType);
|
||||
|
||||
lockdownd_client_free(client);
|
||||
idevice_free(device);
|
||||
|
||||
state.isSupported = SequenceIsSupported(state.productType);
|
||||
|
||||
#ifdef WITH_GUI
|
||||
send_device_state(frame, state);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void recovery_device_event_cb(
|
||||
const irecv_device_event_t* event,
|
||||
void* user_data)
|
||||
{
|
||||
#ifdef WITH_GUI
|
||||
auto* frame = static_cast<MainFrame*>(user_data);
|
||||
if (!frame || !event)
|
||||
return;
|
||||
#endif
|
||||
bool build_recovery_state(const irecv_device_event_t* event, DeviceState& out) {
|
||||
if (!event || !event->device_info || !event->device_info->ecid)
|
||||
return false;
|
||||
|
||||
DeviceState state = make_fresh_state();
|
||||
|
||||
if (event->type == IRECV_DEVICE_REMOVE)
|
||||
{
|
||||
#ifdef WITH_GUI
|
||||
send_device_state(frame, state);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->type != IRECV_DEVICE_ADD)
|
||||
return;
|
||||
|
||||
state.connected = true;
|
||||
|
||||
if (event->mode == IRECV_K_DFU_MODE)
|
||||
{
|
||||
state.mode = DeviceMode::DFU;
|
||||
#ifdef WITH_GUI
|
||||
send_device_state(frame, state);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(event->mode == IRECV_K_RECOVERY_MODE_1 ||
|
||||
event->mode == IRECV_K_RECOVERY_MODE_2 ||
|
||||
event->mode == IRECV_K_RECOVERY_MODE_3 ||
|
||||
event->mode == IRECV_K_RECOVERY_MODE_4))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
state.mode = DeviceMode::Recovery;
|
||||
|
||||
uint64_t ecid_val = 0;
|
||||
if (event->device_info && event->device_info->ecid)
|
||||
ecid_val = event->device_info->ecid;
|
||||
state.ecid = ecid_val;
|
||||
out.connected = true;
|
||||
out.ecid = event->device_info->ecid;
|
||||
out.mode = (event->mode == IRECV_K_DFU_MODE) ? DeviceMode::DFU : DeviceMode::Recovery;
|
||||
|
||||
irecv_client_t client = nullptr;
|
||||
|
||||
int attempts = 0;
|
||||
const int max_attempts = 10;
|
||||
|
||||
while (attempts < max_attempts)
|
||||
{
|
||||
if (irecv_open_with_ecid(&client, state.ecid) == IRECV_E_SUCCESS)
|
||||
const int maxAttempts = 10;
|
||||
while (attempts < maxAttempts) {
|
||||
if (irecv_open_with_ecid(&client, out.ecid) == IRECV_E_SUCCESS)
|
||||
break;
|
||||
|
||||
attempts++;
|
||||
++attempts;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(300));
|
||||
}
|
||||
|
||||
if (client)
|
||||
{
|
||||
LOG_VERBOSE("Opened client after %d attempts", attempts);
|
||||
|
||||
if (client) {
|
||||
irecv_device_t device = nullptr;
|
||||
if (irecv_devices_get_device_by_client(client, &device) == IRECV_E_SUCCESS && device)
|
||||
{
|
||||
state.productType = device->product_type ? device->product_type : "";
|
||||
state.displayName = device->display_name ? device->display_name : "";
|
||||
if (irecv_devices_get_device_by_client(client, &device) == IRECV_E_SUCCESS && device) {
|
||||
out.productType = device->product_type ? device->product_type : "";
|
||||
out.displayName = device->display_name ? device->display_name : "";
|
||||
}
|
||||
|
||||
irecv_close(client);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("Failed to open irecv client after retries");
|
||||
} else {
|
||||
LOG_VERBOSE("Failed to open irecv client after retries for ECID=%llu", static_cast<unsigned long long>(out.ecid));
|
||||
}
|
||||
|
||||
state.isSupported = SequenceIsSupported(state.productType);
|
||||
|
||||
#ifdef WITH_GUI
|
||||
send_device_state(frame, state);
|
||||
#endif
|
||||
out.isSupported = SequenceIsSupported(out.productType);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // WITH_GUI
|
||||
void handle_normal_add(const idevice_event_t* event) {
|
||||
if (!event)
|
||||
return;
|
||||
|
||||
if (event->conn_type != CONNECTION_USBMUXD) {
|
||||
LOG_VERBOSE("Ignoring non-USB normal mode device event");
|
||||
return;
|
||||
}
|
||||
|
||||
DeviceState discovered;
|
||||
std::string connectionType;
|
||||
if (!build_normal_state(event, discovered, connectionType))
|
||||
return;
|
||||
|
||||
if (!connectionType.empty() && connectionType != "USB") {
|
||||
LOG_VERBOSE("Ignoring over-the-air normal mode device (ConnectionType=%s)", connectionType.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (discovered.ecid == 0) {
|
||||
LOG_VERBOSE("Ignoring normal mode device without ECID");
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_mutex);
|
||||
ManagedDevice& m = g_devices[discovered.ecid];
|
||||
m.state = discovered;
|
||||
m.normalPresent = true;
|
||||
m.recoveryPresent = false;
|
||||
|
||||
if (!discovered.udid.empty())
|
||||
g_udidToEcid[discovered.udid] = discovered.ecid;
|
||||
}
|
||||
|
||||
publish_state();
|
||||
}
|
||||
|
||||
void handle_normal_remove(const idevice_event_t* event) {
|
||||
if (!event || !event->udid)
|
||||
return;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_mutex);
|
||||
auto mapIt = g_udidToEcid.find(event->udid);
|
||||
if (mapIt == g_udidToEcid.end())
|
||||
return;
|
||||
|
||||
const uint64_t ecid = mapIt->second;
|
||||
g_udidToEcid.erase(mapIt);
|
||||
|
||||
auto devIt = g_devices.find(ecid);
|
||||
if (devIt == g_devices.end())
|
||||
return;
|
||||
|
||||
devIt->second.normalPresent = false;
|
||||
if (!devIt->second.normalPresent && !devIt->second.recoveryPresent)
|
||||
g_devices.erase(devIt);
|
||||
}
|
||||
|
||||
publish_state();
|
||||
}
|
||||
|
||||
void handle_recovery_add(const irecv_device_event_t* event) {
|
||||
DeviceState discovered;
|
||||
if (!build_recovery_state(event, discovered))
|
||||
return;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_mutex);
|
||||
ManagedDevice& m = g_devices[discovered.ecid];
|
||||
m.state = discovered;
|
||||
m.recoveryPresent = true;
|
||||
m.normalPresent = false;
|
||||
}
|
||||
|
||||
publish_state();
|
||||
}
|
||||
|
||||
void handle_recovery_remove(const irecv_device_event_t* event) {
|
||||
bool changed = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_mutex);
|
||||
|
||||
uint64_t ecid = 0;
|
||||
if (event && event->device_info)
|
||||
ecid = event->device_info->ecid;
|
||||
|
||||
if (ecid == 0) {
|
||||
for (const auto& [key, value] : g_devices) {
|
||||
if (!value.recoveryPresent)
|
||||
continue;
|
||||
if (ecid != 0) {
|
||||
ecid = 0;
|
||||
break;
|
||||
}
|
||||
ecid = key;
|
||||
}
|
||||
}
|
||||
|
||||
if (ecid == 0)
|
||||
return;
|
||||
|
||||
auto devIt = g_devices.find(ecid);
|
||||
if (devIt == g_devices.end())
|
||||
return;
|
||||
|
||||
devIt->second.recoveryPresent = false;
|
||||
if (!devIt->second.normalPresent && !devIt->second.recoveryPresent)
|
||||
g_devices.erase(devIt);
|
||||
|
||||
for (auto it = g_udidToEcid.begin(); it != g_udidToEcid.end();) {
|
||||
if (it->second == ecid && g_devices.find(ecid) == g_devices.end())
|
||||
it = g_udidToEcid.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed)
|
||||
publish_state();
|
||||
}
|
||||
|
||||
bool is_supported_recovery_mode(irecv_mode mode) {
|
||||
return mode == IRECV_K_DFU_MODE ||
|
||||
mode == IRECV_K_RECOVERY_MODE_1 ||
|
||||
mode == IRECV_K_RECOVERY_MODE_2 ||
|
||||
mode == IRECV_K_RECOVERY_MODE_3 ||
|
||||
mode == IRECV_K_RECOVERY_MODE_4;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void register_device_state_callback(DeviceStateCallback cb) {
|
||||
if (!cb)
|
||||
return;
|
||||
|
||||
DeviceState snapshot;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_mutex);
|
||||
g_callbacks.push_back(cb);
|
||||
snapshot = build_public_state_locked();
|
||||
}
|
||||
|
||||
cb(snapshot);
|
||||
}
|
||||
|
||||
void ensure_device_event_system_started() {
|
||||
std::call_once(g_startOnce, []() {
|
||||
if (idevice_event_subscribe(normal_device_event_cb, nullptr) != IDEVICE_E_SUCCESS)
|
||||
LOG_ERROR("Failed to subscribe to normal mode device events");
|
||||
|
||||
if (irecv_device_event_subscribe(&g_recoveryCtx, recovery_device_event_cb, nullptr) != IRECV_E_SUCCESS)
|
||||
LOG_ERROR("Failed to subscribe to recovery/DFU device events");
|
||||
|
||||
char** udids = nullptr;
|
||||
int count = 0;
|
||||
if (idevice_get_device_list(&udids, &count) == IDEVICE_E_SUCCESS && udids) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
idevice_event_t synthetic_event;
|
||||
synthetic_event.event = IDEVICE_DEVICE_ADD;
|
||||
synthetic_event.udid = udids[i];
|
||||
synthetic_event.conn_type = CONNECTION_USBMUXD;
|
||||
handle_normal_add(&synthetic_event);
|
||||
}
|
||||
idevice_device_list_free(udids);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void normal_device_event_cb(const idevice_event_t* event, void* user_data) {
|
||||
(void)user_data;
|
||||
if (!event)
|
||||
return;
|
||||
|
||||
if (event->event == IDEVICE_DEVICE_ADD) {
|
||||
handle_normal_add(event);
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->event == IDEVICE_DEVICE_REMOVE)
|
||||
handle_normal_remove(event);
|
||||
}
|
||||
|
||||
void recovery_device_event_cb(const irecv_device_event_t* event, void* user_data) {
|
||||
(void)user_data;
|
||||
if (!event)
|
||||
return;
|
||||
|
||||
if (event->type == IRECV_DEVICE_ADD && is_supported_recovery_mode(event->mode)) {
|
||||
handle_recovery_add(event);
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->type == IRECV_DEVICE_REMOVE)
|
||||
handle_recovery_remove(event);
|
||||
}
|
||||
|
||||
#endif // WITH_GUI || WITH_TUI
|
||||
+33
-5
@@ -1,21 +1,49 @@
|
||||
#ifdef WITH_GUI
|
||||
#if defined(WITH_GUI) || defined(WITH_TUI)
|
||||
|
||||
#ifndef EVENT_H
|
||||
#define EVENT_H
|
||||
|
||||
#include "state.hpp"
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#include <wx/wx.h>
|
||||
#ifdef WITH_GUI
|
||||
# include <wx/wx.h>
|
||||
#endif
|
||||
|
||||
#include <libimobiledevice/libimobiledevice.h>
|
||||
#include <libimobiledevice/lockdown.h>
|
||||
#include <libirecovery.h>
|
||||
|
||||
class MainFrame;
|
||||
enum class DeviceMode
|
||||
{
|
||||
None,
|
||||
Normal,
|
||||
Recovery,
|
||||
DFU
|
||||
};
|
||||
|
||||
struct DeviceState
|
||||
{
|
||||
bool connected = false;
|
||||
bool isSupported = false;
|
||||
bool multipleDevices = false;
|
||||
DeviceMode mode = DeviceMode::None;
|
||||
std::string productVersion;
|
||||
std::string productType;
|
||||
std::string displayName;
|
||||
std::string udid;
|
||||
uint64_t ecid = 0;
|
||||
uint32_t connectedDeviceCount = 0;
|
||||
};
|
||||
|
||||
using DeviceStateCallback = std::function<void(const DeviceState&)>;
|
||||
void register_device_state_callback(DeviceStateCallback cb);
|
||||
void ensure_device_event_system_started();
|
||||
|
||||
void normal_device_event_cb(const idevice_event_t* event, void* user_data);
|
||||
void recovery_device_event_cb(const irecv_device_event_t* event, void* user_data);
|
||||
|
||||
#endif // EVENT_H
|
||||
|
||||
#endif // WITH_GUI
|
||||
#endif // WITH_GUI || WITH_TUI
|
||||
@@ -23,6 +23,36 @@
|
||||
#define PID_PONGO (0x4141)
|
||||
#define PID_DFU (0x1227)
|
||||
|
||||
#if defined(WITH_GUI) || defined(WITH_TUI)
|
||||
|
||||
const char* stage_to_string(int stage)
|
||||
{
|
||||
switch (stage) {
|
||||
case STAGE_SETUP: return "Checking if device is ready";
|
||||
case STAGE_SPRAY: return "Setting up the exploit (this is the heap spray)";
|
||||
case STAGE_PATCH: return "Right before trigger (this is the real bug setup)";
|
||||
case STAGE_YOLODFU: return "Booting PongoOS...";
|
||||
case STAGE_PONGO: return "Booting...";
|
||||
case STAGE_DONE: return "Done.";
|
||||
default: return "Idle.";
|
||||
}
|
||||
}
|
||||
|
||||
int stage_to_progress(int stage)
|
||||
{
|
||||
switch (stage) {
|
||||
case STAGE_SETUP: return 30;
|
||||
case STAGE_SPRAY: return 40;
|
||||
case STAGE_PATCH: return 50;
|
||||
case STAGE_YOLODFU: return 60;
|
||||
case STAGE_PONGO: return 70;
|
||||
case STAGE_DONE: return 100;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // WITH_GUI || WITH_TUI
|
||||
|
||||
static void *exploit_thread(void *arg)
|
||||
{
|
||||
LOG_VERBOSE("Starting thread for DFU devices");
|
||||
|
||||
@@ -22,6 +22,13 @@ enum exploit_stage {
|
||||
STAGE_DONE = 6,
|
||||
};
|
||||
|
||||
#if defined(WITH_GUI) || defined(WITH_TUI)
|
||||
|
||||
const char* stage_to_string(int stage);
|
||||
int stage_to_progress(int stage);
|
||||
|
||||
#endif // WITH_GUI || WITH_TUI
|
||||
|
||||
typedef struct {
|
||||
atomic_int result;
|
||||
atomic_bool stop;
|
||||
|
||||
+8
-6
@@ -11,7 +11,9 @@
|
||||
#endif
|
||||
|
||||
#include "m8/run.h"
|
||||
|
||||
#ifdef WITH_TUI
|
||||
# include "tui/Tui.hpp"
|
||||
#endif
|
||||
#include "utils.h"
|
||||
#include "globals.h"
|
||||
#include "paleinfo.h"
|
||||
@@ -31,9 +33,9 @@ void print_credits() {
|
||||
":: Made by: asdfugil, kok3shidoll, claration, mineek\n"
|
||||
":: nekohaxx, plooshi, staturnz\n"
|
||||
":: ======== Thanks to =======>\n"
|
||||
":: Thanks to: llsc12, itsnebulalol, lrdsnow, kirb, ehilwyma\n"
|
||||
":: opa334, 0x7ff, sbingner, nikias (libimobiledevice)\n"
|
||||
":: dedbeddedbed, tihmstar\n"
|
||||
":: Thanks to: itsnebulalol, llsc12, lrdsnow, dedbeddedbed\n"
|
||||
":: kirb, ehilwyma, opa334, 0x7ff, alfiecg25, sneko, sbingner\n"
|
||||
":: nikias, tihmstar\n"
|
||||
":: Checkra1n (Siguza, axi0mx, littlelailo et al.)\n"
|
||||
":: Procursus (Hayden Seay, Cameron Katri, Keto et al.)\n"
|
||||
":: ==========================>\n\n"
|
||||
@@ -329,8 +331,8 @@ int main(int argc, char* argv[], char* envp[]) {
|
||||
wxEntryCleanup();
|
||||
return 0;
|
||||
} else if (palerain_flags & palerain_option_tui) {
|
||||
LOG_WARN("TUI is not implemented yet");
|
||||
return 1;
|
||||
ui_run();
|
||||
return 0;
|
||||
} else {
|
||||
shared_t state{};
|
||||
return exploit(&state);
|
||||
|
||||
+2
-3
@@ -1,4 +1,4 @@
|
||||
#ifdef WITH_GUI
|
||||
#if defined(WITH_GUI) || defined(WITH_TUI)
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "gen/DFUHelperDeviceInfo.h"
|
||||
@@ -28,7 +28,6 @@ DfuSequence ParseSequence(const std::string& deviceKey)
|
||||
seq.imageName = layout.value("image_name", "");
|
||||
seq.imageWidth = layout.value("image_width", 0);
|
||||
seq.imageHeight = layout.value("image_height", 0);
|
||||
seq.imageOffsetX = layout.value("image_offset_x", 0);
|
||||
|
||||
if (layout.contains("buttons"))
|
||||
{
|
||||
@@ -85,4 +84,4 @@ bool SequenceIsSupported(const std::string& deviceKey)
|
||||
return !seq.steps.empty();
|
||||
}
|
||||
|
||||
#endif // WITH_GUI
|
||||
#endif // WITH_GUI || WITH_TUI
|
||||
+3
-3
@@ -1,9 +1,10 @@
|
||||
#ifdef WITH_GUI
|
||||
#if defined(WITH_GUI) || defined(WITH_TUI)
|
||||
|
||||
#ifndef SEQUENCE_H
|
||||
#define SEQUENCE_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct DfuButton
|
||||
{
|
||||
@@ -26,7 +27,6 @@ struct DfuSequence
|
||||
std::string imageName;
|
||||
int imageWidth = 0;
|
||||
int imageHeight = 0;
|
||||
int imageOffsetX = 0;
|
||||
|
||||
std::vector<DfuButton> buttons;
|
||||
std::vector<DfuStep> steps;
|
||||
@@ -37,4 +37,4 @@ bool SequenceIsSupported(const std::string& deviceKey);
|
||||
|
||||
#endif // SEQUENCE_H
|
||||
|
||||
#endif // WITH_GUI
|
||||
#endif // WITH_GUI || WITH_TUI
|
||||
@@ -1,27 +0,0 @@
|
||||
#ifndef STATE_H
|
||||
#define STATE_H
|
||||
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
|
||||
enum class DeviceMode
|
||||
{
|
||||
None,
|
||||
Normal,
|
||||
Recovery,
|
||||
DFU
|
||||
};
|
||||
|
||||
struct DeviceState
|
||||
{
|
||||
bool connected = false;
|
||||
bool isSupported = false;
|
||||
DeviceMode mode = DeviceMode::None;
|
||||
std::string productVersion;
|
||||
std::string productType;
|
||||
std::string displayName;
|
||||
std::string udid;
|
||||
uint64_t ecid;
|
||||
};
|
||||
|
||||
#endif // STATE_H
|
||||
Whitespace-only changes.
+408
@@ -0,0 +1,408 @@
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#include "Tui.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <clocale>
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include <ncurses.h>
|
||||
|
||||
#include "TuiPanel.hpp"
|
||||
#include "TuiMainPanel.hpp"
|
||||
#include "TuiSettingsPanel.hpp"
|
||||
#include "TuiRecoveryPanel.hpp"
|
||||
#include "TuiDfuPanel.hpp"
|
||||
#include "TuiExploitPanel.hpp"
|
||||
#include "TuiBranding.hpp"
|
||||
#include "../globals.h"
|
||||
#include "../utils.h"
|
||||
#include "../paleinfo.h"
|
||||
#include "../event.hpp"
|
||||
|
||||
#define WIDTH 80
|
||||
#define HEIGHT 24
|
||||
|
||||
static constexpr int LEFT_PANEL_WIDTH = 65;
|
||||
static constexpr int RIGHT_PANEL_WIDTH = WIDTH - LEFT_PANEL_WIDTH;
|
||||
static constexpr int RIGHT_INNER_WIDTH = RIGHT_PANEL_WIDTH - 2;
|
||||
static constexpr int MAIN_BUTTON_VERTICAL_GAP = 1;
|
||||
static constexpr int SIDEBAR_BUTTON_WIDTH = 13;
|
||||
static constexpr int SIDEBAR_WALL_GAP = 1;
|
||||
static constexpr short COLOR_PAIR_SUBTITLE = 1;
|
||||
|
||||
static std::string trim_button_label(std::string label) {
|
||||
while (!label.empty() && label.front() == ' ') {
|
||||
label.erase(label.begin());
|
||||
}
|
||||
while (!label.empty() && label.back() == ' ') {
|
||||
label.pop_back();
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
static std::string format_sidebar_button_label(const char* raw_label, int button_width) {
|
||||
if (button_width < 2) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (!raw_label) {
|
||||
return std::string(static_cast<size_t>(button_width), ' ');
|
||||
}
|
||||
|
||||
std::string label(raw_label);
|
||||
if (label.size() >= 2 && label.front() == '[' && label.back() == ']') {
|
||||
label = label.substr(1, label.size() - 2);
|
||||
}
|
||||
|
||||
label = trim_button_label(label);
|
||||
|
||||
const int inner_width = button_width - 2;
|
||||
if (inner_width <= 0) {
|
||||
return "[]";
|
||||
}
|
||||
|
||||
if (static_cast<int>(label.size()) > inner_width) {
|
||||
label.resize(static_cast<size_t>(inner_width));
|
||||
}
|
||||
|
||||
const int left_pad = std::max(0, (inner_width - static_cast<int>(label.size())) / 2);
|
||||
const int right_pad = std::max(0, inner_width - static_cast<int>(label.size()) - left_pad);
|
||||
|
||||
return "[" + std::string(static_cast<size_t>(left_pad), ' ') + label + std::string(static_cast<size_t>(right_pad), ' ') + "]";
|
||||
}
|
||||
|
||||
static void draw_panel_border(int y, int x, int width, int height, const char* title = nullptr) {
|
||||
if (title) {
|
||||
char title_buf[64];
|
||||
std::snprintf(title_buf, sizeof(title_buf), "[ %s ]", title);
|
||||
int title_len = static_cast<int>(std::strlen(title_buf));
|
||||
int title_start_x = x + (width - title_len) / 2;
|
||||
|
||||
mvhline(y, x, ACS_HLINE, title_start_x - x);
|
||||
mvprintw(y, title_start_x, "%s", title_buf);
|
||||
mvhline(y, title_start_x + title_len, ACS_HLINE, (x + width) - (title_start_x + title_len));
|
||||
} else {
|
||||
mvhline(y, x, ACS_HLINE, width);
|
||||
}
|
||||
|
||||
mvhline(y + height - 1, x, ACS_HLINE, width);
|
||||
mvvline(y, x, ACS_VLINE, height);
|
||||
mvvline(y, x + width - 1, ACS_VLINE, height);
|
||||
|
||||
mvaddch(y, x, ACS_ULCORNER);
|
||||
mvaddch(y, x + width - 1, ACS_URCORNER);
|
||||
mvaddch(y + height - 1, x, ACS_LLCORNER);
|
||||
mvaddch(y + height - 1, x + width - 1, ACS_LRCORNER);
|
||||
}
|
||||
|
||||
static TuiFrame* g_frame = nullptr;
|
||||
|
||||
TuiFrame::TuiFrame() {
|
||||
InitPanels();
|
||||
g_frame = this;
|
||||
InitDeviceEventListeners();
|
||||
}
|
||||
|
||||
TuiFrame::~TuiFrame() {
|
||||
if (g_frame == this) {
|
||||
g_frame = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void TuiFrame::InitPanels() {
|
||||
m_panels.resize(PANEL_COUNT);
|
||||
m_panels[PANEL_MAIN] = std::make_unique<TuiMainPanel>(this);
|
||||
m_panels[PANEL_OPTIONS] = std::make_unique<TuiSettingsPanel>(this);
|
||||
m_panels[PANEL_START_1] = std::make_unique<TuiRecoveryPanel>(this);
|
||||
m_panels[PANEL_START_2] = std::make_unique<TuiDfuPanel>(this);
|
||||
m_panels[PANEL_FINAL] = std::make_unique<TuiExploitPanel>(this);
|
||||
}
|
||||
|
||||
void TuiFrame::InitDeviceEventListeners() {
|
||||
register_device_state_callback([](const DeviceState& state) {
|
||||
update_tui_device_state(&state);
|
||||
});
|
||||
ensure_device_event_system_started();
|
||||
}
|
||||
|
||||
void TuiFrame::ActivatePanelIfNeeded(PanelState previous_panel) {
|
||||
if (m_currentPanel == previous_panel) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_panels[m_currentPanel]) {
|
||||
m_panels[m_currentPanel]->on_show();
|
||||
}
|
||||
}
|
||||
|
||||
void TuiFrame::DrawUi() {
|
||||
int rows, cols;
|
||||
getmaxyx(stdscr, rows, cols);
|
||||
|
||||
erase();
|
||||
|
||||
if (rows < HEIGHT || cols < WIDTH) {
|
||||
mvprintw(rows / 2, (cols - 34) / 2, "Terminal too small, need 80x24");
|
||||
refresh();
|
||||
return;
|
||||
}
|
||||
|
||||
int start_y = (rows - HEIGHT) / 2;
|
||||
int start_x = (cols - WIDTH) / 2;
|
||||
|
||||
|
||||
const int left_x = start_x;
|
||||
const int right_x = start_x + LEFT_PANEL_WIDTH;
|
||||
|
||||
draw_panel_border(start_y, left_x, LEFT_PANEL_WIDTH, HEIGHT, "palera1n - Version beta " PALERAIN_VERSION);
|
||||
draw_panel_border(start_y, right_x, RIGHT_PANEL_WIDTH, HEIGHT);
|
||||
|
||||
if (m_panels[m_currentPanel]) {
|
||||
m_panels[m_currentPanel]->draw(start_y, left_x, m_selected);
|
||||
}
|
||||
|
||||
if (m_panels[m_currentPanel]) {
|
||||
int btn_cnt = m_panels[m_currentPanel]->get_button_count();
|
||||
int total_items = m_panels[m_currentPanel]->get_total_items();
|
||||
const char **panel_buttons = m_panels[m_currentPanel]->get_buttons();
|
||||
|
||||
const int sidebar_x = right_x;
|
||||
const int sidebar_inner_x = sidebar_x + 1;
|
||||
const int sidebar_inner_width = RIGHT_INNER_WIDTH;
|
||||
const int sidebar_top = start_y + 1;
|
||||
const int sidebar_bottom = start_y + HEIGHT - 2;
|
||||
const int content_top = sidebar_top + SIDEBAR_WALL_GAP;
|
||||
const int content_bottom = sidebar_bottom;
|
||||
const int content_x = sidebar_inner_x + SIDEBAR_WALL_GAP;
|
||||
const int content_width = std::max(1, sidebar_inner_width - (SIDEBAR_WALL_GAP * 2));
|
||||
|
||||
draw_tui_sidebar_branding(sidebar_top, sidebar_inner_x, sidebar_inner_width);
|
||||
|
||||
std::vector<std::string> labels;
|
||||
labels.reserve(static_cast<size_t>(btn_cnt));
|
||||
const int button_width = std::clamp(SIDEBAR_BUTTON_WIDTH, 8, content_width);
|
||||
|
||||
for (int i = 0; i < btn_cnt; ++i) {
|
||||
labels.push_back(format_sidebar_button_label(panel_buttons[i], button_width));
|
||||
}
|
||||
|
||||
const int buttons_block_height = std::max(1, btn_cnt + ((btn_cnt - 1) * MAIN_BUTTON_VERTICAL_GAP));
|
||||
const int first_y = std::max(content_top, content_bottom - buttons_block_height + 1);
|
||||
const int button_x = content_x + std::max(0, (content_width - button_width) / 2);
|
||||
|
||||
for (int i = 0; i < btn_cnt; ++i) {
|
||||
if (i > 0) {
|
||||
const int separator_y = first_y + (i * (MAIN_BUTTON_VERTICAL_GAP + 1)) - 1;
|
||||
mvhline(separator_y, sidebar_inner_x, ACS_HLINE, sidebar_inner_width);
|
||||
}
|
||||
|
||||
int btn_idx = total_items - btn_cnt + i;
|
||||
bool enabled = m_panels[m_currentPanel]->is_button_enabled(btn_idx);
|
||||
const std::string& label = labels[static_cast<size_t>(i)];
|
||||
|
||||
if (!enabled) {
|
||||
attron(A_DIM);
|
||||
mvprintw(first_y + (i * (MAIN_BUTTON_VERTICAL_GAP + 1)), button_x, "%s", label.c_str());
|
||||
attroff(A_DIM);
|
||||
} else {
|
||||
if (btn_idx == m_selected) attron(A_REVERSE);
|
||||
mvprintw(first_y + (i * (MAIN_BUTTON_VERTICAL_GAP + 1)), button_x, "%s", label.c_str());
|
||||
if (btn_idx == m_selected) attroff(A_REVERSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
void TuiFrame::HandleInput(int ch, int start_y, int start_x) {
|
||||
if (ch == ERR || !m_panels[m_currentPanel]) return;
|
||||
|
||||
int total_items = m_panels[m_currentPanel]->get_total_items();
|
||||
if (total_items == 0) return;
|
||||
|
||||
PanelState previous_panel = m_currentPanel;
|
||||
|
||||
switch(ch) {
|
||||
case KEY_UP:
|
||||
case KEY_LEFT: {
|
||||
int prev = m_selected;
|
||||
do {
|
||||
prev = (prev - 1 + total_items) % total_items;
|
||||
} while (!m_panels[m_currentPanel]->is_button_enabled(prev) && prev != m_selected);
|
||||
m_selected = prev;
|
||||
break;
|
||||
}
|
||||
|
||||
case KEY_DOWN:
|
||||
case KEY_RIGHT: {
|
||||
int next = m_selected;
|
||||
do {
|
||||
next = (next + 1) % total_items;
|
||||
} while (!m_panels[m_currentPanel]->is_button_enabled(next) && next != m_selected);
|
||||
m_selected = next;
|
||||
break;
|
||||
}
|
||||
|
||||
case KEY_ENTER:
|
||||
case 10:
|
||||
case 13:
|
||||
if (!m_panels[m_currentPanel]->is_button_enabled(m_selected)) return;
|
||||
m_panels[m_currentPanel]->handle_enter(m_selected, start_y, start_x);
|
||||
break;
|
||||
}
|
||||
|
||||
ActivatePanelIfNeeded(previous_panel);
|
||||
}
|
||||
|
||||
void TuiFrame::Run() {
|
||||
m_running = true;
|
||||
std::setlocale(LC_ALL, "");
|
||||
initscr();
|
||||
noecho();
|
||||
cbreak();
|
||||
curs_set(0);
|
||||
keypad(stdscr, TRUE);
|
||||
timeout(100);
|
||||
|
||||
if (has_colors()) {
|
||||
start_color();
|
||||
use_default_colors();
|
||||
init_pair(COLOR_PAIR_SUBTITLE, COLOR_YELLOW, -1);
|
||||
}
|
||||
|
||||
int ch;
|
||||
|
||||
while (m_running) {
|
||||
PanelState previous_panel = m_currentPanel;
|
||||
|
||||
if (m_panels[m_currentPanel]) {
|
||||
m_panels[m_currentPanel]->update();
|
||||
}
|
||||
|
||||
ActivatePanelIfNeeded(previous_panel);
|
||||
|
||||
DrawUi();
|
||||
ch = getch();
|
||||
|
||||
if (ch == 'q' || ch == 'Q') {
|
||||
Stop();
|
||||
continue;
|
||||
}
|
||||
|
||||
int rows, cols;
|
||||
getmaxyx(stdscr, rows, cols);
|
||||
if (rows >= HEIGHT && cols >= WIDTH) {
|
||||
int start_y = (rows - HEIGHT) / 2;
|
||||
int start_x = (cols - WIDTH) / 2;
|
||||
HandleInput(ch, start_y, start_x);
|
||||
}
|
||||
}
|
||||
|
||||
endwin();
|
||||
}
|
||||
|
||||
void TuiFrame::ShowMain(int selected) {
|
||||
m_currentPanel = PANEL_MAIN;
|
||||
m_selected = selected;
|
||||
}
|
||||
|
||||
void TuiFrame::ShowSettings(int selected) {
|
||||
m_currentPanel = PANEL_OPTIONS;
|
||||
m_selected = selected;
|
||||
}
|
||||
|
||||
void TuiFrame::ShowRecovery(int selected) {
|
||||
m_currentPanel = PANEL_START_1;
|
||||
m_selected = selected;
|
||||
}
|
||||
|
||||
void TuiFrame::ShowDfu(int selected) {
|
||||
m_currentPanel = PANEL_START_2;
|
||||
m_selected = selected;
|
||||
}
|
||||
|
||||
void TuiFrame::ShowExploit(int selected) {
|
||||
m_currentPanel = PANEL_FINAL;
|
||||
m_selected = selected;
|
||||
}
|
||||
|
||||
PanelState TuiFrame::GetCurrentPanel() const {
|
||||
return m_currentPanel;
|
||||
}
|
||||
|
||||
int TuiFrame::GetSelected() const {
|
||||
return m_selected;
|
||||
}
|
||||
|
||||
void TuiFrame::SetSelected(int selected) {
|
||||
m_selected = selected;
|
||||
}
|
||||
|
||||
bool TuiFrame::IsRunning() const {
|
||||
return m_running;
|
||||
}
|
||||
|
||||
void TuiFrame::Stop() {
|
||||
m_running = false;
|
||||
}
|
||||
|
||||
DeviceState TuiFrame::GetDeviceState() const {
|
||||
std::lock_guard<std::mutex> lock(m_deviceMutex);
|
||||
return m_device;
|
||||
}
|
||||
|
||||
void TuiFrame::UpdateDeviceState(const DeviceState& state) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_deviceMutex);
|
||||
m_device = state;
|
||||
}
|
||||
|
||||
const PanelState previous_panel = m_currentPanel;
|
||||
if (m_currentPanel != PANEL_MAIN && m_currentPanel != PANEL_OPTIONS && m_panels[m_currentPanel]) {
|
||||
m_panels[m_currentPanel]->handle_device_update(state);
|
||||
ActivatePanelIfNeeded(previous_panel);
|
||||
}
|
||||
}
|
||||
|
||||
TuiMainPanel* TuiFrame::GetMainPanel() const {
|
||||
return static_cast<TuiMainPanel*>(m_panels[PANEL_MAIN].get());
|
||||
}
|
||||
|
||||
TuiSettingsPanel* TuiFrame::GetSettingsPanel() const {
|
||||
return static_cast<TuiSettingsPanel*>(m_panels[PANEL_OPTIONS].get());
|
||||
}
|
||||
|
||||
TuiRecoveryPanel* TuiFrame::GetRecoveryPanel() const {
|
||||
return static_cast<TuiRecoveryPanel*>(m_panels[PANEL_START_1].get());
|
||||
}
|
||||
|
||||
TuiDfuPanel* TuiFrame::GetDfuPanel() const {
|
||||
return static_cast<TuiDfuPanel*>(m_panels[PANEL_START_2].get());
|
||||
}
|
||||
|
||||
TuiExploitPanel* TuiFrame::GetExploitPanel() const {
|
||||
return static_cast<TuiExploitPanel*>(m_panels[PANEL_FINAL].get());
|
||||
}
|
||||
|
||||
void update_tui_device_state(const DeviceState* new_state) {
|
||||
if (!g_frame || !new_state) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_frame->UpdateDeviceState(*new_state);
|
||||
}
|
||||
|
||||
void ui_run(void) {
|
||||
TuiFrame frame;
|
||||
frame.Run();
|
||||
}
|
||||
|
||||
#endif // WITH_TUI
|
||||
@@ -0,0 +1,71 @@
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#ifndef TUI_HPP
|
||||
#define TUI_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include "../event.hpp"
|
||||
#include "TuiPanel.hpp"
|
||||
|
||||
class TuiPanel;
|
||||
class TuiMainPanel;
|
||||
class TuiSettingsPanel;
|
||||
class TuiRecoveryPanel;
|
||||
class TuiDfuPanel;
|
||||
class TuiExploitPanel;
|
||||
|
||||
class TuiFrame {
|
||||
public:
|
||||
TuiFrame();
|
||||
~TuiFrame();
|
||||
|
||||
void Run();
|
||||
|
||||
void ShowMain(int selected = 1);
|
||||
void ShowSettings(int selected = 5);
|
||||
void ShowRecovery(int selected = 1);
|
||||
void ShowDfu(int selected = 1);
|
||||
void ShowExploit(int selected = 0);
|
||||
|
||||
PanelState GetCurrentPanel() const;
|
||||
int GetSelected() const;
|
||||
void SetSelected(int selected);
|
||||
|
||||
bool IsRunning() const;
|
||||
void Stop();
|
||||
|
||||
DeviceState GetDeviceState() const;
|
||||
void UpdateDeviceState(const DeviceState& state);
|
||||
|
||||
TuiMainPanel* GetMainPanel() const;
|
||||
TuiSettingsPanel* GetSettingsPanel() const;
|
||||
TuiRecoveryPanel* GetRecoveryPanel() const;
|
||||
TuiDfuPanel* GetDfuPanel() const;
|
||||
TuiExploitPanel* GetExploitPanel() const;
|
||||
|
||||
private:
|
||||
void InitPanels();
|
||||
void InitDeviceEventListeners();
|
||||
void ActivatePanelIfNeeded(PanelState previous_panel);
|
||||
void DrawUi();
|
||||
void HandleInput(int ch, int start_y, int start_x);
|
||||
|
||||
PanelState m_currentPanel = PANEL_MAIN;
|
||||
int m_selected = 1;
|
||||
bool m_running = true;
|
||||
|
||||
DeviceState m_device;
|
||||
mutable std::mutex m_deviceMutex;
|
||||
|
||||
std::vector<std::unique_ptr<TuiPanel>> m_panels;
|
||||
};
|
||||
|
||||
void ui_run(void);
|
||||
void update_tui_device_state(const DeviceState* new_state);
|
||||
|
||||
#endif // TUI_HPP
|
||||
|
||||
#endif // WITH_TUI
|
||||
@@ -0,0 +1,52 @@
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#include "TuiBranding.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
#include <ncurses.h>
|
||||
|
||||
static constexpr int kBrandingLeftTrim = 3;
|
||||
|
||||
void draw_tui_sidebar_branding(int y, int x, int width) {
|
||||
static const char* art[] = {
|
||||
" ++ ",
|
||||
" #@@# ",
|
||||
" +@@@@+ ",
|
||||
" :@@@@@@: ",
|
||||
" #@@@@@@# ",
|
||||
" +@@@@@@@@+ ",
|
||||
" :#*@@@@@@*#: ",
|
||||
" %##palera1n% ",
|
||||
" .####%@@%####. ",
|
||||
" @############@ ",
|
||||
"=@@*########*@@=",
|
||||
"+@@@@%####%@@@@+",
|
||||
":@@@@%%**%%@@@@:",
|
||||
" +@@%%*@@*%%@@+ ",
|
||||
" ++%@@@@%++ ",
|
||||
" :++: "
|
||||
};
|
||||
static const int art_count = static_cast<int>(sizeof(art) / sizeof(art[0]));
|
||||
|
||||
int max_art_width = 0;
|
||||
for (int i = 0; i < art_count; ++i) {
|
||||
const int line_width = std::max(0, static_cast<int>(std::strlen(art[i])) - kBrandingLeftTrim);
|
||||
max_art_width = std::max(max_art_width, line_width);
|
||||
}
|
||||
|
||||
const int centered_x = x + std::max(0, (width - max_art_width) / 2);
|
||||
const int art_x = std::max(x, centered_x - 7);
|
||||
|
||||
attron(A_DIM);
|
||||
for (int i = 0; i < art_count; ++i) {
|
||||
const char* line = art[i];
|
||||
const int line_len = static_cast<int>(std::strlen(line));
|
||||
const int trim = std::min(kBrandingLeftTrim, line_len);
|
||||
mvprintw(y + i, art_x, "%.*s", width, line + trim);
|
||||
}
|
||||
attroff(A_DIM);
|
||||
}
|
||||
|
||||
#endif // WITH_TUI
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#ifndef TUI_BRANDING_HPP
|
||||
#define TUI_BRANDING_HPP
|
||||
|
||||
void draw_tui_sidebar_branding(int y, int x, int width);
|
||||
|
||||
#endif // TUI_BRANDING_HPP
|
||||
|
||||
#endif // WITH_TUI
|
||||
@@ -0,0 +1,139 @@
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#include "TuiDfuArt.hpp"
|
||||
|
||||
#include <ncurses.h>
|
||||
|
||||
static const char* kIpadArt[] = {
|
||||
" __ ",
|
||||
" ┌───────────────────┐ ",
|
||||
" │ o === │ ",
|
||||
" │███████████████████│ ",
|
||||
" |│███████████████████│ ",
|
||||
" |│███████████████████│ ",
|
||||
" │███████████████████│ ",
|
||||
" │███████████████████│ ",
|
||||
" │███████████████████│ ",
|
||||
" │███████████████████│ ",
|
||||
" │███████████████████│ ",
|
||||
" │███████████████████│ ",
|
||||
" │███████████████████│ ",
|
||||
" │ ┌─┐ │ ",
|
||||
" │ └─┘ │ ",
|
||||
" └───────────────────┘ ",
|
||||
};
|
||||
|
||||
static const char* kIphoneSeArt[] = {
|
||||
" __ ",
|
||||
" ┌─────────────┐ ",
|
||||
" │ o === │ ",
|
||||
" │█████████████│ ",
|
||||
" |│█████████████│ ",
|
||||
" |│█████████████│ ",
|
||||
" │█████████████│ ",
|
||||
" │█████████████│ ",
|
||||
" │█████████████│ ",
|
||||
" │█████████████│ ",
|
||||
" │█████████████│ ",
|
||||
" │█████████████│ ",
|
||||
" │█████████████│ ",
|
||||
" │ ┌─┐ │ ",
|
||||
" │ └─┘ │ ",
|
||||
" └─────────────┘ ",
|
||||
};
|
||||
|
||||
static const char* kIphone7Art[] = {
|
||||
" ",
|
||||
" ┌─────────────┐ ",
|
||||
" │ o === │ ",
|
||||
" │█████████████│ ",
|
||||
" |│█████████████│| ",
|
||||
" |│█████████████│ ",
|
||||
" │█████████████│ ",
|
||||
" │█████████████│ ",
|
||||
" │█████████████│ ",
|
||||
" │█████████████│ ",
|
||||
" │█████████████│ ",
|
||||
" │█████████████│ ",
|
||||
" │█████████████│ ",
|
||||
" │ ┌─┐ │ ",
|
||||
" │ └─┘ │ ",
|
||||
" └─────────────┘ ",
|
||||
};
|
||||
|
||||
static const char* kIphoneXArt[] = {
|
||||
" ",
|
||||
" ┌─────────────┐ ",
|
||||
" │███ o === ███│ ",
|
||||
" │█████████████│ ",
|
||||
" |│█████████████││ ",
|
||||
" |│█████████████││ ",
|
||||
" │█████████████│ ",
|
||||
" │█████████████│ ",
|
||||
" │█████████████│ ",
|
||||
" │█████████████│ ",
|
||||
" │█████████████│ ",
|
||||
" │█████████████│ ",
|
||||
" │█████████████│ ",
|
||||
" │█████████████│ ",
|
||||
" │█████████████│ ",
|
||||
" └─────────────┘ ",
|
||||
};
|
||||
|
||||
static const char* kRemoteArt[] = {
|
||||
" ",
|
||||
" ┌─────────┐ ",
|
||||
" │ │ ",
|
||||
" │ │ ",
|
||||
" │ │ ",
|
||||
" │ ┌─┐ ┌─┐ │ ",
|
||||
" │ └─┘ └─┘ │ ",
|
||||
" │ ┌─┐ ┌─┐ │ ",
|
||||
" │ └─┘ │+│ │ ",
|
||||
" │ ┌─┐ │-│ │ ",
|
||||
" │ └─┘ └─┘ │ ",
|
||||
" │ │ ",
|
||||
" │ │ ",
|
||||
" │ │ ",
|
||||
" └─────────┘ ",
|
||||
};
|
||||
|
||||
static const char* kAtvStdBrdArt[] = {
|
||||
" art not yet implemented ",
|
||||
};
|
||||
|
||||
static const DfuAsciiPreview kPreviews[] = {
|
||||
{ "ipad", kIpadArt, 16, 20, -1 },
|
||||
{ "ipadmini", kIpadArt, 16, 19, -1 },
|
||||
{ "ipodtouch", kIphoneSeArt, 16, 22, -1 },
|
||||
{ "ipodtouch7", kIpadArt, 16, 20, -1 },
|
||||
{ "siriremote", kRemoteArt, 15, 22, -1 },
|
||||
{ "atv_std_brd", kAtvStdBrdArt, 1, 20, -1 },
|
||||
{ "iphonese", kIphoneSeArt, 16, 22, -1 },
|
||||
{ "iphone6s", kIphone7Art, 16, 22, -1 },
|
||||
{ "iphone7", kIphone7Art, 16, 22, -1 },
|
||||
{ "iphone8", kIphone7Art, 16, 22, -1 },
|
||||
{ "iphonex", kIphoneXArt, 16, 22, -1 },
|
||||
};
|
||||
|
||||
const DfuAsciiPreview* find_dfu_ascii_preview(const std::string& imageName) {
|
||||
for (const auto& preview : kPreviews) {
|
||||
if (imageName == preview.imageName) {
|
||||
return &preview;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void draw_dfu_ascii_preview(const DfuAsciiPreview& preview, int sx, int sy) {
|
||||
const int start_x = sx + preview.xOffset;
|
||||
const int start_y = sy + 4 + preview.yOffset;
|
||||
|
||||
attron(A_DIM);
|
||||
for (int i = 0; i < preview.lineCount; ++i) {
|
||||
mvprintw(start_y + i, start_x, "%s", preview.lines[i]);
|
||||
}
|
||||
attroff(A_DIM);
|
||||
}
|
||||
|
||||
#endif // WITH_TUI
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#ifndef TUI_DFU_ART_HPP
|
||||
#define TUI_DFU_ART_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
struct DfuAsciiPreview {
|
||||
const char* imageName;
|
||||
const char* const* lines;
|
||||
int lineCount;
|
||||
int xOffset;
|
||||
int yOffset;
|
||||
};
|
||||
|
||||
const DfuAsciiPreview* find_dfu_ascii_preview(const std::string& imageName);
|
||||
void draw_dfu_ascii_preview(const DfuAsciiPreview& preview, int sx, int sy);
|
||||
|
||||
#endif // TUI_DFU_ART_HPP
|
||||
|
||||
#endif // WITH_TUI
|
||||
@@ -0,0 +1,359 @@
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#include "TuiDfuPanel.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <ncurses.h>
|
||||
#include <libirecovery.h>
|
||||
|
||||
#include "Tui.hpp"
|
||||
#include "TuiDfuArt.hpp"
|
||||
#include "TuiText.hpp"
|
||||
#include "TuiRecoveryPanel.hpp"
|
||||
#include "../sequence.hpp"
|
||||
#include "../event.hpp"
|
||||
#include "../globals.h"
|
||||
#include "../paleinfo.h"
|
||||
|
||||
static const char *buttons[] = { "Back", "Start" };
|
||||
static constexpr int kDfuPanelCoordinateWidth = 310;
|
||||
static constexpr int kTuiDeviceCanvasWidth = 52;
|
||||
static constexpr int kDfuButtonCoordinateMaxY = 200;
|
||||
static constexpr int kDfuContentX = 2;
|
||||
static constexpr int kDfuContentWidth = 60;
|
||||
static constexpr int kDfuStepsMaxLines = 5;
|
||||
static constexpr int kDfuStepsBottomY = 22;
|
||||
static constexpr int kDfuButtonX = 2;
|
||||
static constexpr int kDfuButtonY = 4;
|
||||
|
||||
TuiDfuPanel::TuiDfuPanel(TuiFrame* frame)
|
||||
: TuiPanel(frame) {}
|
||||
|
||||
const char** TuiDfuPanel::get_buttons() const {
|
||||
return buttons;
|
||||
}
|
||||
|
||||
void TuiDfuPanel::reset_sequence_state() {
|
||||
m_index = 0;
|
||||
m_stepRemaining = -1;
|
||||
m_actionExecutedIndex = -1;
|
||||
m_isEnteringDfu = false;
|
||||
m_dfuSuccess = false;
|
||||
m_waitingForDfuTransition = false;
|
||||
m_dfuSuccessAt = std::chrono::steady_clock::time_point{};
|
||||
m_last_tick = std::chrono::steady_clock::time_point{};
|
||||
}
|
||||
|
||||
void TuiDfuPanel::load_sequence_for_product(const std::string& product_type, bool reset_state) {
|
||||
if (m_current_product_type != product_type) {
|
||||
m_current_product_type = product_type;
|
||||
m_sequence = ParseSequence(product_type);
|
||||
}
|
||||
|
||||
if (reset_state) {
|
||||
reset_sequence_state();
|
||||
}
|
||||
}
|
||||
|
||||
void TuiDfuPanel::on_show() {
|
||||
const DeviceState state = GetFrame()->GetDeviceState();
|
||||
load_sequence_for_product(state.productType, true);
|
||||
GetFrame()->SetSelected(1);
|
||||
|
||||
if (palerain_flags & palerain_option_quick) {
|
||||
start_sequence();
|
||||
}
|
||||
}
|
||||
|
||||
void TuiDfuPanel::update() {
|
||||
if (!m_waitingForDfuTransition || !m_dfuSuccess) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
if (std::chrono::duration_cast<std::chrono::seconds>(now - m_dfuSuccessAt).count() < 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_waitingForDfuTransition = false;
|
||||
GetFrame()->ShowExploit(0);
|
||||
}
|
||||
|
||||
bool TuiDfuPanel::is_button_enabled(int btn_idx) const {
|
||||
(void)btn_idx;
|
||||
return !m_isEnteringDfu && !m_dfuSuccess;
|
||||
}
|
||||
|
||||
void TuiDfuPanel::start_sequence() {
|
||||
if (m_sequence.steps.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_isEnteringDfu = true;
|
||||
m_dfuSuccess = false;
|
||||
m_index = 0;
|
||||
m_stepRemaining = -1;
|
||||
m_actionExecutedIndex = -1;
|
||||
m_last_tick = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
void TuiDfuPanel::update_sequence_timer() {
|
||||
if (!m_isEnteringDfu || m_sequence.steps.empty() || m_index >= m_sequence.steps.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
if (std::chrono::duration_cast<std::chrono::seconds>(now - m_last_tick).count() >= 1) {
|
||||
m_last_tick = now;
|
||||
|
||||
const auto& step = m_sequence.steps[m_index];
|
||||
if (m_stepRemaining < 0) {
|
||||
m_stepRemaining = step.duration;
|
||||
}
|
||||
|
||||
if (m_stepRemaining > 0) {
|
||||
--m_stepRemaining;
|
||||
}
|
||||
|
||||
if (m_stepRemaining == 0) {
|
||||
++m_index;
|
||||
m_stepRemaining = -1;
|
||||
}
|
||||
|
||||
if (m_index < m_sequence.steps.size()) {
|
||||
const auto& activeStep = m_sequence.steps[m_index];
|
||||
if (!activeStep.action.empty() && m_actionExecutedIndex != (int)m_index) {
|
||||
m_actionExecutedIndex = m_index;
|
||||
if (activeStep.action == "reboot") {
|
||||
reboot();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int TuiDfuPanel::scale_coord(int value, int input_span, int output_span) const {
|
||||
if (input_span <= 0 || output_span <= 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (value * (output_span - 1)) / input_span;
|
||||
}
|
||||
|
||||
void TuiDfuPanel::draw_sequence_buttons(int button_x, int button_y, int button_width, int button_height) const {
|
||||
if (m_sequence.buttons.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::unordered_set<std::string> active_buttons;
|
||||
if (m_isEnteringDfu && m_index < m_sequence.steps.size()) {
|
||||
const auto& buttons_for_step = m_sequence.steps[m_index].activeButtons;
|
||||
active_buttons.insert(buttons_for_step.begin(), buttons_for_step.end());
|
||||
}
|
||||
|
||||
for (const auto& btn : m_sequence.buttons) {
|
||||
const int mapped_x = button_x + scale_coord(btn.x, kDfuPanelCoordinateWidth, button_width);
|
||||
const int mapped_y = button_y + scale_coord(btn.y, kDfuButtonCoordinateMaxY, button_height);
|
||||
|
||||
const int label_x = std::clamp(mapped_x, button_x, button_x + std::max(0, button_width - 1));
|
||||
|
||||
const bool is_active = active_buttons.find(btn.id) != active_buttons.end();
|
||||
if (is_active) {
|
||||
attron(A_BOLD);
|
||||
} else {
|
||||
attron(A_DIM);
|
||||
}
|
||||
|
||||
mvprintw(mapped_y, label_x, "%s", btn.name.c_str());
|
||||
|
||||
if (is_active) {
|
||||
attroff(A_BOLD);
|
||||
} else {
|
||||
attroff(A_DIM);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TuiDfuPanel::draw_wrapped_steps(int steps_x, int steps_width, int max_lines, int bottom_y) const {
|
||||
if (steps_width <= 0 || max_lines <= 0 || m_sequence.steps.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct RenderedLine {
|
||||
bool is_current;
|
||||
bool is_completed;
|
||||
std::string text;
|
||||
};
|
||||
|
||||
std::vector<RenderedLine> rendered_lines;
|
||||
rendered_lines.reserve(static_cast<size_t>(max_lines));
|
||||
|
||||
for (size_t i = 0; i < m_sequence.steps.size(); ++i) {
|
||||
const auto& step = m_sequence.steps[i];
|
||||
const int seconds_left = (m_isEnteringDfu && i == m_index && m_stepRemaining >= 0)
|
||||
? m_stepRemaining
|
||||
: ((i < m_index) || m_dfuSuccess) ? 0 : step.duration;
|
||||
|
||||
std::string text = std::to_string(i + 1) + ". " + step.description + " (" + std::to_string(seconds_left) + ")";
|
||||
std::vector<std::string> wrapped;
|
||||
tui_text::append_wrapped_lines(text, steps_width, wrapped);
|
||||
|
||||
const bool is_current = m_isEnteringDfu && i == m_index;
|
||||
const bool is_completed = (i < m_index) || m_dfuSuccess;
|
||||
for (const auto& line : wrapped) {
|
||||
if (static_cast<int>(rendered_lines.size()) >= max_lines) {
|
||||
break;
|
||||
}
|
||||
rendered_lines.push_back({ is_current, is_completed, line });
|
||||
}
|
||||
|
||||
if (static_cast<int>(rendered_lines.size()) >= max_lines) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const int start_y = bottom_y - static_cast<int>(rendered_lines.size()) + 1;
|
||||
for (size_t i = 0; i < rendered_lines.size(); ++i) {
|
||||
const auto& line = rendered_lines[i];
|
||||
|
||||
if (line.is_current) {
|
||||
attron(A_BOLD);
|
||||
} else if (line.is_completed) {
|
||||
attron(A_DIM);
|
||||
}
|
||||
|
||||
mvprintw(start_y + static_cast<int>(i), steps_x, "%.*s", steps_width, line.text.c_str());
|
||||
|
||||
if (line.is_current) {
|
||||
attroff(A_BOLD);
|
||||
} else if (line.is_completed) {
|
||||
attroff(A_DIM);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TuiDfuPanel::draw(int sy, int sx, int selected) {
|
||||
(void)selected;
|
||||
update_sequence_timer();
|
||||
|
||||
const int content_x = sx + kDfuContentX;
|
||||
|
||||
if (m_sequence.steps.empty()) {
|
||||
mvprintw(sy + 2, content_x, "No DFU helper sequence is available for this device.");
|
||||
} else if (m_dfuSuccess) {
|
||||
mvprintw(sy + 2, content_x, "Device entered DFU mode successfully.");
|
||||
} else {
|
||||
tui_text::draw_wrapped_text(
|
||||
sy + 2,
|
||||
content_x,
|
||||
kDfuContentWidth,
|
||||
"Time to put the device into DFU mode.",
|
||||
2
|
||||
);
|
||||
}
|
||||
|
||||
const int steps_x = content_x;
|
||||
const int steps_width = kDfuContentWidth;
|
||||
const int steps_max_lines = kDfuStepsMaxLines;
|
||||
const int steps_bottom_y = sy + kDfuStepsBottomY;
|
||||
|
||||
const int button_x = sx + kDfuButtonX;
|
||||
const int button_y = sy + kDfuButtonY;
|
||||
const int button_width = kTuiDeviceCanvasWidth - 1;
|
||||
const int button_height = (steps_bottom_y - steps_max_lines + 1) - button_y;
|
||||
|
||||
if (const DfuAsciiPreview* preview = find_dfu_ascii_preview(m_sequence.imageName)) {
|
||||
draw_dfu_ascii_preview(*preview, sx, sy);
|
||||
}
|
||||
|
||||
draw_sequence_buttons(button_x, button_y, button_width, button_height);
|
||||
draw_wrapped_steps(steps_x, steps_width, steps_max_lines, steps_bottom_y);
|
||||
}
|
||||
|
||||
void TuiDfuPanel::handle_enter(int selected, int sy, int sx) {
|
||||
switch(selected) {
|
||||
case 0: { // Back
|
||||
reboot();
|
||||
m_isEnteringDfu = false;
|
||||
m_dfuSuccess = false;
|
||||
GetFrame()->ShowMain(0);
|
||||
break;
|
||||
}
|
||||
case 1: { // Start
|
||||
start_sequence();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TuiDfuPanel::handle_device_update(const DeviceState& state) {
|
||||
if (state.connected && !state.productType.empty()) {
|
||||
load_sequence_for_product(state.productType, false);
|
||||
}
|
||||
|
||||
if (!state.connected) {
|
||||
if (!m_isEnteringDfu) {
|
||||
reset_sequence_state();
|
||||
GetFrame()->ShowMain(1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_isEnteringDfu && state.mode == DeviceMode::DFU) {
|
||||
m_isEnteringDfu = false;
|
||||
m_dfuSuccess = true;
|
||||
m_waitingForDfuTransition = true;
|
||||
m_dfuSuccessAt = std::chrono::steady_clock::now();
|
||||
m_index = m_sequence.steps.size();
|
||||
m_stepRemaining = -1;
|
||||
|
||||
GetFrame()->SetSelected(1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_isEnteringDfu && state.mode == DeviceMode::Normal) {
|
||||
reset_sequence_state();
|
||||
if (GetFrame()->GetRecoveryPanel()) {
|
||||
GetFrame()->GetRecoveryPanel()->SetStatusText("Hmm... It seems like the device didn't enter recovery mode.");
|
||||
}
|
||||
GetFrame()->ShowRecovery(0);
|
||||
}
|
||||
|
||||
if (m_isEnteringDfu && state.mode == DeviceMode::Recovery) {
|
||||
reset_sequence_state();
|
||||
GetFrame()->ShowMain(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void TuiDfuPanel::reboot() {
|
||||
std::thread([this]() {
|
||||
const auto state = GetFrame()->GetDeviceState();
|
||||
|
||||
if (!state.connected || state.mode != DeviceMode::Recovery)
|
||||
return;
|
||||
|
||||
irecv_client_t client = nullptr;
|
||||
int attempts = 0;
|
||||
const int max_attempts = 8;
|
||||
|
||||
while (attempts < max_attempts) {
|
||||
if (irecv_open_with_ecid(&client, state.ecid) == IRECV_E_SUCCESS)
|
||||
break;
|
||||
attempts++;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||
}
|
||||
|
||||
if (!client) return;
|
||||
irecv_setenv(client, "auto-boot", "true");
|
||||
irecv_saveenv(client);
|
||||
irecv_reboot(client);
|
||||
irecv_close(client);
|
||||
}).detach();
|
||||
}
|
||||
|
||||
#endif // WITH_TUI
|
||||
@@ -0,0 +1,51 @@
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#ifndef TUI_DFU_PANEL_HPP
|
||||
#define TUI_DFU_PANEL_HPP
|
||||
|
||||
#include "TuiPanel.hpp"
|
||||
#include "../sequence.hpp"
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
|
||||
class TuiDfuPanel : public TuiPanel {
|
||||
public:
|
||||
explicit TuiDfuPanel(TuiFrame* frame);
|
||||
~TuiDfuPanel() override = default;
|
||||
|
||||
void on_show() override;
|
||||
void update() override;
|
||||
void draw(int sy, int sx, int selected) override;
|
||||
void handle_enter(int selected, int sy, int sx) override;
|
||||
void handle_device_update(const DeviceState& state) override;
|
||||
bool is_button_enabled(int btn_idx) const override;
|
||||
|
||||
int get_total_items() const override { return 2; }
|
||||
int get_button_count() const override { return 2; }
|
||||
const char** get_buttons() const override;
|
||||
|
||||
private:
|
||||
void load_sequence_for_product(const std::string& product_type, bool reset_state);
|
||||
void reset_sequence_state();
|
||||
void start_sequence();
|
||||
void reboot();
|
||||
void update_sequence_timer();
|
||||
int scale_coord(int value, int input_span, int output_span) const;
|
||||
void draw_sequence_buttons(int button_x, int button_y, int button_width, int button_height) const;
|
||||
void draw_wrapped_steps(int steps_x, int steps_width, int max_lines, int bottom_y) const;
|
||||
|
||||
DfuSequence m_sequence;
|
||||
std::string m_current_product_type;
|
||||
size_t m_index = 0;
|
||||
int m_stepRemaining = -1;
|
||||
int m_actionExecutedIndex = -1;
|
||||
bool m_isEnteringDfu = false;
|
||||
bool m_dfuSuccess = false;
|
||||
bool m_waitingForDfuTransition = false;
|
||||
std::chrono::steady_clock::time_point m_dfuSuccessAt;
|
||||
std::chrono::steady_clock::time_point m_last_tick;
|
||||
};
|
||||
|
||||
#endif // TUI_DFU_PANEL_HPP
|
||||
|
||||
#endif // WITH_TUI
|
||||
@@ -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
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#ifndef TUI_EXPLOIT_PANEL_HPP
|
||||
#define TUI_EXPLOIT_PANEL_HPP
|
||||
|
||||
#include "TuiPanel.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
class TuiExploitPanel : public TuiPanel {
|
||||
public:
|
||||
explicit TuiExploitPanel(TuiFrame* frame);
|
||||
~TuiExploitPanel() override = default;
|
||||
|
||||
void draw(int sy, int sx, int selected) override;
|
||||
void handle_enter(int selected, int sy, int sx) override;
|
||||
void handle_device_update(const DeviceState& state) override;
|
||||
bool is_button_enabled(int btn_idx) const override;
|
||||
void on_show() override;
|
||||
|
||||
int get_total_items() const override { return 1; }
|
||||
int get_button_count() const override { return 1; }
|
||||
const char** get_buttons() const override;
|
||||
private:
|
||||
void exploit();
|
||||
void set_status(const std::string& status, int progress);
|
||||
|
||||
std::mutex m_status_mutex;
|
||||
std::string m_status_text;
|
||||
int m_progress = 0;
|
||||
std::atomic<bool> m_is_exploiting = false;
|
||||
};
|
||||
|
||||
#endif // TUI_EXPLOIT_PANEL_HPP
|
||||
|
||||
#endif // WITH_TUI
|
||||
@@ -0,0 +1,227 @@
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#include "TuiMainPanel.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <ncurses.h>
|
||||
|
||||
#include "Tui.hpp"
|
||||
#include "TuiText.hpp"
|
||||
#include "../event.hpp"
|
||||
#include "../globals.h"
|
||||
#include "../paleinfo.h"
|
||||
|
||||
static const char *buttons[] = { "Options", "Start", "Quit" };
|
||||
static constexpr int kMainContentX = 2;
|
||||
static constexpr int kMainContentWidth = 60;
|
||||
static constexpr int kMainSeparatorX = 1;
|
||||
static constexpr int kMainSeparatorWidth = 63;
|
||||
|
||||
static std::string get_device_title(const DeviceState& state) {
|
||||
if (!state.connected) {
|
||||
if (state.connectedDeviceCount > 1) {
|
||||
return "Multiple devices detected";
|
||||
}
|
||||
return "No device connected";
|
||||
}
|
||||
|
||||
const std::string product = state.productType.empty() ? "Unknown" : state.productType;
|
||||
if (state.mode == DeviceMode::DFU) {
|
||||
return "DFU Mode device";
|
||||
}
|
||||
return product;
|
||||
}
|
||||
|
||||
static std::string get_device_subtitle(const DeviceState& state) {
|
||||
if (!state.connected) {
|
||||
if (state.connectedDeviceCount > 1) {
|
||||
return std::to_string(state.connectedDeviceCount) +
|
||||
"USB devices connected. Disconnect extras and keep only one device attached.";
|
||||
}
|
||||
return "Please connect a device to get started. Ensure version range is 15.0+";
|
||||
}
|
||||
|
||||
const std::string version = state.productVersion.empty() ? "Unknown" : state.productVersion;
|
||||
const std::string ecid = state.ecid != 0 ? ("ECID: " + std::to_string(state.ecid)) : "ECID: Unknown";
|
||||
|
||||
switch (state.mode) {
|
||||
case DeviceMode::Normal:
|
||||
if (state.isSupported) {
|
||||
return "Connected in normal mode * iOS " + version + "\n" + ecid;
|
||||
}
|
||||
return "Not supported * iOS " + version + "\n" + ecid;
|
||||
case DeviceMode::Recovery:
|
||||
if (state.isSupported) {
|
||||
return "Connected in recovery mode\n" + ecid;
|
||||
}
|
||||
return "Not supported\n" + ecid;
|
||||
case DeviceMode::DFU:
|
||||
return "Sorry, jailbreaking in DFU mode is not supported.";
|
||||
case DeviceMode::None:
|
||||
return "Please connect a device to get started. Ensure version range is 15.0+";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
TuiMainPanel::TuiMainPanel(TuiFrame* frame)
|
||||
: TuiPanel(frame) {}
|
||||
|
||||
const char** TuiMainPanel::get_buttons() const {
|
||||
return buttons;
|
||||
}
|
||||
|
||||
void TuiMainPanel::draw(int sy, int sx, int selected) {
|
||||
const DeviceState state = GetFrame()->GetDeviceState();
|
||||
const int content_x = sx + kMainContentX;
|
||||
int y = sy + 2;
|
||||
const int bottom_y = sy + 22;
|
||||
const int content_bottom = bottom_y - 1;
|
||||
|
||||
(void)selected;
|
||||
|
||||
const std::string title = get_device_title(state);
|
||||
const std::string subtitle = get_device_subtitle(state);
|
||||
|
||||
attron(A_BOLD);
|
||||
mvprintw(y, content_x, "%.*s", kMainContentWidth, title.c_str());
|
||||
attroff(A_BOLD);
|
||||
++y;
|
||||
|
||||
if (has_colors()) {
|
||||
attron(COLOR_PAIR(1));
|
||||
}
|
||||
y += tui_text::draw_wrapped_text(y, content_x, kMainContentWidth, subtitle, 2);
|
||||
if (has_colors()) {
|
||||
attroff(COLOR_PAIR(1));
|
||||
}
|
||||
++y;
|
||||
|
||||
if (y <= content_bottom) {
|
||||
mvhline(y, sx + kMainSeparatorX, ACS_HLINE, kMainSeparatorWidth);
|
||||
y += 2;
|
||||
}
|
||||
|
||||
const std::string made_by =
|
||||
"Made by: asdfugil, claration, kok3shidoll, mineek, plooshi, staturnz";
|
||||
const std::string thanks_to =
|
||||
"Thanks to: itsnebulalol, llsc12, lrdsnow, dedbeddedbed, kirb, ehilwyma, "
|
||||
"opa334, 0x7ff, alfiecg24, sneko, sbingner, nikias, "
|
||||
"tihmstar, Checkra1n (Siguza, axi0mx, littlelailo et al.), Procursus "
|
||||
"(Hayden Seay, Cameron Katri, Keto et al.)";
|
||||
|
||||
if (y <= content_bottom) {
|
||||
y += tui_text::draw_wrapped_text(y, content_x, kMainContentWidth, made_by, 2);
|
||||
}
|
||||
|
||||
if (y <= content_bottom) {
|
||||
++y;
|
||||
}
|
||||
|
||||
if (y <= content_bottom) {
|
||||
y += tui_text::draw_wrapped_text(y, content_x, kMainContentWidth, thanks_to, 4);
|
||||
}
|
||||
|
||||
if (y <= content_bottom) {
|
||||
++y;
|
||||
}
|
||||
|
||||
if (y <= content_bottom) {
|
||||
mvhline(y, sx + kMainSeparatorX, ACS_HLINE, kMainSeparatorWidth);
|
||||
y += 1;
|
||||
}
|
||||
|
||||
if (y <= content_bottom) {
|
||||
mvprintw(y, content_x, "Twitter: @palera1n");
|
||||
++y;
|
||||
}
|
||||
if (y <= content_bottom) {
|
||||
mvprintw(y, content_x, "Website: https://palera.in");
|
||||
++y;
|
||||
}
|
||||
|
||||
if (y <= content_bottom) {
|
||||
attron(A_BOLD);
|
||||
y += tui_text::draw_wrapped_text(
|
||||
y,
|
||||
content_x,
|
||||
kMainContentWidth,
|
||||
"NOTE: Please ensure you've made a backup of your device before proceeding.",
|
||||
2
|
||||
);
|
||||
attroff(A_BOLD);
|
||||
}
|
||||
|
||||
if (y <= content_bottom) {
|
||||
mvhline(y, sx + kMainSeparatorX, ACS_HLINE, kMainSeparatorWidth);
|
||||
y += 2;
|
||||
}
|
||||
|
||||
const bool quick_mode_enabled = (palerain_flags & palerain_option_quick) != 0;
|
||||
const std::string quick_mode_prefix = std::string("[") + (quick_mode_enabled ? "x" : " ") + "] ";
|
||||
const std::string quick_mode_label = "Enable Quick Mode";
|
||||
const int quick_mode_total_width = static_cast<int>(quick_mode_prefix.size() + quick_mode_label.size());
|
||||
const int quick_mode_x = content_x + std::max(0, kMainContentWidth - quick_mode_total_width);
|
||||
|
||||
attron(A_DIM);
|
||||
mvprintw(bottom_y, content_x, "%.*s", kMainContentWidth, "Made with <3 by C (claration)");
|
||||
attroff(A_DIM);
|
||||
|
||||
mvprintw(bottom_y, quick_mode_x, "%s", quick_mode_prefix.c_str());
|
||||
|
||||
if (selected == 0) {
|
||||
attron(A_REVERSE);
|
||||
}
|
||||
mvprintw(bottom_y, quick_mode_x + static_cast<int>(quick_mode_prefix.size()), "%s", quick_mode_label.c_str());
|
||||
if (selected == 0) {
|
||||
attroff(A_REVERSE);
|
||||
}
|
||||
}
|
||||
|
||||
void TuiMainPanel::handle_enter(int selected, int sy, int sx) {
|
||||
switch(selected) {
|
||||
case 0:
|
||||
palerain_flags ^= palerain_option_quick;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
GetFrame()->ShowSettings(5);
|
||||
break;
|
||||
|
||||
case 2: {
|
||||
DeviceState state = GetFrame()->GetDeviceState();
|
||||
bool can_start = (state.connected && state.isSupported && state.mode != DeviceMode::DFU);
|
||||
|
||||
if (can_start) {
|
||||
if (state.mode == DeviceMode::Recovery) {
|
||||
GetFrame()->ShowDfu(1);
|
||||
} else {
|
||||
GetFrame()->ShowRecovery(1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 3:
|
||||
GetFrame()->Stop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void TuiMainPanel::handle_device_update(const DeviceState& state) {}
|
||||
|
||||
bool TuiMainPanel::is_button_enabled(int btn_idx) const {
|
||||
DeviceState state = GetFrame()->GetDeviceState();
|
||||
|
||||
switch(btn_idx) {
|
||||
case 0: return true;
|
||||
case 1: return true;
|
||||
case 2: return (state.connected && state.isSupported && state.mode != DeviceMode::DFU);
|
||||
case 3: return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif // WITH_TUI
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#ifndef TUI_MAIN_PANEL_HPP
|
||||
#define TUI_MAIN_PANEL_HPP
|
||||
|
||||
#include "TuiPanel.hpp"
|
||||
|
||||
class TuiMainPanel : public TuiPanel {
|
||||
public:
|
||||
explicit TuiMainPanel(TuiFrame* frame);
|
||||
~TuiMainPanel() override = default;
|
||||
|
||||
void draw(int sy, int sx, int selected) override;
|
||||
void handle_enter(int selected, int sy, int sx) override;
|
||||
void handle_device_update(const DeviceState& state) override;
|
||||
bool is_button_enabled(int btn_idx) const override;
|
||||
|
||||
int get_total_items() const override { return 4; }
|
||||
int get_button_count() const override { return 3; }
|
||||
const char** get_buttons() const override;
|
||||
};
|
||||
|
||||
#endif // TUI_MAIN_PANEL_HPP
|
||||
|
||||
#endif // WITH_TUI
|
||||
@@ -0,0 +1,49 @@
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#ifndef TUI_PANEL_HPP
|
||||
#define TUI_PANEL_HPP
|
||||
|
||||
#include <ncurses.h>
|
||||
|
||||
#include "../event.hpp"
|
||||
|
||||
class TuiFrame;
|
||||
|
||||
enum PanelState {
|
||||
PANEL_MAIN = 0,
|
||||
PANEL_OPTIONS,
|
||||
PANEL_START_1,
|
||||
PANEL_START_2,
|
||||
PANEL_FINAL,
|
||||
PANEL_COUNT
|
||||
};
|
||||
|
||||
class TuiPanel {
|
||||
public:
|
||||
explicit TuiPanel(TuiFrame* frame)
|
||||
: m_frame(frame) {}
|
||||
|
||||
virtual ~TuiPanel() = default;
|
||||
|
||||
virtual void on_show() {}
|
||||
virtual void update() {}
|
||||
|
||||
virtual void draw(int sy, int sx, int selected) = 0;
|
||||
virtual void handle_enter(int selected, int sy, int sx) = 0;
|
||||
virtual void handle_device_update(const DeviceState& state) = 0;
|
||||
virtual bool is_button_enabled(int btn_idx) const { return true; }
|
||||
|
||||
virtual int get_total_items() const = 0;
|
||||
virtual int get_button_count() const = 0;
|
||||
virtual const char** get_buttons() const = 0;
|
||||
protected:
|
||||
TuiFrame* GetFrame() const {
|
||||
return m_frame;
|
||||
}
|
||||
private:
|
||||
TuiFrame* m_frame = nullptr;
|
||||
};
|
||||
|
||||
#endif // TUI_PANEL_HPP
|
||||
|
||||
#endif // WITH_TUI
|
||||
@@ -0,0 +1,139 @@
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#include "TuiRecoveryPanel.hpp"
|
||||
|
||||
#include <ncurses.h>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
#include "Tui.hpp"
|
||||
#include "TuiText.hpp"
|
||||
#include "../globals.h"
|
||||
#include "../paleinfo.h"
|
||||
|
||||
#include <libimobiledevice/libimobiledevice.h>
|
||||
#include <libimobiledevice/lockdown.h>
|
||||
|
||||
static const char *buttons[] = { "Back", "Next" };
|
||||
static constexpr int kRecoveryContentX = 2;
|
||||
static constexpr int kRecoveryContentWidth = 60;
|
||||
|
||||
TuiRecoveryPanel::TuiRecoveryPanel(TuiFrame* frame)
|
||||
: TuiPanel(frame), m_status_text(""), m_is_entering_recovery(false), m_buttons_disabled(false) {}
|
||||
|
||||
void TuiRecoveryPanel::SetStatusText(const std::string& text) {
|
||||
m_status_text = text;
|
||||
m_preserve_status_on_show = true;
|
||||
}
|
||||
|
||||
const char** TuiRecoveryPanel::get_buttons() const {
|
||||
return buttons;
|
||||
}
|
||||
|
||||
bool TuiRecoveryPanel::is_button_enabled(int btn_idx) const {
|
||||
return !m_buttons_disabled;
|
||||
}
|
||||
|
||||
void TuiRecoveryPanel::on_show() {
|
||||
if (!m_preserve_status_on_show) {
|
||||
m_status_text = "";
|
||||
}
|
||||
m_preserve_status_on_show = false;
|
||||
m_is_entering_recovery = false;
|
||||
m_buttons_disabled = false;
|
||||
GetFrame()->SetSelected(1);
|
||||
|
||||
if (palerain_flags & palerain_option_quick) {
|
||||
m_status_text = "Entering recovery mode...";
|
||||
m_buttons_disabled = true;
|
||||
enter_recovery_mode();
|
||||
}
|
||||
}
|
||||
|
||||
void TuiRecoveryPanel::draw(int sy, int sx, int selected) {
|
||||
const int content_x = sx + kRecoveryContentX;
|
||||
const std::string intro = "The device needs to be put into DFU mode to apply the jailbreak. This is a manual process and we will guide you through it.";
|
||||
tui_text::draw_wrapped_text(sy + 2, content_x, kRecoveryContentWidth, intro, 6);
|
||||
|
||||
if (!m_status_text.empty()) {
|
||||
tui_text::draw_wrapped_text(sy + 9, content_x, kRecoveryContentWidth, m_status_text, 3);
|
||||
}
|
||||
}
|
||||
|
||||
void TuiRecoveryPanel::handle_enter(int selected, int sy, int sx) {
|
||||
if (m_buttons_disabled) return;
|
||||
|
||||
switch(selected) {
|
||||
case 0:
|
||||
GetFrame()->ShowMain(1);
|
||||
break;
|
||||
case 1:
|
||||
m_status_text = "Entering recovery mode...";
|
||||
m_buttons_disabled = true;
|
||||
enter_recovery_mode();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void TuiRecoveryPanel::handle_device_update(const DeviceState& state) {
|
||||
if (m_is_entering_recovery && state.mode == DeviceMode::Recovery) {
|
||||
m_status_text = "Device is now in recovery mode.";
|
||||
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||
m_status_text = "";
|
||||
m_is_entering_recovery = false;
|
||||
m_buttons_disabled = false;
|
||||
GetFrame()->ShowDfu(1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_is_entering_recovery && state.connected && state.mode == DeviceMode::Normal) {
|
||||
m_is_entering_recovery = false;
|
||||
m_buttons_disabled = false;
|
||||
m_status_text = "Hmm... It seems like the device didn't enter recovery mode. Please try again.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_is_entering_recovery && !state.connected) {
|
||||
m_buttons_disabled = false;
|
||||
GetFrame()->ShowMain(1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void TuiRecoveryPanel::enter_recovery_mode() {
|
||||
m_is_entering_recovery = true;
|
||||
m_buttons_disabled = true;
|
||||
|
||||
std::thread([this]() {
|
||||
DeviceState state = GetFrame()->GetDeviceState();
|
||||
if (!state.connected) {
|
||||
m_is_entering_recovery = false;
|
||||
m_buttons_disabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
idevice_t device = nullptr;
|
||||
lockdownd_client_t client = nullptr;
|
||||
|
||||
if (idevice_new(&device, state.udid.c_str()) != IDEVICE_E_SUCCESS) {
|
||||
m_is_entering_recovery = false;
|
||||
m_buttons_disabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (lockdownd_client_new_with_handshake(device, &client, "palera1n") != LOCKDOWN_E_SUCCESS) {
|
||||
idevice_free(device);
|
||||
m_is_entering_recovery = false;
|
||||
m_buttons_disabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
lockdownd_enter_recovery(client);
|
||||
|
||||
lockdownd_client_free(client);
|
||||
idevice_free(device);
|
||||
}).detach();
|
||||
}
|
||||
|
||||
#endif // WITH_TUI
|
||||
@@ -0,0 +1,39 @@
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#ifndef TUI_RECOVERY_PANEL_HPP
|
||||
#define TUI_RECOVERY_PANEL_HPP
|
||||
|
||||
#include "TuiPanel.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <atomic>
|
||||
|
||||
class TuiRecoveryPanel : public TuiPanel {
|
||||
public:
|
||||
explicit TuiRecoveryPanel(TuiFrame* frame);
|
||||
~TuiRecoveryPanel() override = default;
|
||||
|
||||
void SetStatusText(const std::string& text);
|
||||
|
||||
void on_show() override;
|
||||
void draw(int sy, int sx, int selected) override;
|
||||
void handle_enter(int selected, int sy, int sx) override;
|
||||
void handle_device_update(const DeviceState& state) override;
|
||||
bool is_button_enabled(int btn_idx) const override;
|
||||
|
||||
int get_total_items() const override { return 2; }
|
||||
int get_button_count() const override { return 2; }
|
||||
const char** get_buttons() const override;
|
||||
|
||||
private:
|
||||
void enter_recovery_mode();
|
||||
|
||||
std::string m_status_text;
|
||||
bool m_preserve_status_on_show = false;
|
||||
std::atomic<bool> m_is_entering_recovery;
|
||||
bool m_buttons_disabled;
|
||||
};
|
||||
|
||||
#endif // TUI_RECOVERY_PANEL_HPP
|
||||
|
||||
#endif // WITH_TUI
|
||||
@@ -0,0 +1,190 @@
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#include "TuiSettingsPanel.hpp"
|
||||
|
||||
#include <ncurses.h>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include "Tui.hpp"
|
||||
#include "TuiText.hpp"
|
||||
|
||||
#include "../globals.h"
|
||||
#include "../paleinfo.h"
|
||||
|
||||
static const char *buttons[] = { "Back" };
|
||||
static constexpr int kSettingsContentX = 2;
|
||||
static constexpr int kSettingsContentWidth = 60;
|
||||
static constexpr int kSettingsBootArgsWidth = 56;
|
||||
|
||||
TuiSettingsPanel::TuiSettingsPanel(TuiFrame* frame)
|
||||
: TuiPanel(frame) {}
|
||||
|
||||
const char** TuiSettingsPanel::get_buttons() const {
|
||||
return buttons;
|
||||
}
|
||||
|
||||
void TuiSettingsPanel::draw(int sy, int sx, int selected) {
|
||||
const int content_x = sx + kSettingsContentX;
|
||||
const bool is_rootful = (palerain_flags & palerain_option_rootful) != 0;
|
||||
const int setup_selection = (palerain_flags & palerain_option_setup_rootful) ? 1
|
||||
: (palerain_flags & palerain_option_setup_partial_root) ? 2 : 0;
|
||||
|
||||
tui_text::draw_wrapped_text(
|
||||
sy + 2,
|
||||
content_x,
|
||||
kSettingsContentWidth,
|
||||
"You may set the following options. If you don't know what they mean you'll probably have no reason to set them.",
|
||||
2
|
||||
);
|
||||
|
||||
int states[] = {
|
||||
!!(palerain_flags & palerain_option_safemode),
|
||||
!!(palerain_flags & palerain_option_verbose_boot),
|
||||
!!(palerain_flags & palerain_option_force_revert),
|
||||
!!(palerain_flags & palerain_option_flower_chain)
|
||||
};
|
||||
|
||||
const char *labels[] = {
|
||||
"Safe Mode",
|
||||
"Verbose Boot",
|
||||
"Restore System",
|
||||
"Dark Blockchain"
|
||||
};
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
mvprintw(sy + 6 + i, content_x, "[%c] ", states[i] ? 'x' : ' ');
|
||||
if (selected == i) attron(A_REVERSE);
|
||||
printw("%s", labels[i]);
|
||||
if (selected == i) attroff(A_REVERSE);
|
||||
}
|
||||
|
||||
mvprintw(sy + 8, sx + 6, "Boot Arguments:");
|
||||
if (selected == 2) attron(A_REVERSE);
|
||||
mvprintw(sy + 9, sx + 6, "%-56s", boot_args);
|
||||
if (selected == 2) attroff(A_REVERSE);
|
||||
|
||||
for (int i = 2; i < 4; i++) {
|
||||
mvprintw(sy + 8 + i, content_x, "[%c] ", states[i] ? 'x' : ' ');
|
||||
if (selected == i + 1) attron(A_REVERSE);
|
||||
printw("%s", labels[i]);
|
||||
if (selected == i + 1) attroff(A_REVERSE);
|
||||
}
|
||||
|
||||
mvprintw(sy + 13, content_x, "Jailbreak Type:");
|
||||
if (selected == 5) attron(A_REVERSE);
|
||||
mvprintw(sy + 14, content_x + 2, "(%c) Rootless", is_rootful ? ' ' : 'x');
|
||||
if (selected == 5) attroff(A_REVERSE);
|
||||
|
||||
if (selected == 6) attron(A_REVERSE);
|
||||
mvprintw(sy + 15, content_x + 2, "(%c) Rootful", is_rootful ? 'x' : ' ');
|
||||
if (selected == 6) attroff(A_REVERSE);
|
||||
|
||||
mvprintw(sy + 17, content_x, "Rootful Options:");
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
const int item_idx = 7 + i;
|
||||
const int row = sy + 18 + i;
|
||||
const bool active = is_rootful;
|
||||
const bool is_selected = selected == item_idx;
|
||||
const bool checked = setup_selection == i;
|
||||
|
||||
if (!active) attron(A_DIM);
|
||||
if (is_selected) attron(A_REVERSE);
|
||||
|
||||
const char* option_label = i == 0 ? "Boot" : (i == 1 ? "Create FakeFS" : "Create BindFS");
|
||||
mvprintw(row, content_x + 2, "(%c) %s", checked ? 'x' : ' ', option_label);
|
||||
|
||||
if (is_selected) attroff(A_REVERSE);
|
||||
if (!active) attroff(A_DIM);
|
||||
}
|
||||
}
|
||||
|
||||
void TuiSettingsPanel::edit_boot_args(int start_y, int start_x) {
|
||||
curs_set(1);
|
||||
timeout(-1);
|
||||
|
||||
int field_y = start_y + 9;
|
||||
int field_x = start_x + 6;
|
||||
int max_len = kSettingsBootArgsWidth;
|
||||
|
||||
char temp_buf[128];
|
||||
std::snprintf(temp_buf, sizeof(temp_buf), "%s", boot_args);
|
||||
int len = static_cast<int>(std::strlen(temp_buf));
|
||||
|
||||
while (1) {
|
||||
mvprintw(field_y, field_x, "%-56s", "");
|
||||
mvprintw(field_y, field_x, "%s", temp_buf);
|
||||
move(field_y, field_x + len);
|
||||
refresh();
|
||||
|
||||
int input_ch = getch();
|
||||
|
||||
if (input_ch == KEY_ENTER || input_ch == 10 || input_ch == 13) {
|
||||
break;
|
||||
} else if (input_ch == KEY_BACKSPACE || input_ch == 127 || input_ch == '\b') {
|
||||
if (len > 0) temp_buf[--len] = '\0';
|
||||
} else if (input_ch >= 32 && input_ch <= 126) {
|
||||
if (len < max_len) {
|
||||
temp_buf[len++] = static_cast<char>(input_ch);
|
||||
temp_buf[len] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::snprintf(boot_args, sizeof(boot_args), "%s", temp_buf);
|
||||
curs_set(0);
|
||||
timeout(100);
|
||||
}
|
||||
|
||||
void TuiSettingsPanel::handle_enter(int selected, int sy, int sx) {
|
||||
switch(selected) {
|
||||
case 0: palerain_flags ^= palerain_option_safemode; break;
|
||||
case 1: palerain_flags ^= palerain_option_verbose_boot; break;
|
||||
case 2: edit_boot_args(sy, sx); break;
|
||||
case 3: palerain_flags ^= palerain_option_force_revert; break;
|
||||
case 4: palerain_flags ^= palerain_option_flower_chain; break;
|
||||
case 5:
|
||||
palerain_flags &= ~(palerain_option_rootless | palerain_option_rootful);
|
||||
palerain_flags &= ~(palerain_option_setup_rootful | palerain_option_setup_partial_root);
|
||||
palerain_flags |= palerain_option_rootless;
|
||||
break;
|
||||
case 6:
|
||||
palerain_flags &= ~(palerain_option_rootless | palerain_option_rootful);
|
||||
palerain_flags &= ~(palerain_option_setup_rootful | palerain_option_setup_partial_root);
|
||||
palerain_flags |= palerain_option_rootful;
|
||||
break;
|
||||
case 7:
|
||||
if (palerain_flags & palerain_option_rootful) {
|
||||
palerain_flags &= ~(palerain_option_setup_rootful | palerain_option_setup_partial_root);
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
if (palerain_flags & palerain_option_rootful) {
|
||||
palerain_flags &= ~(palerain_option_setup_rootful | palerain_option_setup_partial_root);
|
||||
palerain_flags |= palerain_option_setup_rootful;
|
||||
}
|
||||
break;
|
||||
case 9:
|
||||
if (palerain_flags & palerain_option_rootful) {
|
||||
palerain_flags &= ~(palerain_option_setup_rootful | palerain_option_setup_partial_root);
|
||||
palerain_flags |= palerain_option_setup_partial_root;
|
||||
}
|
||||
break;
|
||||
case 10: GetFrame()->ShowMain(0); break;
|
||||
}
|
||||
}
|
||||
|
||||
void TuiSettingsPanel::handle_device_update(const DeviceState& state) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool TuiSettingsPanel::is_button_enabled(int btn_idx) const {
|
||||
if (btn_idx >= 7 && btn_idx <= 9) {
|
||||
return (palerain_flags & palerain_option_rootful) != 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // WITH_TUI
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#ifndef TUI_SETTINGS_PANEL_HPP
|
||||
#define TUI_SETTINGS_PANEL_HPP
|
||||
|
||||
#include "TuiPanel.hpp"
|
||||
|
||||
class TuiSettingsPanel : public TuiPanel {
|
||||
public:
|
||||
explicit TuiSettingsPanel(TuiFrame* frame);
|
||||
~TuiSettingsPanel() override = default;
|
||||
|
||||
void draw(int sy, int sx, int selected) override;
|
||||
void handle_enter(int selected, int sy, int sx) override;
|
||||
void handle_device_update(const DeviceState& state) override;
|
||||
bool is_button_enabled(int btn_idx) const override;
|
||||
|
||||
int get_total_items() const override { return 11; }
|
||||
int get_button_count() const override { return 1; }
|
||||
const char** get_buttons() const override;
|
||||
|
||||
private:
|
||||
void edit_boot_args(int start_y, int start_x);
|
||||
};
|
||||
|
||||
#endif // TUI_SETTINGS_PANEL_HPP
|
||||
|
||||
#endif // WITH_TUI
|
||||
@@ -0,0 +1,83 @@
|
||||
#ifdef WITH_TUI
|
||||
|
||||
#ifndef TUI_TEXT_HPP
|
||||
#define TUI_TEXT_HPP
|
||||
|
||||
#include <ncurses.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace tui_text {
|
||||
|
||||
inline void append_wrapped_lines(const std::string& text, int width, std::vector<std::string>& out) {
|
||||
if (width <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t start = 0;
|
||||
while (start <= text.size()) {
|
||||
size_t end = text.find('\n', start);
|
||||
if (end == std::string::npos) {
|
||||
end = text.size();
|
||||
}
|
||||
|
||||
std::string paragraph = text.substr(start, end - start);
|
||||
size_t cursor = 0;
|
||||
|
||||
if (paragraph.empty()) {
|
||||
out.push_back("");
|
||||
}
|
||||
|
||||
while (cursor < paragraph.size()) {
|
||||
size_t remaining = paragraph.size() - cursor;
|
||||
if (remaining <= static_cast<size_t>(width)) {
|
||||
out.push_back(paragraph.substr(cursor));
|
||||
break;
|
||||
}
|
||||
|
||||
size_t cut = cursor + static_cast<size_t>(width);
|
||||
size_t split = paragraph.rfind(' ', cut);
|
||||
if (split == std::string::npos || split <= cursor) {
|
||||
split = cut;
|
||||
}
|
||||
|
||||
out.push_back(paragraph.substr(cursor, split - cursor));
|
||||
cursor = split;
|
||||
while (cursor < paragraph.size() && paragraph[cursor] == ' ') {
|
||||
++cursor;
|
||||
}
|
||||
}
|
||||
|
||||
if (end == text.size()) {
|
||||
break;
|
||||
}
|
||||
start = end + 1;
|
||||
}
|
||||
}
|
||||
|
||||
inline int draw_wrapped_text(int y, int x, int width, const std::string& text, int max_lines = 0) {
|
||||
if (width <= 0 || text.empty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<std::string> lines;
|
||||
append_wrapped_lines(text, width, lines);
|
||||
|
||||
int rendered = 0;
|
||||
for (const auto& line : lines) {
|
||||
if (max_lines > 0 && rendered >= max_lines) {
|
||||
break;
|
||||
}
|
||||
mvprintw(y + rendered, x, "%.*s", width, line.c_str());
|
||||
++rendered;
|
||||
}
|
||||
|
||||
return rendered;
|
||||
}
|
||||
|
||||
} // namespace tui_text
|
||||
|
||||
#endif // TUI_TEXT_HPP
|
||||
|
||||
#endif // WITH_TUI
|
||||
@@ -48,6 +48,9 @@ static const char* get_timestamp(void) {
|
||||
}
|
||||
|
||||
void log_write(log_level_t level, const char *fmt, ...) {
|
||||
if (palerain_flags & palerain_option_tui)
|
||||
return;
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
|
||||
|
||||
+8
-28
@@ -6,17 +6,14 @@
|
||||
#include "../utils.h"
|
||||
#include "../event.hpp"
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include <libimobiledevice/libimobiledevice.h>
|
||||
#include <libimobiledevice/lockdown.h>
|
||||
#include <libirecovery.h>
|
||||
|
||||
wxDEFINE_EVENT(EVT_DEVICE_STATE_UPDATE, wxCommandEvent);
|
||||
|
||||
void send_device_state(MainFrame* frame, const DeviceState& state)
|
||||
{
|
||||
if (!wxTheApp) return;
|
||||
|
||||
wxTheApp->CallAfter([frame, state]() {
|
||||
if (!frame) return;
|
||||
auto* evt = new wxCommandEvent(EVT_DEVICE_STATE_UPDATE);
|
||||
evt->SetClientData(new DeviceState(state));
|
||||
wxPostEvent(frame, *evt);
|
||||
@@ -28,7 +25,7 @@ MainFrame::MainFrame()
|
||||
wxID_ANY,
|
||||
"palera1n - Version beta " + wxString(PALERAIN_VERSION),
|
||||
wxDefaultPosition,
|
||||
wxSize(480, 360),
|
||||
wxSize(480, 348),
|
||||
wxDEFAULT_FRAME_STYLE & ~(wxMAXIMIZE_BOX | wxRESIZE_BORDER))
|
||||
{
|
||||
auto* root = new wxBoxSizer(wxVERTICAL);
|
||||
@@ -61,27 +58,10 @@ MainFrame::MainFrame()
|
||||
|
||||
ShowMain();
|
||||
|
||||
std::thread([this]()
|
||||
{
|
||||
idevice_event_subscribe(normal_device_event_cb, this);
|
||||
|
||||
while (true)
|
||||
sleep_ms(1000);
|
||||
}).detach();
|
||||
|
||||
std::thread([this]()
|
||||
{
|
||||
static irecv_device_event_context_t ctx = nullptr;
|
||||
|
||||
irecv_device_event_subscribe(
|
||||
&ctx,
|
||||
recovery_device_event_cb,
|
||||
this
|
||||
);
|
||||
|
||||
while (true)
|
||||
sleep_ms(1000);
|
||||
}).detach();
|
||||
register_device_state_callback([this](const DeviceState& state) {
|
||||
send_device_state(this, state);
|
||||
});
|
||||
ensure_device_event_system_started();
|
||||
}
|
||||
|
||||
void MainFrame::ShowMain()
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
#ifdef WITH_GUI
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include "../state.hpp"
|
||||
#include "../event.hpp"
|
||||
|
||||
#include "MainPanel.hpp"
|
||||
#include "SettingsPanel.hpp"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#define DEVICEPANEL_H
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include "../state.hpp"
|
||||
#include "../event.hpp"
|
||||
|
||||
class MainFrame;
|
||||
class DevicePanel : public wxPanel
|
||||
|
||||
+48
-22
@@ -5,7 +5,7 @@
|
||||
#include "AppFrame.hpp"
|
||||
#include "DevicePanel.hpp"
|
||||
|
||||
#include "../state.hpp"
|
||||
#include "../event.hpp"
|
||||
#include "../sequence.hpp"
|
||||
#include "../utils.h"
|
||||
#include "../globals.h"
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <wx/mstream.h>
|
||||
#include <wx/image.h>
|
||||
#include <wx/bitmap.h>
|
||||
#include <wx/statline.h>
|
||||
|
||||
#include "../gen/images/atv_std_brd.h"
|
||||
#include "../gen/images/ipad.h"
|
||||
@@ -28,6 +29,7 @@
|
||||
#include "../gen/images/iphonex.h"
|
||||
#include "../gen/images/ipodtouch.h"
|
||||
#include "../gen/images/siriremote.h"
|
||||
#include "../gen/images/logo.h"
|
||||
|
||||
#include <libirecovery.h>
|
||||
|
||||
@@ -35,12 +37,23 @@ DfuPanel::DfuPanel(MainFrame* frame, wxWindow* parent)
|
||||
: DevicePanel(frame, parent)
|
||||
{
|
||||
Bind(wxEVT_SHOW, &DfuPanel::OnShow, this);
|
||||
auto* root = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
m_headerText = new wxStaticText(this, wxID_ANY, "Time to put the device into DFU mode. Locate the buttons as marked below\non your device and check the instructions on the right.");
|
||||
root->Add(m_headerText, 0, wxALL, 10);
|
||||
auto* root = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto* left = new wxBoxSizer(wxVERTICAL);
|
||||
wxMemoryInputStream stream(images_logo_png, images_logo_png_len);
|
||||
wxImage img(stream, wxBITMAP_TYPE_PNG);
|
||||
wxBitmap bmp(img);
|
||||
auto* logo = new wxStaticBitmap(this, wxID_ANY, bmp);
|
||||
auto* logoText = new wxStaticText(this, wxID_ANY, "DFU");
|
||||
auto* right = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
auto* contentRow = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_headerText = new wxStaticText(this, wxID_ANY,
|
||||
"Time to put the device into DFU mode.",
|
||||
wxDefaultPosition,
|
||||
wxDefaultSize,
|
||||
wxST_WRAP
|
||||
);
|
||||
left->Add(m_headerText, 0, wxTOP | wxLEFT | wxRIGHT, 10);
|
||||
|
||||
m_devicePanel = new wxPanel(this);
|
||||
m_devicePanel->SetMinSize(wxSize(310, 260));
|
||||
@@ -51,11 +64,18 @@ DfuPanel::DfuPanel(MainFrame* frame, wxWindow* parent)
|
||||
wxBitmap()
|
||||
);
|
||||
|
||||
contentRow->Add(m_devicePanel, 1, wxTOP, 10);
|
||||
m_stepsSizer = new wxBoxSizer(wxVERTICAL);
|
||||
contentRow->Add(m_stepsSizer, 2, wxEXPAND | wxALL, 0);
|
||||
auto* deviceSizer = new wxBoxSizer(wxVERTICAL);
|
||||
deviceSizer->AddStretchSpacer(1);
|
||||
deviceSizer->Add(m_deviceImage, 0, wxALIGN_CENTER);
|
||||
deviceSizer->AddStretchSpacer(1);
|
||||
m_devicePanel->SetSizer(deviceSizer);
|
||||
|
||||
root->Add(contentRow, 1, wxEXPAND);
|
||||
left->Add(m_devicePanel, 1, wxEXPAND | wxTOP, 0);
|
||||
|
||||
left->AddStretchSpacer(1);
|
||||
|
||||
m_stepsSizer = new wxBoxSizer(wxVERTICAL);
|
||||
left->Add(m_stepsSizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
|
||||
|
||||
m_backButton = new wxButton(this, wxID_ANY, "Back");
|
||||
m_startButton = new wxButton(this, wxID_ANY, "Start");
|
||||
@@ -82,18 +102,21 @@ DfuPanel::DfuPanel(MainFrame* frame, wxWindow* parent)
|
||||
|
||||
m_stagnentTimer.Bind(wxEVT_TIMER, [this](wxTimerEvent&)
|
||||
{
|
||||
m_headerText->SetLabel("Time to put the device into DFU mode. Locate the buttons as marked below\non your device and check the instructions on the right.");
|
||||
m_headerText->SetLabel("Time to put the device into DFU mode.");
|
||||
m_backButton->Enable();
|
||||
m_startButton->Enable();
|
||||
GetMainFrame()->ShowExploit();
|
||||
});
|
||||
|
||||
auto* bottomRow = new wxBoxSizer(wxHORIZONTAL);
|
||||
bottomRow->AddStretchSpacer();
|
||||
bottomRow->Add(m_backButton, 0, wxRIGHT | wxLEFT | wxBOTTOM, 12);
|
||||
bottomRow->Add(m_startButton, 0, wxRIGHT | wxLEFT | wxBOTTOM, 12);
|
||||
right->Add(logo, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP | wxLEFT | wxRIGHT, 10);
|
||||
right->Add(logoText, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP, 5);
|
||||
right->AddStretchSpacer();
|
||||
right->Add(m_backButton, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10);
|
||||
right->Add(m_startButton, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10);
|
||||
|
||||
root->Add(bottomRow, 0, wxEXPAND);
|
||||
root->Add(left, 1, wxEXPAND);
|
||||
root->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL), 0, wxEXPAND | wxTOP | wxBOTTOM, 10);
|
||||
root->Add(right, 0, wxEXPAND);
|
||||
|
||||
SetSizer(root);
|
||||
}
|
||||
@@ -143,6 +166,14 @@ void DfuPanel::SetDeviceState(const DeviceState& state)
|
||||
m_startButton->Enable();
|
||||
GetMainFrame()->ShowMain();
|
||||
}
|
||||
|
||||
if (!state.connected)
|
||||
{
|
||||
m_isEnteringDfu = false;
|
||||
m_backButton->Enable();
|
||||
m_startButton->Enable();
|
||||
GetMainFrame()->ShowMain();
|
||||
}
|
||||
}
|
||||
|
||||
void DfuPanel::OnShow(wxShowEvent& event)
|
||||
@@ -254,7 +285,6 @@ void DfuPanel::LoadDevice(const std::string& productType)
|
||||
);
|
||||
|
||||
m_deviceImage->SetBitmap(wxBitmap(img));
|
||||
m_deviceImage->SetPosition(wxPoint(m_sequence.imageOffsetX, 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,7 +320,7 @@ void DfuPanel::LoadDevice(const std::string& productType)
|
||||
|
||||
lbl->SetForegroundColour(i == 0 ? kActive : kInactive);
|
||||
|
||||
m_stepsSizer->Add(lbl, 0, wxBOTTOM, 5);
|
||||
m_stepsSizer->Add(lbl, 0, wxTOP, 5);
|
||||
m_stepLabels.push_back(lbl);
|
||||
}
|
||||
|
||||
@@ -452,14 +482,10 @@ void DfuPanel::Reboot()
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||
}
|
||||
|
||||
if (!client)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!client) return;
|
||||
irecv_setenv(client, "auto-boot", "true");
|
||||
irecv_saveenv(client);
|
||||
irecv_reboot(client);
|
||||
|
||||
irecv_close(client);
|
||||
}).detach();
|
||||
}
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include "DevicePanel.hpp"
|
||||
#include "../state.hpp"
|
||||
#include "../event.hpp"
|
||||
#include "../sequence.hpp"
|
||||
|
||||
class DfuPanel;
|
||||
|
||||
+27
-40
@@ -3,6 +3,10 @@
|
||||
#include "ExploitPanel.hpp"
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/mstream.h>
|
||||
#include <wx/image.h>
|
||||
#include <wx/bitmap.h>
|
||||
#include <wx/statline.h>
|
||||
|
||||
#include <thread>
|
||||
#ifdef __cplusplus
|
||||
@@ -17,42 +21,19 @@ using std::atomic_bool;
|
||||
#include "AppFrame.hpp"
|
||||
#include "../m8/run.h"
|
||||
#include "../utils.h"
|
||||
|
||||
static const char* stage_to_string(int stage)
|
||||
{
|
||||
switch (stage) {
|
||||
case STAGE_SETUP: return "Checking if device is ready";
|
||||
case STAGE_SPRAY: return "Setting up the exploit (this is the heap spray)";
|
||||
case STAGE_PATCH: return "Right before trigger (this is the real bug setup)";
|
||||
case STAGE_YOLODFU: return "Booting PongoOS...";
|
||||
case STAGE_PONGO: return "Booting...";
|
||||
case STAGE_DONE: return "Done.";
|
||||
default: return "Idle.";
|
||||
}
|
||||
}
|
||||
|
||||
static int stage_to_progress(int stage)
|
||||
{
|
||||
switch (stage) {
|
||||
case STAGE_SETUP: return 30;
|
||||
case STAGE_SPRAY: return 40;
|
||||
case STAGE_PATCH: return 50;
|
||||
case STAGE_YOLODFU: return 60;
|
||||
case STAGE_PONGO: return 70;
|
||||
case STAGE_DONE: return 100;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
#include "../gen/images/logo.h"
|
||||
|
||||
ExploitPanel::ExploitPanel(MainFrame* frame, wxWindow* parent)
|
||||
: DevicePanel(frame, parent)
|
||||
{
|
||||
auto* root = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
auto* descriptionText = new wxStaticText(this, wxID_ANY,
|
||||
"Installing jailbreak, this will take a moment. If the device asks for a passcode\n"
|
||||
"please enter it. Do not disconnect the device until finished."
|
||||
);
|
||||
auto* root = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto* left = new wxBoxSizer(wxVERTICAL);
|
||||
wxMemoryInputStream stream(images_logo_png, images_logo_png_len);
|
||||
wxImage img(stream, wxBITMAP_TYPE_PNG);
|
||||
wxBitmap bmp(img);
|
||||
auto* logo = new wxStaticBitmap(this, wxID_ANY, bmp);
|
||||
auto* logoText = new wxStaticText(this, wxID_ANY, "Jailbreak");
|
||||
auto* right = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
m_statusText = new wxStaticText(this, wxID_ANY, stage_to_string(STAGE_RESET));
|
||||
|
||||
@@ -68,16 +49,22 @@ ExploitPanel::ExploitPanel(MainFrame* frame, wxWindow* parent)
|
||||
frame->ShowMain();
|
||||
});
|
||||
|
||||
root->Add(descriptionText, 0, wxALL, 10);
|
||||
root->Add(m_statusText, 0, wxALL, 10);
|
||||
root->Add(m_progressBar, 0, wxALL | wxEXPAND, 20);
|
||||
left->Add(new wxStaticText(this, wxID_ANY, "Installing jailbreak, this will take a moment. If the device asks for a passcode please enter it. Do not disconnect the device until finished.",
|
||||
wxDefaultPosition,
|
||||
wxDefaultSize,
|
||||
wxST_WRAP
|
||||
), 0, wxALL, 10);
|
||||
left->Add(m_statusText, 0, wxALL, 10);
|
||||
left->Add(m_progressBar, 0, wxALL | wxEXPAND, 20);
|
||||
|
||||
root->AddStretchSpacer();
|
||||
auto* bottomRow = new wxBoxSizer(wxHORIZONTAL);
|
||||
bottomRow->AddStretchSpacer();
|
||||
bottomRow->Add(m_backButton, 0, wxRIGHT | wxBOTTOM, 12);
|
||||
right->Add(logo, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP | wxLEFT | wxRIGHT, 10);
|
||||
right->Add(logoText, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP, 5);
|
||||
right->AddStretchSpacer();
|
||||
right->Add(m_backButton, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10);
|
||||
|
||||
root->Add(bottomRow, 0, wxEXPAND);
|
||||
root->Add(left, 1, wxEXPAND);
|
||||
root->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL), 0, wxEXPAND | wxTOP | wxBOTTOM, 10);
|
||||
root->Add(right, 0, wxEXPAND);
|
||||
|
||||
SetSizer(root);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include "DevicePanel.hpp"
|
||||
#include "../state.hpp"
|
||||
#include "../event.hpp"
|
||||
|
||||
class ExploitPanel;
|
||||
class ExploitPanel : public DevicePanel
|
||||
|
||||
+129
-94
@@ -12,20 +12,22 @@
|
||||
#include "AppFrame.hpp"
|
||||
#include "DevicePanel.hpp"
|
||||
|
||||
#include "../state.hpp"
|
||||
#include "../event.hpp"
|
||||
#include "../globals.h"
|
||||
#include "../utils.h"
|
||||
#include "../paleinfo.h"
|
||||
#include "../gen/images/logo.h"
|
||||
|
||||
const char* deviceTextString = "Connect your iPhone, iPod touch, iPad, or AppleTV to begin.";
|
||||
const char* deviceTextString = "No device connected";
|
||||
const char* deviceTextString2 = "Please connect a device to get started.\nEnsure version range is 15.0+";
|
||||
|
||||
MainPanel::MainPanel(MainFrame* frame, wxWindow* parent)
|
||||
: DevicePanel(frame, parent)
|
||||
{
|
||||
auto* root = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
wxInitAllImageHandlers();
|
||||
auto* root = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto* left = new wxBoxSizer(wxVERTICAL);
|
||||
auto* right = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
wxMemoryInputStream stream(images_logo_png, images_logo_png_len);
|
||||
wxImage img(stream, wxBITMAP_TYPE_PNG);
|
||||
@@ -33,101 +35,70 @@ MainPanel::MainPanel(MainFrame* frame, wxWindow* parent)
|
||||
|
||||
auto* logo = new wxStaticBitmap(this, wxID_ANY, bmp);
|
||||
|
||||
auto* titletext = new wxStaticText(this, wxID_ANY, "Welcome to palera1n!");
|
||||
wxFont titlefont = titletext->GetFont();
|
||||
titlefont.SetWeight(wxFONTWEIGHT_BOLD);
|
||||
titletext->SetFont(titlefont);
|
||||
m_deviceTitle = new wxStaticText(this, wxID_ANY, deviceTextString);
|
||||
m_deviceSubtitle = new wxStaticText(this, wxID_ANY, deviceTextString2);
|
||||
|
||||
m_deviceText = new wxStaticText(
|
||||
wxFont titleFont = m_deviceTitle->GetFont();
|
||||
titleFont.SetPointSize(titleFont.GetPointSize() + 6);
|
||||
titleFont.SetWeight(wxFONTWEIGHT_SEMIBOLD);
|
||||
m_deviceTitle->SetFont(titleFont);
|
||||
|
||||
m_deviceSubtitle->SetForegroundColour(wxColour(160, 160, 160));
|
||||
|
||||
auto* credtext = new wxStaticText(
|
||||
this,
|
||||
wxID_ANY,
|
||||
deviceTextString
|
||||
"Made by: asdfugil, claration, kok3shidoll, mineek, plooshi, staturnz\n\n"
|
||||
"Thanks to: itsnebulalol, llsc12, lrdsnow, dedbeddedbed, kirb, ehilwyma, "
|
||||
"opa334, 0x7ff, alfiecg24, sneko, sbingner, nikias, tihmstar, "
|
||||
"Checkra1n (Siguza, axi0mx, littlelailo et al.), Procursus (Hayden Seay, "
|
||||
"Cameron Katri, Keto et al.)",
|
||||
wxDefaultPosition,
|
||||
wxDefaultSize,
|
||||
wxST_WRAP
|
||||
);
|
||||
|
||||
auto* left = new wxBoxSizer(wxVERTICAL);
|
||||
left->Add(titletext, 0, wxTOP | wxLEFT, 10);
|
||||
left->Add(m_deviceText, 0, wxTOP | wxLEFT, 10);
|
||||
auto* twitterLabel = new wxStaticText(this, wxID_ANY, "Twitter:");
|
||||
auto* twitter = new wxStaticText(this, wxID_ANY, "@palera1n");
|
||||
|
||||
auto* row = new wxBoxSizer(wxHORIZONTAL);
|
||||
row->Add(left, 0, wxALIGN_TOP);
|
||||
row->AddStretchSpacer();
|
||||
row->Add(logo, 0, wxTOP | wxRIGHT, 10);
|
||||
auto* websiteLabel = new wxStaticText(this, wxID_ANY, "Website:");
|
||||
auto* website = new wxStaticText(this, wxID_ANY, "https://palera.in");
|
||||
|
||||
root->Add(row, 0, wxEXPAND);
|
||||
twitter->SetForegroundColour(wxColour(0, 120, 215));
|
||||
website->SetForegroundColour(wxColour(0, 120, 215));
|
||||
|
||||
root->Add(new wxStaticLine(this), 0, wxEXPAND | wxALL, 10);
|
||||
twitter->SetCursor(wxCursor(wxCURSOR_HAND));
|
||||
website->SetCursor(wxCursor(wxCURSOR_HAND));
|
||||
|
||||
auto* credtext = new wxStaticText(this, wxID_ANY,
|
||||
"Made by: asdfugil, kok3shidoll, C, mineek, nekohaxx, plooshi, staturnz\n\n"
|
||||
"Thanks to: llsc12, itsnebulalol, lrdsnow, kirb, ehilwyma opa334, 0x7ff,\n"
|
||||
"sbingner, nikias (libimobiledevice), Checkra1n (Siguza, axi0mx, littlelailo\n"
|
||||
"et al.), Procursus (Hayden Seay, Cameron Katri, Keto et al.)"
|
||||
);
|
||||
|
||||
auto* credrow = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
auto* lovetext = new wxStaticText(this, wxID_ANY,
|
||||
#ifdef __APPLE__
|
||||
"With 💖 from C (claration)"
|
||||
#else
|
||||
"With <3 from C (claration)"
|
||||
#endif
|
||||
);
|
||||
|
||||
auto* link1 = new wxStaticText(this, wxID_ANY, "@palera1n");
|
||||
auto* link2 = new wxStaticText(this, wxID_ANY, "https://palera.in");
|
||||
|
||||
link1->SetForegroundColour(wxColour(0, 120, 215));
|
||||
link2->SetForegroundColour(wxColour(0, 120, 215));
|
||||
|
||||
link1->SetCursor(wxCursor(wxCURSOR_HAND));
|
||||
link2->SetCursor(wxCursor(wxCURSOR_HAND));
|
||||
|
||||
link1->Bind(wxEVT_LEFT_DOWN, [](wxMouseEvent&) {
|
||||
twitter->Bind(wxEVT_LEFT_DOWN, [](wxMouseEvent&)
|
||||
{
|
||||
wxLaunchDefaultBrowser("https://twitter.com/intent/follow?screen_name=palera1n");
|
||||
});
|
||||
|
||||
link2->Bind(wxEVT_LEFT_DOWN, [](wxMouseEvent&) {
|
||||
website->Bind(wxEVT_LEFT_DOWN, [](wxMouseEvent&)
|
||||
{
|
||||
wxLaunchDefaultBrowser("https://palera.in");
|
||||
});
|
||||
|
||||
credrow->Add(lovetext, 0, wxALIGN_BOTTOM | wxLEFT, 0);
|
||||
credrow->AddStretchSpacer();
|
||||
auto* twitterRow = new wxBoxSizer(wxHORIZONTAL);
|
||||
twitterRow->Add(twitterLabel, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
|
||||
twitterRow->Add(twitter, 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
auto* linkSizer = new wxBoxSizer(wxVERTICAL);
|
||||
linkSizer->Add(link1, 0, wxALIGN_RIGHT);
|
||||
linkSizer->Add(link2, 0, wxALIGN_RIGHT);
|
||||
auto* websiteRow = new wxBoxSizer(wxHORIZONTAL);
|
||||
websiteRow->Add(websiteLabel, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
|
||||
websiteRow->Add(website, 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
credrow->Add(linkSizer, 0, wxALIGN_CENTER_VERTICAL, 0);
|
||||
root->Add(credtext, 0, wxLEFT | wxRIGHT, 10);
|
||||
root->Add(credrow, 0, wxEXPAND | wxLEFT | wxRIGHT, 10);
|
||||
auto* logoText = new wxStaticText(this, wxID_ANY, "palera1n");
|
||||
|
||||
root->Add(new wxStaticLine(this), 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
auto* notetext = new wxStaticText(this, wxID_ANY,
|
||||
"NOTE: Please ensure you have a backup of your device before applying the\n"
|
||||
"jailbreak. While data loss is unlikely, we won't be responsible if something goes\n"
|
||||
"wrong. Use at your own risk.");
|
||||
|
||||
wxFont notefont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
#ifdef __APPLE__
|
||||
notefont.SetPointSize(12);
|
||||
#else
|
||||
notefont.SetPointSize(9);
|
||||
#endif
|
||||
notefont.SetStyle(wxFONTSTYLE_ITALIC);
|
||||
notetext->SetFont(notefont);
|
||||
|
||||
root->Add(notetext, 0, wxLEFT | wxRIGHT, 10);
|
||||
|
||||
root->AddStretchSpacer();
|
||||
|
||||
auto* quickMode = new wxCheckBox(this, wxID_ANY, "Quick Mode");
|
||||
auto* quickMode = new wxCheckBox(this, wxID_ANY, "Enable Quick Mode");
|
||||
auto* donateButton = new wxButton(this, wxID_ANY, "Donate");
|
||||
auto* optionsButton = new wxButton(this, wxID_ANY, "Options");
|
||||
|
||||
m_startButton = new wxButton(this, wxID_ANY, "Start");
|
||||
m_startButton->Disable();
|
||||
|
||||
quickMode->SetValue(palerain_flags & palerain_option_quick);
|
||||
|
||||
quickMode->Bind(wxEVT_CHECKBOX, [&](wxCommandEvent& e)
|
||||
{
|
||||
if (e.IsChecked())
|
||||
@@ -144,22 +115,66 @@ MainPanel::MainPanel(MainFrame* frame, wxWindow* parent)
|
||||
m_startButton->Bind(wxEVT_BUTTON, [frame](wxCommandEvent&)
|
||||
{
|
||||
if (frame->GetDeviceState().mode == DeviceMode::Normal)
|
||||
{
|
||||
frame->ShowRecovery();
|
||||
}
|
||||
else if (frame->GetDeviceState().mode == DeviceMode::Recovery)
|
||||
{
|
||||
frame->ShowDfu();
|
||||
}
|
||||
});
|
||||
|
||||
auto* bottomRow = new wxBoxSizer(wxHORIZONTAL);
|
||||
bottomRow->AddStretchSpacer();
|
||||
bottomRow->Add(quickMode, 0, wxRIGHT | wxTOP, 1);
|
||||
bottomRow->Add(optionsButton, 0, wxRIGHT | wxLEFT | wxBOTTOM, 12);
|
||||
bottomRow->Add(m_startButton, 0, wxRIGHT | wxLEFT | wxBOTTOM, 12);
|
||||
left->Add(m_deviceTitle, 0, wxLEFT | wxRIGHT | wxTOP, 10);
|
||||
left->Add(m_deviceSubtitle, 0, wxLEFT | wxRIGHT | wxBOTTOM, 10);
|
||||
left->Add(new wxStaticLine(this), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10);
|
||||
left->Add(credtext, 0, wxLEFT | wxRIGHT | wxBOTTOM, 10);
|
||||
|
||||
root->Add(bottomRow, 0, wxEXPAND);
|
||||
auto* linksCol = new wxBoxSizer(wxVERTICAL);
|
||||
linksCol->Add(twitterRow, 0);
|
||||
linksCol->Add(websiteRow, 0);
|
||||
|
||||
auto* warningNote = new wxStaticText(this, wxID_ANY,
|
||||
"NOTE: Please ensure you've made a backup of your device before proceeding.\n",
|
||||
wxDefaultPosition,
|
||||
wxDefaultSize,
|
||||
wxST_WRAP
|
||||
);
|
||||
|
||||
wxFont noteFont = warningNote->GetFont();
|
||||
noteFont.SetStyle(wxFONTSTYLE_ITALIC);
|
||||
warningNote->SetFont(noteFont);
|
||||
warningNote->SetForegroundColour(wxColour(200, 80, 80));
|
||||
|
||||
auto* linksRow = new wxBoxSizer(wxHORIZONTAL);
|
||||
linksRow->Add(linksCol, 0, wxLEFT | wxRIGHT | wxALIGN_TOP, 10);
|
||||
linksRow->AddStretchSpacer();
|
||||
linksRow->Add(warningNote, 0, wxRIGHT | wxALIGN_TOP, 10);
|
||||
|
||||
left->Add(linksRow, 0, wxEXPAND | wxBOTTOM, 10);
|
||||
|
||||
left->AddStretchSpacer();
|
||||
|
||||
left->Add(new wxStaticLine(this), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10);
|
||||
|
||||
#if __APPLE__
|
||||
auto* madeBy = new wxStaticText(this, wxID_ANY, "Made with 💖 by C (claration)");
|
||||
#else
|
||||
auto* madeBy = new wxStaticText(this, wxID_ANY, "Made with <3 by C (claration)");
|
||||
#endif
|
||||
|
||||
auto* quickModeRow = new wxBoxSizer(wxHORIZONTAL);
|
||||
quickModeRow->Add(madeBy, 0, wxALIGN_CENTER_VERTICAL);
|
||||
quickModeRow->AddStretchSpacer();
|
||||
quickModeRow->Add(quickMode, 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
left->Add(quickModeRow, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10);
|
||||
|
||||
right->Add(logo, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP | wxLEFT | wxRIGHT, 10);
|
||||
right->Add(logoText, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP, 5);
|
||||
right->AddStretchSpacer();
|
||||
right->Add(donateButton, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10);
|
||||
right->Add(optionsButton, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10);
|
||||
right->Add(m_startButton, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10);
|
||||
|
||||
root->Add(left, 1, wxEXPAND);
|
||||
root->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL), 0, wxEXPAND | wxTOP | wxBOTTOM, 10);
|
||||
root->Add(right, 0, wxEXPAND);
|
||||
|
||||
SetSizer(root);
|
||||
}
|
||||
@@ -168,11 +183,25 @@ void MainPanel::SetDeviceState(const DeviceState& state)
|
||||
{
|
||||
DevicePanel::SetDeviceState(state);
|
||||
|
||||
if (m_deviceText)
|
||||
if (m_deviceTitle && m_deviceSubtitle)
|
||||
{
|
||||
if (!state.connected)
|
||||
{
|
||||
m_deviceText->SetLabel(deviceTextString);
|
||||
if (state.connectedDeviceCount > 1)
|
||||
{
|
||||
m_deviceTitle->SetLabel("Multiple devices detected");
|
||||
m_deviceSubtitle->SetLabel(
|
||||
wxString::Format(
|
||||
"%u USB devices connected. Disconnect extras and keep only one device attached.",
|
||||
state.connectedDeviceCount
|
||||
)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_deviceTitle->SetLabel(deviceTextString);
|
||||
m_deviceSubtitle->SetLabel(deviceTextString2);
|
||||
}
|
||||
m_startButton->Enable(false);
|
||||
}
|
||||
else
|
||||
@@ -187,28 +216,34 @@ void MainPanel::SetDeviceState(const DeviceState& state)
|
||||
{
|
||||
case DeviceMode::Normal:
|
||||
if (state.isSupported) {
|
||||
m_deviceText->SetLabel("Connected " + productString + " (iOS " + versionString + ") in normal mode.\n" + ecidString);
|
||||
m_deviceTitle->SetLabel(productString);
|
||||
m_deviceSubtitle->SetLabel("Connected in normal mode • iOS " + versionString + "\n" + ecidString);
|
||||
m_startButton->Enable();
|
||||
} else {
|
||||
m_deviceText->SetLabel("Sorry, " + productString + " (iOS " + versionString + ") is not supported.\n" + ecidString);
|
||||
m_deviceTitle->SetLabel(productString);
|
||||
m_deviceSubtitle->SetLabel("Not supported • iOS " + versionString + "\n" + ecidString);
|
||||
m_startButton->Enable(false);
|
||||
}
|
||||
break;
|
||||
case DeviceMode::Recovery:
|
||||
if (state.isSupported) {
|
||||
m_deviceText->SetLabel("Connected " + productString + " in recovery mode.\n" + ecidString);
|
||||
m_deviceTitle->SetLabel(productString);
|
||||
m_deviceSubtitle->SetLabel("Connected in recovery mode\n" + ecidString);
|
||||
m_startButton->Enable();
|
||||
} else {
|
||||
m_deviceText->SetLabel("Sorry, " + productString + " is not supported.\n" + ecidString);
|
||||
m_deviceTitle->SetLabel(productString);
|
||||
m_deviceSubtitle->SetLabel("Not supported\n" + ecidString);
|
||||
m_startButton->Enable(false);
|
||||
}
|
||||
break;
|
||||
case DeviceMode::DFU:
|
||||
m_deviceText->SetLabel("Sorry, DFU mode is not supported.");
|
||||
m_deviceTitle->SetLabel("DFU Mode device");
|
||||
m_deviceSubtitle->SetLabel("Sorry, jailbreaking in DFU mode is not supported.\n");
|
||||
m_startButton->Enable(false);
|
||||
break;
|
||||
case DeviceMode::None:
|
||||
m_deviceText->SetLabel(deviceTextString);
|
||||
m_deviceTitle->SetLabel(deviceTextString);
|
||||
m_deviceSubtitle->SetLabel(deviceTextString2);
|
||||
m_startButton->Enable(false);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include "DevicePanel.hpp"
|
||||
#include "../state.hpp"
|
||||
#include "../event.hpp"
|
||||
|
||||
class MainPanel;
|
||||
class MainPanel : public DevicePanel
|
||||
@@ -14,7 +14,8 @@ public:
|
||||
explicit MainPanel(MainFrame* frame, wxWindow* parent);
|
||||
void SetDeviceState(const DeviceState& state) override;
|
||||
private:
|
||||
wxStaticText* m_deviceText = nullptr;
|
||||
wxStaticText* m_deviceTitle = nullptr;
|
||||
wxStaticText* m_deviceSubtitle = nullptr;
|
||||
wxButton* m_startButton = nullptr;
|
||||
};
|
||||
|
||||
|
||||
+33
-19
@@ -3,16 +3,21 @@
|
||||
#include "RecoveryPanel.hpp"
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/mstream.h>
|
||||
#include <wx/image.h>
|
||||
#include <wx/bitmap.h>
|
||||
#include <wx/statline.h>
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "AppFrame.hpp"
|
||||
#include "DevicePanel.hpp"
|
||||
|
||||
#include "../state.hpp"
|
||||
#include "../event.hpp"
|
||||
#include "../utils.h"
|
||||
#include "../globals.h"
|
||||
#include "../paleinfo.h"
|
||||
#include "../gen/images/logo.h"
|
||||
|
||||
#include <libimobiledevice/libimobiledevice.h>
|
||||
#include <libimobiledevice/lockdown.h>
|
||||
@@ -22,20 +27,19 @@ RecoveryPanel::RecoveryPanel(MainFrame* frame, wxWindow* parent)
|
||||
: DevicePanel(frame, parent)
|
||||
{
|
||||
Bind(wxEVT_SHOW, &RecoveryPanel::OnShow, this);
|
||||
auto* root = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
root->Add(new wxStaticText(this, wxID_ANY,
|
||||
"The device needs to be put into DFU mode to apply the jailbreak. This is a\n"
|
||||
"manual process and we will guide you through it.\n"
|
||||
"In order to prevent filesystem corruption through hard reset, the device will\n"
|
||||
"be put into recovery mode first. Click next when you are ready."
|
||||
), 0, wxALL, 10);
|
||||
auto* root = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto* left = new wxBoxSizer(wxVERTICAL);
|
||||
wxMemoryInputStream stream(images_logo_png, images_logo_png_len);
|
||||
wxImage img(stream, wxBITMAP_TYPE_PNG);
|
||||
wxBitmap bmp(img);
|
||||
auto* logo = new wxStaticBitmap(this, wxID_ANY, bmp);
|
||||
auto* logoText = new wxStaticText(this, wxID_ANY, "Recovery");
|
||||
auto* right = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
m_statusText = new wxStaticText(this, wxID_ANY, "");
|
||||
root->Add(m_statusText, 0, wxALL, 10);
|
||||
|
||||
m_backButton = new wxButton(this, wxID_ANY, "Back");
|
||||
m_nextButton = new wxButton(this, wxID_ANY, "Next");
|
||||
|
||||
m_backButton->Bind(wxEVT_BUTTON, [frame](wxCommandEvent&)
|
||||
{
|
||||
frame->ShowMain();
|
||||
@@ -47,7 +51,6 @@ RecoveryPanel::RecoveryPanel(MainFrame* frame, wxWindow* parent)
|
||||
m_nextButton->Disable();
|
||||
EnterRecoveryMode();
|
||||
});
|
||||
|
||||
m_timer.Bind(wxEVT_TIMER, [this](wxTimerEvent&)
|
||||
{
|
||||
m_backButton->Enable();
|
||||
@@ -58,13 +61,24 @@ RecoveryPanel::RecoveryPanel(MainFrame* frame, wxWindow* parent)
|
||||
m_statusText->SetLabel("");
|
||||
});
|
||||
|
||||
auto* bottomRow = new wxBoxSizer(wxHORIZONTAL);
|
||||
bottomRow->AddStretchSpacer();
|
||||
bottomRow->Add(m_backButton, 0, wxRIGHT | wxBOTTOM, 12);
|
||||
bottomRow->Add(m_nextButton, 0, wxRIGHT | wxLEFT | wxBOTTOM, 12);
|
||||
left->Add(
|
||||
new wxStaticText(this, wxID_ANY,
|
||||
"The device needs to be put into DFU mode to apply the jailbreak. This is a manual process and we will guide you through it.",
|
||||
wxDefaultPosition,
|
||||
wxDefaultSize,
|
||||
wxST_WRAP
|
||||
), 0, wxALL, 10);
|
||||
left->Add(m_statusText, 0, wxALL, 10);
|
||||
|
||||
root->AddStretchSpacer();
|
||||
root->Add(bottomRow, 0, wxEXPAND);
|
||||
right->Add(logo, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP | wxLEFT | wxRIGHT, 10);
|
||||
right->Add(logoText, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP, 5);
|
||||
right->AddStretchSpacer();
|
||||
right->Add(m_backButton, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10);
|
||||
right->Add(m_nextButton, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10);
|
||||
|
||||
root->Add(left, 1, wxEXPAND);
|
||||
root->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL), 0, wxEXPAND | wxTOP | wxBOTTOM, 10);
|
||||
root->Add(right, 0, wxEXPAND);
|
||||
|
||||
SetSizer(root);
|
||||
}
|
||||
@@ -85,7 +99,7 @@ void RecoveryPanel::SetDeviceState(const DeviceState& state)
|
||||
if (m_isEnteringRecovery && state.mode == DeviceMode::Normal)
|
||||
{
|
||||
m_isEnteringRecovery = false;
|
||||
m_statusText->SetLabel("Hmm... It seems like the device didn't enter recovery mode. Please try again.");
|
||||
m_statusText->SetLabel("Hmm... It seems like the device didn't enter recovery mode.");
|
||||
m_backButton->Enable();
|
||||
m_nextButton->Enable();
|
||||
}
|
||||
|
||||
+111
-23
@@ -3,32 +3,90 @@
|
||||
#include "SettingsPanel.hpp"
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/mstream.h>
|
||||
#include <wx/image.h>
|
||||
#include <wx/bitmap.h>
|
||||
#include <wx/statline.h>
|
||||
|
||||
#include "AppFrame.hpp"
|
||||
#include "DevicePanel.hpp"
|
||||
#include "../utils.h"
|
||||
#include "../globals.h"
|
||||
#include "../paleinfo.h"
|
||||
#include "../gen/images/logo.h"
|
||||
|
||||
SettingsPanel::SettingsPanel(MainFrame* frame, wxWindow* parent)
|
||||
: DevicePanel(frame, parent)
|
||||
{
|
||||
auto* root = new wxBoxSizer(wxVERTICAL);
|
||||
auto* root = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto* left = new wxBoxSizer(wxVERTICAL);
|
||||
wxMemoryInputStream stream(images_logo_png, images_logo_png_len);
|
||||
wxImage img(stream, wxBITMAP_TYPE_PNG);
|
||||
wxBitmap bmp(img);
|
||||
auto* logo = new wxStaticBitmap(this, wxID_ANY, bmp);
|
||||
auto* logoText = new wxStaticText(this, wxID_ANY, "Options");
|
||||
auto* right = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
root->Add(new wxStaticText(this, wxID_ANY,
|
||||
"You may set the following options. If you don't know what they mean you'll\n"
|
||||
"probably have no reason to set them."
|
||||
left->Add(new wxStaticText(this, wxID_ANY,
|
||||
"You may set the following options. If you don't know what they mean you'll probably have no reason to set them.",
|
||||
wxDefaultPosition,
|
||||
wxDefaultSize,
|
||||
wxST_WRAP
|
||||
), 0, wxALL, 10);
|
||||
|
||||
auto* optionSafemode = new wxCheckBox(this, wxID_ANY, "Safe Mode");
|
||||
auto* optionVerbose = new wxCheckBox(this, wxID_ANY, "Verbose Boot");
|
||||
auto* optionRevert = new wxCheckBox(this, wxID_ANY, "Restore System");
|
||||
auto* optionDarkBlockchain = new wxCheckBox(this, wxID_ANY, "Dark Blockchain");
|
||||
auto* bootArgs = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize);
|
||||
auto* rootType = new wxRadioBox(
|
||||
this,
|
||||
wxID_ANY,
|
||||
"Jailbreak Type",
|
||||
wxDefaultPosition,
|
||||
wxDefaultSize,
|
||||
2,
|
||||
(const wxString[]){
|
||||
"Rootless",
|
||||
"Rootful"
|
||||
},
|
||||
1,
|
||||
wxRA_SPECIFY_COLS
|
||||
);
|
||||
|
||||
auto* setupMode = new wxRadioBox(
|
||||
this,
|
||||
wxID_ANY,
|
||||
"Rootful Options",
|
||||
wxDefaultPosition,
|
||||
wxDefaultSize,
|
||||
3,
|
||||
(const wxString[]){
|
||||
"Boot",
|
||||
"Create FakeFS",
|
||||
"Create BindFS"
|
||||
},
|
||||
1,
|
||||
wxRA_SPECIFY_COLS
|
||||
);
|
||||
|
||||
optionSafemode->SetValue(palerain_flags & palerain_option_safemode);
|
||||
optionVerbose->SetValue(palerain_flags & palerain_option_verbose_boot);
|
||||
optionRevert->SetValue(palerain_flags & palerain_option_force_revert);
|
||||
optionDarkBlockchain->SetValue(palerain_flags & palerain_option_flower_chain);
|
||||
bootArgs->SetValue(wxString(boot_args, wxConvUTF8));
|
||||
if (palerain_flags & palerain_option_rootful)
|
||||
rootType->SetSelection(1);
|
||||
else
|
||||
rootType->SetSelection(0);
|
||||
|
||||
if (palerain_flags & palerain_option_setup_rootful)
|
||||
setupMode->SetSelection(1);
|
||||
else if (palerain_flags & palerain_option_setup_partial_root)
|
||||
setupMode->SetSelection(2);
|
||||
else
|
||||
setupMode->SetSelection(0);
|
||||
setupMode->Enable(rootType->GetSelection() == 1);
|
||||
|
||||
optionSafemode->Bind(wxEVT_CHECKBOX, [&](wxCommandEvent& e)
|
||||
{
|
||||
@@ -37,7 +95,6 @@ SettingsPanel::SettingsPanel(MainFrame* frame, wxWindow* parent)
|
||||
else
|
||||
palerain_flags &= ~palerain_option_safemode;
|
||||
});
|
||||
|
||||
optionVerbose->Bind(wxEVT_CHECKBOX, [&](wxCommandEvent& e)
|
||||
{
|
||||
if (e.IsChecked())
|
||||
@@ -45,7 +102,6 @@ SettingsPanel::SettingsPanel(MainFrame* frame, wxWindow* parent)
|
||||
else
|
||||
palerain_flags &= ~palerain_option_verbose_boot;
|
||||
});
|
||||
|
||||
optionRevert->Bind(wxEVT_CHECKBOX, [&](wxCommandEvent& e)
|
||||
{
|
||||
if (e.IsChecked())
|
||||
@@ -53,7 +109,6 @@ SettingsPanel::SettingsPanel(MainFrame* frame, wxWindow* parent)
|
||||
else
|
||||
palerain_flags &= ~palerain_option_force_revert;
|
||||
});
|
||||
|
||||
optionDarkBlockchain->Bind(wxEVT_CHECKBOX, [&](wxCommandEvent& e)
|
||||
{
|
||||
if (e.IsChecked())
|
||||
@@ -61,13 +116,6 @@ SettingsPanel::SettingsPanel(MainFrame* frame, wxWindow* parent)
|
||||
else
|
||||
palerain_flags &= ~palerain_option_flower_chain;
|
||||
});
|
||||
|
||||
root->Add(optionSafemode, 0, wxLEFT | wxRIGHT, 10);
|
||||
root->Add(optionVerbose, 0, wxLEFT | wxRIGHT | wxTOP, 10);
|
||||
root->Add(new wxStaticText(this, wxID_ANY, "Boot Arguments:"), 0, wxALL, 10);
|
||||
|
||||
auto* bootArgs = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize);
|
||||
bootArgs->SetValue(wxString(boot_args, wxConvUTF8));
|
||||
bootArgs->Bind(wxEVT_TEXT, [&](wxCommandEvent& e)
|
||||
{
|
||||
wxString value = e.GetString();
|
||||
@@ -77,25 +125,65 @@ SettingsPanel::SettingsPanel(MainFrame* frame, wxWindow* parent)
|
||||
}
|
||||
snprintf(boot_args, sizeof(boot_args), "%s", value.ToStdString().c_str());
|
||||
});
|
||||
rootType->Bind(wxEVT_RADIOBOX, [setupMode](wxCommandEvent& e)
|
||||
{
|
||||
palerain_flags &= ~(palerain_option_rootless | palerain_option_rootful);
|
||||
palerain_flags &= ~(palerain_option_setup_rootful | palerain_option_setup_partial_root);
|
||||
|
||||
root->Add(bootArgs, 0, wxLEFT | wxRIGHT | wxEXPAND, 30);
|
||||
root->Add(optionRevert, 0, wxLEFT | wxRIGHT | wxTOP, 10);
|
||||
root->Add(optionDarkBlockchain, 0, wxLEFT | wxRIGHT | wxTOP, 10);
|
||||
setupMode->Enable(e.GetSelection() == 1);
|
||||
setupMode->SetSelection(0);
|
||||
|
||||
root->AddStretchSpacer();
|
||||
if (e.GetSelection() == 0)
|
||||
palerain_flags |= palerain_option_rootless;
|
||||
else
|
||||
palerain_flags |= palerain_option_rootful;
|
||||
});
|
||||
|
||||
setupMode->Bind(wxEVT_RADIOBOX, [&](wxCommandEvent& e)
|
||||
{
|
||||
palerain_flags &= ~(palerain_option_setup_rootful | palerain_option_setup_partial_root);
|
||||
|
||||
if (e.GetSelection() == 1)
|
||||
palerain_flags |= palerain_option_setup_rootful;
|
||||
else if (e.GetSelection() == 2)
|
||||
palerain_flags |= palerain_option_setup_partial_root;
|
||||
});
|
||||
|
||||
auto* contentRow = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto* leftColumn = new wxBoxSizer(wxVERTICAL);
|
||||
auto* rightColumn = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
leftColumn->Add(rootType, 0, wxALL | wxEXPAND, 5);
|
||||
leftColumn->Add(setupMode, 0, wxALL | wxEXPAND, 5);
|
||||
|
||||
rightColumn->Add(optionSafemode, 0, wxALL, 5);
|
||||
rightColumn->Add(optionVerbose, 0, wxALL, 5);
|
||||
rightColumn->Add(optionRevert, 0, wxALL, 5);
|
||||
rightColumn->Add(optionDarkBlockchain, 0, wxALL, 5);
|
||||
rightColumn->AddStretchSpacer();
|
||||
|
||||
contentRow->Add(leftColumn, 1, wxEXPAND | wxRIGHT, 10);
|
||||
contentRow->Add(rightColumn, 1, wxEXPAND | wxLEFT, 10);
|
||||
|
||||
left->Add(contentRow, 1, wxEXPAND | wxLEFT | wxRIGHT, 10);
|
||||
left->Add(new wxStaticText(this, wxID_ANY, "Boot Arguments:"), 0, wxALL, 15);
|
||||
left->Add(bootArgs, 0, wxLEFT | wxRIGHT | wxEXPAND, 30);
|
||||
left->AddStretchSpacer();
|
||||
|
||||
auto* back = new wxButton(this, wxID_ANY, "Back");
|
||||
right->Add(logo, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP | wxLEFT | wxRIGHT, 10);
|
||||
right->Add(logoText, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP, 5);
|
||||
right->AddStretchSpacer();
|
||||
right->Add(back, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10);
|
||||
|
||||
back->Bind(wxEVT_BUTTON, [frame](wxCommandEvent&)
|
||||
{
|
||||
frame->ShowMain();
|
||||
});
|
||||
|
||||
auto* bottomRow = new wxBoxSizer(wxHORIZONTAL);
|
||||
bottomRow->AddStretchSpacer();
|
||||
bottomRow->Add(back, 0, wxRIGHT | wxLEFT | wxBOTTOM, 12);
|
||||
|
||||
root->Add(bottomRow, 0, wxEXPAND);
|
||||
root->Add(left, 1, wxEXPAND);
|
||||
root->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL), 0, wxEXPAND | wxTOP | wxBOTTOM, 10);
|
||||
root->Add(right, 0, wxEXPAND);
|
||||
|
||||
SetSizer(root);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user