From 4d596dca3e56df1884cda8dcea910778bba38302 Mon Sep 17 00:00:00 2001 From: Jonathan Haylett Date: Sun, 17 Nov 2019 16:05:10 +0000 Subject: [PATCH] Restore only missing programs Previously, when restoring programs, all of the saved programs would be launched regardless of what is already running in that workspace. This commit alters this behaviour such that only programs that are not already open in the workspace will be restored. --- i3_resurrect/layout.py | 1 + i3_resurrect/main.py | 3 +- i3_resurrect/programs.py | 111 +++++++++++++++++++++++---------------- 3 files changed, 70 insertions(+), 45 deletions(-) diff --git a/i3_resurrect/layout.py b/i3_resurrect/layout.py index 6ab06ed..1d39188 100644 --- a/i3_resurrect/layout.py +++ b/i3_resurrect/layout.py @@ -43,6 +43,7 @@ def read(workspace, directory, profile): if profile is not None: filename = f'{profile}_layout.json' layout_file = Path(directory) / filename + layout = None try: layout = json.loads(layout_file.read_text()) diff --git a/i3_resurrect/main.py b/i3_resurrect/main.py index 6369dc2..a954bf0 100644 --- a/i3_resurrect/main.py +++ b/i3_resurrect/main.py @@ -118,7 +118,8 @@ def restore_workspace(workspace, numeric, directory, profile, target): if target != 'layout_only': # Restore programs. - programs.restore(workspace, directory, profile) + saved_programs = programs.read(workspace, directory, profile) + programs.restore(workspace_name, saved_programs) @main.command('ls') diff --git a/i3_resurrect/programs.py b/i3_resurrect/programs.py index 77a0a87..91e45b6 100644 --- a/i3_resurrect/programs.py +++ b/i3_resurrect/programs.py @@ -23,8 +23,6 @@ def save(workspace, numeric, directory, profile): filename = f'{profile}_programs.json' programs_file = Path(directory) / filename - terminals = config.get('terminals', []) - # Print deprecation warning if using old dictionary method of writing # window command mappings. # TODO: Remove in 2.0.0 @@ -34,52 +32,16 @@ def save(workspace, numeric, directory, profile): 'is deprecated and will be removed in favour of the list method ' 'in the next major version.') - # Loop through windows and save commands to launch programs on saved - # workspace. - programs = [] - for (con, pid) in windows_in_workspace(workspace, numeric): - if pid == 0: - continue - - # Get process info for the window. - procinfo = psutil.Process(pid) - - # Create command to launch program. - command = get_window_command( - con['window_properties'], - procinfo.cmdline(), - ) - if command in ([], ''): - continue - - # Remove empty string arguments from command. - command = [arg for arg in command if arg != ''] - - try: - # Obtain working directory using psutil. - if con['window_properties']['class'] in terminals: - # If the program is a terminal emulator, get the working - # directory from its first subprocess. - working_directory = procinfo.children()[0].cwd() - else: - working_directory = procinfo.cwd() - except Exception: - working_directory = str(Path.home()) - - # Add the command to the list. - programs.append({ - 'command': command, - 'working_directory': working_directory - }) + programs = get_programs(workspace, numeric) # Write list of commands to file as JSON. with programs_file.open('w') as f: f.write(json.dumps(programs, indent=2)) -def restore(workspace, directory, profile): +def read(workspace, directory, profile): """ - Restore the running programs from an i3 workspace. + Read saved programs file. """ workspace_id = util.filename_filter(workspace) filename = f'workspace_{workspace_id}_programs.json' @@ -87,7 +49,6 @@ def restore(workspace, directory, profile): filename = f'{profile}_programs.json' programs_file = Path(directory) / filename - # Read saved programs file. programs = None try: programs = json.loads(programs_file.read_text()) @@ -99,8 +60,20 @@ def restore(workspace, directory, profile): util.eprint('Could not find saved programs for workspace ' f'"{workspace}"') sys.exit(1) + return programs + - for entry in programs: +def restore(workspace_name, saved_programs): + """ + Restore the running programs from an i3 workspace. + """ + # Remove already running programs from the list of program to restore. + running_programs = get_programs(workspace_name, False) + for program in running_programs: + saved_programs.remove(program) + + i3 = i3ipc.Connection() + for entry in saved_programs: cmdline = entry['command'] working_directory = entry['working_directory'] @@ -120,10 +93,60 @@ def restore(workspace, directory, profile): command = cmdline # Execute command via i3 exec. - i3 = i3ipc.Connection() i3.command(f'exec cd "{working_directory}" && {command}') +def get_programs(workspace, numeric): + """ + Get running programs in specified workspace. + + Args: + workspace: The workspace to search. + numeric: Identify workspace by number instead of name. + """ + # Loop through windows and save commands to launch programs on saved + # workspace. + programs = [] + for (con, pid) in windows_in_workspace(workspace, numeric): + if pid == 0: + continue + + # Get process info for the window. + procinfo = psutil.Process(pid) + + # Create command to launch program. + command = get_window_command( + con['window_properties'], + procinfo.cmdline(), + ) + if command in ([], ''): + continue + + # Remove empty string arguments from command. + command = [arg for arg in command if arg != ''] + + terminals = config.get('terminals', []) + + try: + # Obtain working directory using psutil. + if con['window_properties']['class'] in terminals: + # If the program is a terminal emulator, get the working + # directory from its first subprocess. + working_directory = procinfo.children()[0].cwd() + else: + working_directory = procinfo.cwd() + except Exception: + working_directory = str(Path.home()) + + # Add the command to the list. + programs.append({ + 'command': command, + 'working_directory': working_directory + }) + + return programs + + def windows_in_workspace(workspace, numeric): """ Generator to iterate over windows in a workspace.