public class MyApp.Window : Gtk.ApplicationWindow{ string laoutput; bool upol; public bool process_line (IOChannel channel, IOCondition condition, string stream_name, out string x, out bool y) { if (condition == IOCondition.HUP) { print ("%s: The fd has been closed.\n", stream_name); return false; } try { string line; channel.read_line (out line, null, null); print ("%s: %s", stream_name, line); x = line; y = true; } catch (IOChannelError e) { print ("%s: IOChannelError: %s\n", stream_name, e.message); return false; } catch (ConvertError e) { print ("%s: ConvertError: %s\n", stream_name, e.message); return false; } return true; } public Window(Nostalgia application){ Object( application: application ); } construct{ bool ssnap=false; var actualstack = new Gtk.Stack(); actualstack.expand = true; var createstack = new Gtk.Stack(); createstack.expand = true; var memc = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); var memr = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); var cr1 = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); var progresscr = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); //memr.add(new Gtk.Label("Restore")); actualstack.add_titled(memc, "memc", "Create"); actualstack.add_titled(memr, "memr", "Restore"); createstack.add_titled(cr1, "cr1", "Backup Options"); createstack.add_titled(progresscr, "progresscr", "Snapshot Progress" ); actualstack.set_transition_type(Gtk.StackTransitionType.SLIDE_UP_DOWN); actualstack.set_transition_duration(750); createstack.set_transition_type(Gtk.StackTransitionType.SLIDE_UP_DOWN); createstack.set_transition_duration(750); //memc.set_column_homogeneous(true); //memc.set_row_homogeneous(true); //BACKUP MENU INTERFACE var label1 = new Gtk.Label("Back up all your files and memories, For when you need them the most"); label1.set_use_markup(true); memc.pack_start(label1, true, true, 50); //memc.pack_start(new Gtk.Image.from_icon_name("liveusb-creator", Gtk.IconSize.DIALOG), false, false, 0); memc.pack_start(new Gtk.Label("Nostalgia makes incremental snapshots, that means: ✔ Only the first snapshot contains the full file system ✔ All snapshots after the first only document changes ✔ It takes up significantly less space compared to full system snapshots ✔ You can hold lots of backups on a drive"), true, false, 0); var button1 = new Gtk.Button.with_label("Create memory"); var blue_color = new Gdk.RGBA(); blue_color.parse("#0860F2"); var green_color = new Gdk.RGBA(); green_color.parse("#59C837"); var black_color = new Gdk.RGBA(); black_color.parse("#15161E"); button1.override_background_color(Gtk.StateFlags.NORMAL, green_color); var gridfb = new Gtk.Grid(); gridfb.attach(button1, 1, 1); button1.clicked.connect (() => { createstack.set_visible_child(cr1); print("Create Button Pressed \n"); remove(actualstack); add(createstack); createstack.set_visible_child(cr1); var crtitlebar = new MyApp.HeaderBar(createstack); set_titlebar(crtitlebar); show_all(); }); gridfb.attach(new Gtk.Label(""), 2, 1); gridfb.attach(new Gtk.Label(""), 0, 1); gridfb.set_column_homogeneous(true); gridfb.set_row_homogeneous(true); //var img1 = new Gtk.Image.from_icon_name("liveusb-creator", Gtk.IconSize.DIALOG); //memc.pack_start(img1, false, false, 30); memc.pack_start(gridfb, true, true, 60); var label1r = new Gtk.Label("For when the worst happens, I'm here to get you back online"); label1r.set_use_markup(true); memr.pack_start(label1r, true, true, 50); memr.pack_start(new Gtk.Label("You can use this option to restore your lost files and configurations from any of your backups. Keep in mind restoring your entire system using this tool may take a while, so please be patient and let us get your system back and running"), true, false, 0); var button1r = new Gtk.Button.with_label("Restore Memory"); button1r.override_background_color(Gtk.StateFlags.NORMAL, blue_color); var gridfbr = new Gtk.Grid(); gridfbr.attach(button1r, 1, 1); gridfbr.attach(new Gtk.Label(""), 0, 1); gridfbr.attach(new Gtk.Label(""), 2, 1); gridfbr.set_column_homogeneous(true); gridfbr.set_row_homogeneous(true); memr.pack_start(gridfbr, true, true, 60); //CREATESTACK STUFF cr1.pack_start(new Gtk.Label(""), false, false, 100); var cr11 = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0); //cr11.expand = true; cr11.pack_start(new Gtk.Label(""), true, true, 40); var cr1label = new Gtk.Label("Select your backup drive:"); cr1label.set_use_markup(true); cr11.pack_start(cr1label, true, true, 10); var crloc1 = new Gtk.FileChooserButton("Set location", Gtk.FileChooserAction.SELECT_FOLDER); cr11.pack_start(crloc1, true, true, 0); cr11.pack_start(new Gtk.Label(""), true, true, 10); cr1.pack_start(cr11, false, false, 0); cr1.pack_start(new Gtk.Label(""), false, false, 15); var cr12 = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0); var crbackbutton = new Gtk.Button.with_label("↩ Go back"); crbackbutton.clicked.connect (() => { if (!ssnap){ print("Menu Button Pressed \n"); remove(createstack); add(actualstack); var hmtitlebar = new MyApp.HeaderBar(actualstack); set_titlebar(hmtitlebar); show_all(); string x; //Process.spawn_command_line_sync("lsblk", out x); //print(x); } }); cr12.pack_start(crbackbutton, false, false, 10); /*var packhome = new Gtk.Switch(); packhome.set_state(true); var cr14 = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0); cr14.pack_start(new Gtk.Label(""), true, true, 10); var packhomela = new Gtk.Label("Include home folder in backup:"); packhomela.set_use_markup(true); cr14.pack_start(packhomela, false, false, 10); cr14.pack_start(packhome, false, false, 10); cr14.pack_start(new Gtk.Label(""), true, true, 10); cr1.pack_start(cr14, false, false, 0); */ var cr15 = new Gtk.Box(Gtk.Orientation.VERTICAL, 20); var ExcludePanel = new Gtk.ScrolledWindow(null, null); var ExcludeListLabel = new Gtk.Label("Type Every Folder to be EXCLUDED from the Backup, using ABSOLUTE paths, one per line"); var ExcludeList = new Gtk.TextView(); ExcludeList.set_editable(true); ExcludeList.set_cursor_visible(true); ExcludeList.override_background_color(Gtk.StateFlags.NORMAL, black_color); ExcludePanel.set_border_width(10); ExcludePanel.set_max_content_height(100); ExcludePanel.add(ExcludeList); cr15.pack_start(ExcludeListLabel); var cr151 = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 20); cr151.pack_start(new Gtk.Label(null), false, false, 5); cr151.pack_start(ExcludePanel); cr151.pack_start(new Gtk.Label(null), false, false, 5); cr15.pack_start(cr151, false, false ,0); cr1.pack_start(cr15, false, false, 0); var finbutcr = new Gtk.Button.with_label("✔ Make snapshot"); finbutcr.override_background_color(Gtk.StateFlags.NORMAL, green_color); cr12.pack_end(finbutcr, false, false, 10); //CONFIG FILE //var ExcludeFolders = ExcludeList.get_buffer().get_text(ExcludeList.get_buffer().get_start_iter(), ExcludeList.get_buffer().get_end_iter(), false); //var ExcludeFolders = ExcludeList.buffer.text; //print(ExcludeFolders); var configData = ""; var configFolderLoc = GLib.Environment.get_home_dir().concat("/.config/nostalgia"); print(configFolderLoc); File configFolder = File.new_for_path(configFolderLoc); if(!configFolder.query_exists()) configFolder.make_directory(); var configFileLoc = configFolderLoc.concat("/exclude_list.txt"); File configFile = File.new_for_path(configFileLoc); if(configFile.query_exists()){ //config file exists print("\nREADING CONFIG FILE\n"); FileInputStream @is = configFile.read(); DataInputStream dis = new DataInputStream(@is); string line; print("\nFILE OPENED\n"); while((line = dis.read_line()) != null){ print("\nREAD LINE\n"); configData = configData.concat(line, "\n"); print("\nDONE\n"); } if(configData == ""){ configFile.replace_contents("/dev\n/proc\n/sys\n/tmp\n/run\n/mnt\n/media\n/cdrom\n/lost+found\n\n".data, null, false, FileCreateFlags.NONE, null); configData = "/dev\n/proc\n/sys\n/tmp\n/run\n/mnt\n/media\n/cdrom\n/lost+found\n\n"; } ExcludeList.buffer.text = configData; }else{ //no config file yet configFile.create(FileCreateFlags.PRIVATE); configFile.replace_contents("/dev\n/proc\n/sys\n/tmp\n/run\n/mnt\n/media\n/cdrom\n/lost+found\n\n".data, null, false, FileCreateFlags.NONE, null); configData = "/dev\n/proc\n/sys\n/tmp\n/run\n/mnt\n/media\n/cdrom\n/lost+found\n\n"; ExcludeList.buffer.text = configData; } finbutcr.clicked.connect (() => { if(!ssnap){ print(crloc1.get_filename()); string loc = crloc1.get_filename(); print("\n"); print(crloc1.get_filename()); if (crloc1.get_filename() == null){ var ibl = new Gtk.Dialog.with_buttons("Invaild backup location", this, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT | Gtk.DialogFlags.USE_HEADER_BAR, null); var iblca = ibl.get_content_area(); iblca.add(new Gtk.Label(" ")); iblca.add(new Gtk.Label(" Make sure the folder you picked is mounted and exists ")); iblca.add(new Gtk.Label(" ")); ibl.show_all(); }else{ ssnap=true; createstack.set_visible_child(progresscr); //string isfile; //Process.spawn_command_line_sync("file " + crloc1.get_filename() + "/nostalgia.file", out isfile); //DONE (dumb af that i didnt do this in the first place lazy ahh): Make this use fs syscalls instead of spawning a process //string isft = crloc1.get_filename() + "/nostalgia.file: empty "; File nostalgiaFile = File.new_for_path(crloc1.get_filename().concat("/nostalgia.file")); bool isNostalgiaFile = nostalgiaFile.query_exists(); var lbl = new Gtk.TextView(); lbl.set_wrap_mode (Gtk.WrapMode.WORD); //lbl.set_monospace(true); lbl.override_background_color(Gtk.StateFlags.NORMAL, black_color); var lblScroll = new Gtk.ScrolledWindow(null, null); lblScroll.add(lbl); lblScroll.set_max_content_height(600); progresscr.pack_start(lblScroll); var ProgBar = new Gtk.ProgressBar(); progresscr.pack_end(ProgBar); var ProgLabel = new Gtk.Label("Nostalgia Progress: 0%"); progresscr.pack_end(ProgLabel); if(!isNostalgiaFile){ print("\n root backup \n"); string testx; //Process.spawn_command_line_sync("bash command", out testx); progresscr.pack_start(new Gtk.Label(testx)); //print(testx); progresscr.pack_start(new Gtk.Label("This seems to be your first nostalgia backup on this harddrive, so it will take a while"), false, false, 10); show_all(); } string[] spawn_env1 = Environ.get(); Pid rpid; int otcr; int dum; bool lop=true; ExcludeList.buffer.text = ExcludeList.buffer.text.concat("\n"); configFile.replace_contents(ExcludeList.buffer.text.data, null, false, FileCreateFlags.NONE, null); string[] backup_command_template = {"/usr/bin/pkexec", "/usr/bin/rsync", "-ahv", "/home/ioachim/Music", "/home/ioachim/Music2", "--no-i-r", "--info=progress2", "--fsync", "--exclude-from=".concat(configFileLoc), null}; // --exclude-from="file.txt" EXCLUDES CONTENTS OF FILE EACH ON A LINE //TODO: Replace command with proper one var ChildProcessBackup = new Subprocess.newv(backup_command_template, SubprocessFlags.STDOUT_PIPE); ChildProcessBackup.wait_check_async.begin(null, (obj, res) => { try{ ChildProcessBackup.wait_check_async.end(res); print("\n\n-------SUCCESFULL------\n\n"); ssnap = false; createstack.set_visible_child(cr1); Notify.init("Nostalgia"); var notification = new Notify.Notification("Nostalgia", "Your snapshot is ready", "deja-dup"); notification.set_timeout(0); notification.show(); print("\nREMOVING CHILDREN FROM PAGE\n"); var progresscrChildren = progresscr.get_children(); foreach (Gtk.Widget element in progresscrChildren) progresscr.remove(element); }catch(Error err){ print("\n\n------ERROR!!!------\n%s\n\n", err.message); createstack.set_visible_child(cr1); var ibl = new Gtk.Dialog.with_buttons("Error Spawning Process", this, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT | Gtk.DialogFlags.USE_HEADER_BAR, null); var iblca = ibl.get_content_area(); iblca.add(new Gtk.Label(" ")); iblca.add(new Gtk.Label(" Make sure /usr/bin/rsync is installed, up to date and that you have root privileges and have inputed your correct user password ")); iblca.add(new Gtk.Label(" ")); ibl.show_all(); print("\nREMOVING CHILDREN FROM PAGE\n"); var progresscrChildren = progresscr.get_children(); foreach (Gtk.Widget element in progresscrChildren) progresscr.remove(element); ssnap = false; } }); var OutputPipeBackup = ChildProcessBackup.get_stdout_pipe(); update_label_from_istream.begin(OutputPipeBackup, lbl, ProgBar, ProgLabel); } } }); cr1.pack_end(cr12, false, false, 10); add(actualstack); var headerbar = new MyApp.HeaderBar(actualstack); set_titlebar(headerbar); window_position = Gtk.WindowPosition.CENTER; window_position = Gtk.WindowPosition.CENTER; set_default_size(800, 600); set_resizable(false); show_all(); print("Hey, thanks for choosing nostalgia! \n"); print("If you're looking at the debug output most probably you're having some trouble with this program, so feel free to contact me and I'll see if I can help \n"); print("\n\n"); print("email: [ioachim.radu@protonmail.com]\n"); print("webpage: [ioachim.eu.org]\n"); print("pgp: [C2462D5103FA6059E1E3279C9E9299CD96C65EC6]\n"); print("\n\n"); } } int timer = 0; async void update_label_from_istream (InputStream istream, Gtk.TextView label, Gtk.ProgressBar ProgBar, Gtk.Label ProgLabel) { try { var dis = new DataInputStream (istream); var line = yield dis.read_line_async (); while (line != null) { if(timer == 5){ label.buffer.text = ""; timer = 0; } label.buffer.text += line + "\n"; print(line); print("\n"); var words = line.split(" "); foreach(unowned string word in words){ if(word[word.length - 1] == '%'){ var word_trimmed = word.substring(0, word.length - 1); double procDone = (double)StrToInt(word_trimmed) / (double)100; ProgBar.set_fraction(procDone); ProgBar.set_text(word_trimmed); ProgLabel.set_text(word_trimmed + "%"); print(procDone.to_string() + " "); } } timer++; line = yield dis.read_line_async (); } } catch (Error err) {} } int StrToInt(string str){ int ret = 0; for(int i = 0; i < str.length; i++){ ret *= 10; ret += (str[i] - '0'); //get char code offset to zero of digit } return ret; }