|
@@ -6,98 +6,221 @@
|
|
|
#include <fcntl.h>
|
|
#include <fcntl.h>
|
|
|
#include <unistd.h>
|
|
#include <unistd.h>
|
|
|
#include <cstdlib>
|
|
#include <cstdlib>
|
|
|
|
|
+#include <cstdio>
|
|
|
|
|
+#include <algorithm>
|
|
|
|
|
+#include <chrono>
|
|
|
|
|
+#include <iomanip>
|
|
|
|
|
+#include <sstream>
|
|
|
|
|
|
|
|
namespace fs = std::filesystem;
|
|
namespace fs = std::filesystem;
|
|
|
|
|
|
|
|
|
|
+// The initialization list now perfectly matches the header declaration order
|
|
|
MainWindow::MainWindow()
|
|
MainWindow::MainWindow()
|
|
|
-: m_MainLayout(Gtk::ORIENTATION_VERTICAL, 0),
|
|
|
|
|
- m_VBoxBackup(Gtk::ORIENTATION_VERTICAL, 15),
|
|
|
|
|
|
|
+: m_HeaderBar(),
|
|
|
|
|
+ m_MainLayout(Gtk::ORIENTATION_VERTICAL, 0),
|
|
|
|
|
+ m_Notebook(),
|
|
|
|
|
+ m_ScrolledWindow(),
|
|
|
|
|
+ m_LogView(),
|
|
|
|
|
+ m_RefTextBuffer(),
|
|
|
|
|
+ m_StatusLabel("Ready."),
|
|
|
|
|
+ m_VBoxBackup(Gtk::ORIENTATION_VERTICAL, 10),
|
|
|
m_LabelBackupInstruction("<span size='large' weight='bold'>Select what to backup:</span>"),
|
|
m_LabelBackupInstruction("<span size='large' weight='bold'>Select what to backup:</span>"),
|
|
|
m_CheckThemesBackup("GTK Themes (~/.themes)"),
|
|
m_CheckThemesBackup("GTK Themes (~/.themes)"),
|
|
|
m_CheckIconsBackup("Icons (~/.icons)"),
|
|
m_CheckIconsBackup("Icons (~/.icons)"),
|
|
|
- m_CheckDconfBackup("GNOME Settings (dconf dump)"),
|
|
|
|
|
|
|
+ m_CheckWallpapersBackup("Wallpapers (Dynamic)"),
|
|
|
|
|
+ m_CheckExtensionsBackup("GNOME Extensions"),
|
|
|
|
|
+ m_CheckDconfBackup("GNOME Settings (dconf)"),
|
|
|
m_ButtonBackup("Start Backup"),
|
|
m_ButtonBackup("Start Backup"),
|
|
|
- m_VBoxRestore(Gtk::ORIENTATION_VERTICAL, 15),
|
|
|
|
|
|
|
+ m_VBoxRestore(Gtk::ORIENTATION_VERTICAL, 10),
|
|
|
m_LabelRestoreInstruction("<span size='large' weight='bold'>Select what to restore:</span>"),
|
|
m_LabelRestoreInstruction("<span size='large' weight='bold'>Select what to restore:</span>"),
|
|
|
m_CheckThemesRestore("GTK Themes"),
|
|
m_CheckThemesRestore("GTK Themes"),
|
|
|
m_CheckIconsRestore("Icons"),
|
|
m_CheckIconsRestore("Icons"),
|
|
|
|
|
+ m_CheckWallpapersRestore("Wallpapers"),
|
|
|
|
|
+ m_CheckExtensionsRestore("GNOME Extensions"),
|
|
|
m_CheckDconfRestore("GNOME Settings"),
|
|
m_CheckDconfRestore("GNOME Settings"),
|
|
|
- m_ButtonRestore("Start Restore")
|
|
|
|
|
|
|
+ m_CheckDryRunRestore("Dry Run (Simulate only, do not write files)"),
|
|
|
|
|
+ m_ButtonRestore("Start Restore"),
|
|
|
|
|
+ m_WorkerRunning(false)
|
|
|
{
|
|
{
|
|
|
- // --- Force Dark Theme ---
|
|
|
|
|
- auto settings = Gtk::Settings::get_default();
|
|
|
|
|
- if (settings) {
|
|
|
|
|
- settings->property_gtk_application_prefer_dark_theme() = true;
|
|
|
|
|
|
|
+ // Connect the inter-thread dispatcher
|
|
|
|
|
+ m_Dispatcher.connect(sigc::mem_fun(*this, &MainWindow::on_dispatcher_ping));
|
|
|
|
|
+
|
|
|
|
|
+ const char* home = std::getenv("HOME");
|
|
|
|
|
+ if (home) {
|
|
|
|
|
+ std::string log_path = std::string(home) + "/gnome-vault.log";
|
|
|
|
|
+ m_LogFile.open(log_path, std::ios::app);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- set_default_size(550, 450);
|
|
|
|
|
- set_border_width(0); // Removing window border to let layout handle it
|
|
|
|
|
|
|
+ set_default_size(650, 700);
|
|
|
|
|
+ set_border_width(0);
|
|
|
|
|
|
|
|
- // --- HeaderBar Setup (Modern GNOME look) ---
|
|
|
|
|
m_HeaderBar.set_title("Gnome-Vault");
|
|
m_HeaderBar.set_title("Gnome-Vault");
|
|
|
m_HeaderBar.set_subtitle("System State Manager");
|
|
m_HeaderBar.set_subtitle("System State Manager");
|
|
|
m_HeaderBar.set_show_close_button(true);
|
|
m_HeaderBar.set_show_close_button(true);
|
|
|
set_titlebar(m_HeaderBar);
|
|
set_titlebar(m_HeaderBar);
|
|
|
|
|
|
|
|
- // Allow markup in the instruction labels
|
|
|
|
|
m_LabelBackupInstruction.set_use_markup(true);
|
|
m_LabelBackupInstruction.set_use_markup(true);
|
|
|
- m_LabelBackupInstruction.set_halign(Gtk::ALIGN_START);
|
|
|
|
|
m_LabelRestoreInstruction.set_use_markup(true);
|
|
m_LabelRestoreInstruction.set_use_markup(true);
|
|
|
- m_LabelRestoreInstruction.set_halign(Gtk::ALIGN_START);
|
|
|
|
|
|
|
|
|
|
- // --- Backup Page Setup ---
|
|
|
|
|
- m_VBoxBackup.set_border_width(20);
|
|
|
|
|
|
|
+ m_RefTextBuffer = Gtk::TextBuffer::create();
|
|
|
|
|
+ m_LogView.set_buffer(m_RefTextBuffer);
|
|
|
|
|
+ m_LogView.set_editable(false);
|
|
|
|
|
+ m_LogView.set_cursor_visible(false);
|
|
|
|
|
+ m_LogView.set_wrap_mode(Gtk::WRAP_WORD);
|
|
|
|
|
+ m_LogView.override_background_color(Gdk::RGBA("#1e1e1e"));
|
|
|
|
|
+ m_LogView.override_color(Gdk::RGBA("#dcdcdc"));
|
|
|
|
|
+
|
|
|
|
|
+ m_ScrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
|
|
|
|
|
+ m_ScrolledWindow.set_shadow_type(Gtk::SHADOW_IN);
|
|
|
|
|
+ m_ScrolledWindow.add(m_LogView);
|
|
|
|
|
+ m_ScrolledWindow.set_size_request(-1, 250);
|
|
|
|
|
+ m_ScrolledWindow.set_margin_left(10);
|
|
|
|
|
+ m_ScrolledWindow.set_margin_right(10);
|
|
|
|
|
+
|
|
|
|
|
+ m_VBoxBackup.set_border_width(15);
|
|
|
m_VBoxBackup.pack_start(m_LabelBackupInstruction, Gtk::PACK_SHRINK);
|
|
m_VBoxBackup.pack_start(m_LabelBackupInstruction, Gtk::PACK_SHRINK);
|
|
|
m_VBoxBackup.pack_start(m_CheckThemesBackup, Gtk::PACK_SHRINK);
|
|
m_VBoxBackup.pack_start(m_CheckThemesBackup, Gtk::PACK_SHRINK);
|
|
|
m_VBoxBackup.pack_start(m_CheckIconsBackup, Gtk::PACK_SHRINK);
|
|
m_VBoxBackup.pack_start(m_CheckIconsBackup, Gtk::PACK_SHRINK);
|
|
|
|
|
+ m_VBoxBackup.pack_start(m_CheckWallpapersBackup, Gtk::PACK_SHRINK);
|
|
|
|
|
+ m_VBoxBackup.pack_start(m_CheckExtensionsBackup, Gtk::PACK_SHRINK);
|
|
|
m_VBoxBackup.pack_start(m_CheckDconfBackup, Gtk::PACK_SHRINK);
|
|
m_VBoxBackup.pack_start(m_CheckDconfBackup, Gtk::PACK_SHRINK);
|
|
|
|
|
|
|
|
m_CheckThemesBackup.set_active(true);
|
|
m_CheckThemesBackup.set_active(true);
|
|
|
-
|
|
|
|
|
- // Make the button chunkier
|
|
|
|
|
|
|
+ m_CheckIconsBackup.set_active(true);
|
|
|
|
|
+ m_CheckWallpapersBackup.set_active(true);
|
|
|
|
|
+ m_CheckExtensionsBackup.set_active(true);
|
|
|
|
|
+ m_CheckDconfBackup.set_active(true);
|
|
|
|
|
+
|
|
|
m_ButtonBackup.set_size_request(-1, 45);
|
|
m_ButtonBackup.set_size_request(-1, 45);
|
|
|
- m_ButtonBackup.signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::on_button_backup_clicked));
|
|
|
|
|
m_VBoxBackup.pack_end(m_ButtonBackup, Gtk::PACK_SHRINK);
|
|
m_VBoxBackup.pack_end(m_ButtonBackup, Gtk::PACK_SHRINK);
|
|
|
|
|
|
|
|
- // --- Restore Page Setup ---
|
|
|
|
|
- m_VBoxRestore.set_border_width(20);
|
|
|
|
|
|
|
+ m_VBoxRestore.set_border_width(15);
|
|
|
m_VBoxRestore.pack_start(m_LabelRestoreInstruction, Gtk::PACK_SHRINK);
|
|
m_VBoxRestore.pack_start(m_LabelRestoreInstruction, Gtk::PACK_SHRINK);
|
|
|
m_VBoxRestore.pack_start(m_CheckThemesRestore, Gtk::PACK_SHRINK);
|
|
m_VBoxRestore.pack_start(m_CheckThemesRestore, Gtk::PACK_SHRINK);
|
|
|
m_VBoxRestore.pack_start(m_CheckIconsRestore, Gtk::PACK_SHRINK);
|
|
m_VBoxRestore.pack_start(m_CheckIconsRestore, Gtk::PACK_SHRINK);
|
|
|
|
|
+ m_VBoxRestore.pack_start(m_CheckWallpapersRestore, Gtk::PACK_SHRINK);
|
|
|
|
|
+ m_VBoxRestore.pack_start(m_CheckExtensionsRestore, Gtk::PACK_SHRINK);
|
|
|
m_VBoxRestore.pack_start(m_CheckDconfRestore, Gtk::PACK_SHRINK);
|
|
m_VBoxRestore.pack_start(m_CheckDconfRestore, Gtk::PACK_SHRINK);
|
|
|
|
|
+
|
|
|
|
|
+ m_CheckThemesRestore.set_active(true);
|
|
|
|
|
+ m_CheckIconsRestore.set_active(true);
|
|
|
|
|
+ m_CheckWallpapersRestore.set_active(true);
|
|
|
|
|
+ m_CheckExtensionsRestore.set_active(true);
|
|
|
|
|
+ m_CheckDconfRestore.set_active(true);
|
|
|
|
|
|
|
|
|
|
+ m_CheckDryRunRestore.set_active(true);
|
|
|
|
|
+ m_VBoxRestore.pack_start(m_CheckDryRunRestore, Gtk::PACK_SHRINK);
|
|
|
|
|
+
|
|
|
m_ButtonRestore.set_size_request(-1, 45);
|
|
m_ButtonRestore.set_size_request(-1, 45);
|
|
|
- m_ButtonRestore.signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::on_button_restore_clicked));
|
|
|
|
|
m_VBoxRestore.pack_end(m_ButtonRestore, Gtk::PACK_SHRINK);
|
|
m_VBoxRestore.pack_end(m_ButtonRestore, Gtk::PACK_SHRINK);
|
|
|
|
|
|
|
|
- // --- Notebook (Tabs) Setup ---
|
|
|
|
|
m_Notebook.append_page(m_VBoxBackup, "Backup");
|
|
m_Notebook.append_page(m_VBoxBackup, "Backup");
|
|
|
m_Notebook.append_page(m_VBoxRestore, "Restore");
|
|
m_Notebook.append_page(m_VBoxRestore, "Restore");
|
|
|
|
|
|
|
|
- // --- Status Label Setup ---
|
|
|
|
|
- m_StatusLabel.set_text("Ready.");
|
|
|
|
|
- m_StatusLabel.set_halign(Gtk::ALIGN_START);
|
|
|
|
|
- m_StatusLabel.set_margin_top(10);
|
|
|
|
|
- m_StatusLabel.set_margin_bottom(10);
|
|
|
|
|
- m_StatusLabel.set_margin_start(10);
|
|
|
|
|
-
|
|
|
|
|
- // --- Main Layout Assembly ---
|
|
|
|
|
m_MainLayout.pack_start(m_Notebook, Gtk::PACK_EXPAND_WIDGET);
|
|
m_MainLayout.pack_start(m_Notebook, Gtk::PACK_EXPAND_WIDGET);
|
|
|
- m_MainLayout.pack_end(m_StatusLabel, Gtk::PACK_SHRINK);
|
|
|
|
|
|
|
+ m_MainLayout.pack_start(m_ScrolledWindow, Gtk::PACK_EXPAND_WIDGET);
|
|
|
|
|
+ m_MainLayout.pack_start(m_StatusLabel, Gtk::PACK_SHRINK);
|
|
|
|
|
+
|
|
|
|
|
+ m_ButtonBackup.signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::on_button_backup_clicked));
|
|
|
|
|
+ m_ButtonRestore.signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::on_button_restore_clicked));
|
|
|
|
|
|
|
|
add(m_MainLayout);
|
|
add(m_MainLayout);
|
|
|
show_all_children();
|
|
show_all_children();
|
|
|
|
|
+ add_log_ui("Session Started. Multithreaded Engine Ready.");
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+MainWindow::~MainWindow() {
|
|
|
|
|
+ if (m_WorkerRunning) {
|
|
|
|
|
+ add_log_ui("Warning: Waiting for background worker to finish before closing...");
|
|
|
|
|
+ while (m_WorkerRunning) std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
|
|
|
+ }
|
|
|
|
|
+ if (m_LogFile.is_open()) m_LogFile.close();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void MainWindow::queue_log(const std::string& message) {
|
|
|
|
|
+ bool needs_emit = false;
|
|
|
|
|
+ {
|
|
|
|
|
+ std::lock_guard<std::mutex> lock(m_LogMutex);
|
|
|
|
|
+ needs_emit = m_LogQueue.empty();
|
|
|
|
|
+ m_LogQueue.push(message);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (needs_emit) {
|
|
|
|
|
+ m_Dispatcher.emit();
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void MainWindow::on_dispatcher_ping() {
|
|
|
|
|
+ std::queue<std::string> local_queue;
|
|
|
|
|
+ {
|
|
|
|
|
+ std::lock_guard<std::mutex> lock(m_LogMutex);
|
|
|
|
|
+ std::swap(local_queue, m_LogQueue);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ while (!local_queue.empty()) {
|
|
|
|
|
+ std::string msg = local_queue.front();
|
|
|
|
|
+ local_queue.pop();
|
|
|
|
|
+
|
|
|
|
|
+ if (msg == "__UNLOCK_UI__") {
|
|
|
|
|
+ set_ui_locked(false);
|
|
|
|
|
+ m_StatusLabel.set_text("Operation completed safely.");
|
|
|
|
|
+ } else {
|
|
|
|
|
+ add_log_ui(msg);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void MainWindow::add_log_ui(const std::string& message) {
|
|
|
|
|
+ auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
|
|
|
|
|
+ std::stringstream ss;
|
|
|
|
|
+ ss << std::put_time(std::localtime(&now), "[%H:%M:%S] ") << message << "\n";
|
|
|
|
|
+ std::string formatted = ss.str();
|
|
|
|
|
+
|
|
|
|
|
+ m_RefTextBuffer->insert(m_RefTextBuffer->end(), formatted);
|
|
|
|
|
+
|
|
|
|
|
+ Gtk::TextBuffer::iterator iter = m_RefTextBuffer->end();
|
|
|
|
|
+ m_RefTextBuffer->place_cursor(iter);
|
|
|
|
|
+
|
|
|
|
|
+ // THE FIX: Do not attempt to scroll if the window isn't painted on the screen yet!
|
|
|
|
|
+ // This stops GTK from segfaulting when trying to calculate geometries in the void.
|
|
|
|
|
+ if (m_LogView.get_realized()) {
|
|
|
|
|
+ m_LogView.scroll_to(m_RefTextBuffer->get_insert());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (m_LogFile.is_open()) {
|
|
|
|
|
+ m_LogFile << formatted;
|
|
|
|
|
+ m_LogFile.flush();
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-MainWindow::~MainWindow() {}
|
|
|
|
|
|
|
+void MainWindow::set_ui_locked(bool locked) {
|
|
|
|
|
+ m_ButtonBackup.set_sensitive(!locked);
|
|
|
|
|
+ m_ButtonRestore.set_sensitive(!locked);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+std::string MainWindow::get_wallpaper_directory() {
|
|
|
|
|
+ char buffer[256];
|
|
|
|
|
+ std::string result = "";
|
|
|
|
|
+ FILE* pipe = popen("gsettings get org.gnome.desktop.background picture-uri-dark 2>/dev/null", "r");
|
|
|
|
|
+ if (pipe) {
|
|
|
|
|
+ while (fgets(buffer, sizeof(buffer), pipe) != nullptr) result += buffer;
|
|
|
|
|
+ pclose(pipe);
|
|
|
|
|
+ }
|
|
|
|
|
+ size_t file_pos = result.find("file://");
|
|
|
|
|
+ if (file_pos != std::string::npos) {
|
|
|
|
|
+ std::string path = result.substr(file_pos + 7);
|
|
|
|
|
+ path.erase(std::remove(path.begin(), path.end(), '\''), path.end());
|
|
|
|
|
+ path.erase(std::remove(path.begin(), path.end(), '\n'), path.end());
|
|
|
|
|
+ return fs::path(path).parent_path().string();
|
|
|
|
|
+ }
|
|
|
|
|
+ return "";
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
bool MainWindow::create_tar_archive(const std::string& source_dir, const std::string& out_filename) {
|
|
bool MainWindow::create_tar_archive(const std::string& source_dir, const std::string& out_filename) {
|
|
|
struct archive *a;
|
|
struct archive *a;
|
|
|
struct archive *disk;
|
|
struct archive *disk;
|
|
|
struct archive_entry *entry;
|
|
struct archive_entry *entry;
|
|
|
char buff[8192];
|
|
char buff[8192];
|
|
|
- int len;
|
|
|
|
|
- int fd;
|
|
|
|
|
|
|
+ int len, fd;
|
|
|
|
|
|
|
|
a = archive_write_new();
|
|
a = archive_write_new();
|
|
|
archive_write_add_filter_gzip(a);
|
|
archive_write_add_filter_gzip(a);
|
|
@@ -107,20 +230,21 @@ bool MainWindow::create_tar_archive(const std::string& source_dir, const std::st
|
|
|
disk = archive_read_disk_new();
|
|
disk = archive_read_disk_new();
|
|
|
archive_read_disk_set_standard_lookup(disk);
|
|
archive_read_disk_set_standard_lookup(disk);
|
|
|
|
|
|
|
|
- for (const auto& dirEntry : fs::recursive_directory_iterator(source_dir)) {
|
|
|
|
|
|
|
+ fs::directory_options options = fs::directory_options::skip_permission_denied;
|
|
|
|
|
+ for (const auto& dirEntry : fs::recursive_directory_iterator(source_dir, options)) {
|
|
|
std::string path = dirEntry.path().string();
|
|
std::string path = dirEntry.path().string();
|
|
|
entry = archive_entry_new();
|
|
entry = archive_entry_new();
|
|
|
archive_entry_copy_pathname(entry, path.c_str());
|
|
archive_entry_copy_pathname(entry, path.c_str());
|
|
|
archive_read_disk_entry_from_file(disk, entry, -1, nullptr);
|
|
archive_read_disk_entry_from_file(disk, entry, -1, nullptr);
|
|
|
archive_write_header(a, entry);
|
|
archive_write_header(a, entry);
|
|
|
|
|
+
|
|
|
|
|
+ queue_log(" -> Packing: " + path);
|
|
|
|
|
|
|
|
if (fs::is_regular_file(dirEntry)) {
|
|
if (fs::is_regular_file(dirEntry)) {
|
|
|
fd = ::open(path.c_str(), O_RDONLY);
|
|
fd = ::open(path.c_str(), O_RDONLY);
|
|
|
if (fd >= 0) {
|
|
if (fd >= 0) {
|
|
|
- len = ::read(fd, buff, sizeof(buff));
|
|
|
|
|
- while (len > 0) {
|
|
|
|
|
|
|
+ while ((len = ::read(fd, buff, sizeof(buff))) > 0) {
|
|
|
archive_write_data(a, buff, len);
|
|
archive_write_data(a, buff, len);
|
|
|
- len = ::read(fd, buff, sizeof(buff));
|
|
|
|
|
}
|
|
}
|
|
|
::close(fd);
|
|
::close(fd);
|
|
|
}
|
|
}
|
|
@@ -133,34 +257,210 @@ bool MainWindow::create_tar_archive(const std::string& source_dir, const std::st
|
|
|
return true;
|
|
return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-void MainWindow::on_button_backup_clicked() {
|
|
|
|
|
- m_StatusLabel.set_text("Initializing Backup Sequence...");
|
|
|
|
|
- // Keep the cout for the terminal debugger
|
|
|
|
|
- std::cout << ">>> Initializing Backup Sequence..." << std::endl;
|
|
|
|
|
-
|
|
|
|
|
- std::string home_dir = getenv("HOME");
|
|
|
|
|
-
|
|
|
|
|
- if (m_CheckThemesBackup.get_active()) {
|
|
|
|
|
|
|
+bool MainWindow::extract_tar_archive(const std::string& archive_path, bool dry_run) {
|
|
|
|
|
+ struct archive *a;
|
|
|
|
|
+ struct archive_entry *entry;
|
|
|
|
|
+ int flags = ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_UNLINK | ARCHIVE_EXTRACT_SECURE_NODOTDOT;
|
|
|
|
|
+
|
|
|
|
|
+ a = archive_read_new();
|
|
|
|
|
+ archive_read_support_format_all(a);
|
|
|
|
|
+ archive_read_support_filter_all(a);
|
|
|
|
|
+
|
|
|
|
|
+ if (archive_read_open_filename(a, archive_path.c_str(), 10240) != ARCHIVE_OK) {
|
|
|
|
|
+ queue_log("Error: Failed to open archive " + archive_path);
|
|
|
|
|
+ archive_read_free(a);
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
|
|
|
|
|
+ std::string current_path = archive_entry_pathname(entry);
|
|
|
|
|
+ if (!current_path.empty() && current_path[0] != '/') {
|
|
|
|
|
+ current_path = "/" + current_path;
|
|
|
|
|
+ archive_entry_set_pathname(entry, current_path.c_str());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (dry_run) {
|
|
|
|
|
+ queue_log(" -> [DRY RUN] Would extract: " + current_path);
|
|
|
|
|
+ archive_read_data_skip(a);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ queue_log(" -> Extracting: " + current_path);
|
|
|
|
|
+ if (archive_read_extract(a, entry, flags) != ARCHIVE_OK) {
|
|
|
|
|
+ queue_log(" -> Warning: Failed to extract " + current_path + " (" + archive_error_string(a) + ")");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ archive_read_close(a);
|
|
|
|
|
+ archive_read_free(a);
|
|
|
|
|
+ return true;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void MainWindow::run_backup_job(JobConfig config, std::string home_dir, std::string wallpaper_dir) {
|
|
|
|
|
+ queue_log("--- Starting Backup Sequence (Worker Thread) ---");
|
|
|
|
|
+
|
|
|
|
|
+ if (config.themes) {
|
|
|
std::string target = home_dir + "/.themes";
|
|
std::string target = home_dir + "/.themes";
|
|
|
- std::string out = home_dir + "/gnome_themes_backup.tar.gz";
|
|
|
|
|
-
|
|
|
|
|
if (fs::exists(target)) {
|
|
if (fs::exists(target)) {
|
|
|
- m_StatusLabel.set_text("Packing Themes into archive...");
|
|
|
|
|
- create_tar_archive(target, out);
|
|
|
|
|
- m_StatusLabel.set_text("Success: Packed ~/.themes into gnome_themes_backup.tar.gz");
|
|
|
|
|
- } else {
|
|
|
|
|
- m_StatusLabel.set_text("Warning: ~/.themes doesn't exist. Skipped.");
|
|
|
|
|
|
|
+ queue_log("Backing up Themes...");
|
|
|
|
|
+ create_tar_archive(target, home_dir + "/gnome_themes_backup.tar.gz");
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (m_CheckIconsBackup.get_active()) std::cout << " [x] Queuing Icons (TODO)..." << std::endl;
|
|
|
|
|
- if (m_CheckDconfBackup.get_active()) std::cout << " [x] Queuing Dconf (TODO)..." << std::endl;
|
|
|
|
|
|
|
+ if (config.icons) {
|
|
|
|
|
+ std::string target = home_dir + "/.icons";
|
|
|
|
|
+ if (fs::exists(target)) {
|
|
|
|
|
+ queue_log("Backing up Icons...");
|
|
|
|
|
+ create_tar_archive(target, home_dir + "/gnome_icons_backup.tar.gz");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (config.wallpapers) {
|
|
|
|
|
+ if (!wallpaper_dir.empty() && wallpaper_dir != home_dir && wallpaper_dir != "/") {
|
|
|
|
|
+ queue_log("Backing up Wallpapers from: " + wallpaper_dir);
|
|
|
|
|
+ create_tar_archive(wallpaper_dir, home_dir + "/gnome_wallpapers_backup.tar.gz");
|
|
|
|
|
+ } else {
|
|
|
|
|
+ queue_log("Warning: Wallpaper directory too broad or empty. Skipped.");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (config.extensions) {
|
|
|
|
|
+ std::string target = home_dir + "/.local/share/gnome-shell/extensions";
|
|
|
|
|
+ if (fs::exists(target)) {
|
|
|
|
|
+ queue_log("Backing up Extensions...");
|
|
|
|
|
+ create_tar_archive(target, home_dir + "/gnome_extensions_backup.tar.gz");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (config.dconf) {
|
|
|
|
|
+ queue_log("Dumping dconf settings...");
|
|
|
|
|
+ std::string cmd = "dconf dump / > " + home_dir + "/gnome_dconf_backup.ini";
|
|
|
|
|
+ if (system(cmd.c_str()) == 0) {
|
|
|
|
|
+ queue_log(" -> Success: Dumped dconf");
|
|
|
|
|
+ } else {
|
|
|
|
|
+ queue_log(" -> Error: dconf dump failed");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ queue_log("--- Backup Completed ---");
|
|
|
|
|
+ queue_log("__UNLOCK_UI__");
|
|
|
|
|
+ m_WorkerRunning = false;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void MainWindow::on_button_backup_clicked() {
|
|
|
|
|
+ if (m_WorkerRunning) return;
|
|
|
|
|
+
|
|
|
|
|
+ set_ui_locked(true);
|
|
|
|
|
+ m_WorkerRunning = true;
|
|
|
|
|
+ m_StatusLabel.set_text("Backup in progress...");
|
|
|
|
|
+
|
|
|
|
|
+ const char* env_home = std::getenv("HOME");
|
|
|
|
|
+ std::string home_dir = env_home ? env_home : "";
|
|
|
|
|
+
|
|
|
|
|
+ if (home_dir.empty()) {
|
|
|
|
|
+ add_log_ui("Error: HOME environment variable missing.");
|
|
|
|
|
+ set_ui_locked(false);
|
|
|
|
|
+ m_WorkerRunning = false;
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ JobConfig config;
|
|
|
|
|
+ config.themes = m_CheckThemesBackup.get_active();
|
|
|
|
|
+ config.icons = m_CheckIconsBackup.get_active();
|
|
|
|
|
+ config.wallpapers = m_CheckWallpapersBackup.get_active();
|
|
|
|
|
+ config.extensions = m_CheckExtensionsBackup.get_active();
|
|
|
|
|
+ config.dconf = m_CheckDconfBackup.get_active();
|
|
|
|
|
+ config.dry_run = false;
|
|
|
|
|
+
|
|
|
|
|
+ std::string wallpaper_dir = get_wallpaper_directory();
|
|
|
|
|
+
|
|
|
|
|
+ std::thread(&MainWindow::run_backup_job, this, config, home_dir, wallpaper_dir).detach();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void MainWindow::run_restore_job(JobConfig config, std::string home_dir) {
|
|
|
|
|
+ if (config.dry_run) {
|
|
|
|
|
+ queue_log("--- Starting DRY RUN Restore (Worker Thread) ---");
|
|
|
|
|
+ queue_log("NO FILES WILL BE MODIFIED");
|
|
|
|
|
+ } else {
|
|
|
|
|
+ queue_log("--- Starting LIVE Restore (Worker Thread) ---");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (config.themes) {
|
|
|
|
|
+ std::string in = home_dir + "/gnome_themes_backup.tar.gz";
|
|
|
|
|
+ if (fs::exists(in)) {
|
|
|
|
|
+ queue_log("Processing Themes archive...");
|
|
|
|
|
+ extract_tar_archive(in, config.dry_run);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (config.icons) {
|
|
|
|
|
+ std::string in = home_dir + "/gnome_icons_backup.tar.gz";
|
|
|
|
|
+ if (fs::exists(in)) {
|
|
|
|
|
+ queue_log("Processing Icons archive...");
|
|
|
|
|
+ extract_tar_archive(in, config.dry_run);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (config.wallpapers) {
|
|
|
|
|
+ std::string in = home_dir + "/gnome_wallpapers_backup.tar.gz";
|
|
|
|
|
+ if (fs::exists(in)) {
|
|
|
|
|
+ queue_log("Processing Wallpapers archive...");
|
|
|
|
|
+ extract_tar_archive(in, config.dry_run);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (config.extensions) {
|
|
|
|
|
+ std::string in = home_dir + "/gnome_extensions_backup.tar.gz";
|
|
|
|
|
+ if (fs::exists(in)) {
|
|
|
|
|
+ queue_log("Processing Extensions archive...");
|
|
|
|
|
+ extract_tar_archive(in, config.dry_run);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (config.dconf) {
|
|
|
|
|
+ std::string in = home_dir + "/gnome_dconf_backup.ini";
|
|
|
|
|
+ if (fs::exists(in)) {
|
|
|
|
|
+ if (config.dry_run) {
|
|
|
|
|
+ queue_log(" -> [DRY RUN] Would load dconf settings from " + in);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ queue_log("Injecting dconf settings...");
|
|
|
|
|
+ std::string cmd = "dconf load / < " + in;
|
|
|
|
|
+ if (system(cmd.c_str()) == 0) {
|
|
|
|
|
+ queue_log(" -> Success: dconf loaded");
|
|
|
|
|
+ } else {
|
|
|
|
|
+ queue_log(" -> Error: dconf load failed");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ queue_log("--- Restore Completed ---");
|
|
|
|
|
+ queue_log("__UNLOCK_UI__");
|
|
|
|
|
+ m_WorkerRunning = false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::on_button_restore_clicked() {
|
|
void MainWindow::on_button_restore_clicked() {
|
|
|
- m_StatusLabel.set_text("Initializing Restore Sequence...");
|
|
|
|
|
- std::cout << ">>> Initializing Restore Sequence..." << std::endl;
|
|
|
|
|
- if (m_CheckThemesRestore.get_active()) std::cout << " [x] Preparing to overwrite Themes..." << std::endl;
|
|
|
|
|
- if (m_CheckIconsRestore.get_active()) std::cout << " [x] Preparing to overwrite Icons..." << std::endl;
|
|
|
|
|
- if (m_CheckDconfRestore.get_active()) std::cout << " [x] Preparing to inject Dconf..." << std::endl;
|
|
|
|
|
|
|
+ if (m_WorkerRunning) return;
|
|
|
|
|
+
|
|
|
|
|
+ set_ui_locked(true);
|
|
|
|
|
+ m_WorkerRunning = true;
|
|
|
|
|
+ m_StatusLabel.set_text("Restore in progress...");
|
|
|
|
|
+
|
|
|
|
|
+ const char* env_home = std::getenv("HOME");
|
|
|
|
|
+ std::string home_dir = env_home ? env_home : "";
|
|
|
|
|
+
|
|
|
|
|
+ if (home_dir.empty()) {
|
|
|
|
|
+ add_log_ui("Error: HOME environment variable missing.");
|
|
|
|
|
+ set_ui_locked(false);
|
|
|
|
|
+ m_WorkerRunning = false;
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ JobConfig config;
|
|
|
|
|
+ config.themes = m_CheckThemesRestore.get_active();
|
|
|
|
|
+ config.icons = m_CheckIconsRestore.get_active();
|
|
|
|
|
+ config.wallpapers = m_CheckWallpapersRestore.get_active();
|
|
|
|
|
+ config.extensions = m_CheckExtensionsRestore.get_active();
|
|
|
|
|
+ config.dconf = m_CheckDconfRestore.get_active();
|
|
|
|
|
+ config.dry_run = m_CheckDryRunRestore.get_active();
|
|
|
|
|
+
|
|
|
|
|
+ std::thread(&MainWindow::run_restore_job, this, config, home_dir).detach();
|
|
|
}
|
|
}
|