-
Notifications
You must be signed in to change notification settings - Fork 306
Fix SaveLastSessionLayout by adding event-driven hooks #1039
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
henricook
wants to merge
1
commit into
gnome-terminator:master
Choose a base branch
from
henricook:fix/save-last-session-layout-reconnect
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,82 +1,123 @@ | ||
| import os | ||
| import signal | ||
| import sys | ||
| import sys | ||
|
|
||
| # Fix imports when testing this file directly | ||
| if __name__ == '__main__': | ||
| sys.path.append( os.path.join(os.path.dirname(__file__), "../..")) | ||
|
|
||
| from gi.repository import GLib | ||
|
|
||
| from terminatorlib.config import Config | ||
| import terminatorlib.plugin as plugin | ||
| from terminatorlib.util import get_config_dir, err, dbg, gerr | ||
| from terminatorlib.util import dbg | ||
| from terminatorlib.terminator import Terminator | ||
| from terminatorlib import util | ||
|
|
||
|
|
||
| # AVAILABLE must contain a list of all the classes that you want exposed | ||
| AVAILABLE = ['SaveLastSessionLayout'] | ||
|
|
||
|
|
||
| class SaveLastSessionLayout(plugin.Plugin): | ||
| capabilities = ['session'] | ||
| """Plugin to automatically save the current session layout on close. | ||
|
|
||
| config = None | ||
| conf_file = os.path.join(get_config_dir(),"save_last_session_cwd") | ||
| conf_sessions = [] | ||
| emit_close_count = 0 | ||
| This plugin saves the terminal layout (tabs, splits, working directories) | ||
| when Terminator closes, allowing restoration via the Layouts menu. | ||
|
|
||
| The implementation uses event-driven signal connections: | ||
| - Connects to each terminal's 'pre-close-term' signal to trigger saves | ||
| - Connects to each Notebook's 'page-added' signal to catch new tabs | ||
| - Uses deferred setup via GLib.idle_add to avoid blocking the UI | ||
| """ | ||
| capabilities = ['session'] | ||
|
|
||
| def __init__(self): | ||
| dbg("SaveLastSessionLayout Init") | ||
| self.connect_signals() | ||
| self.connected_terminals = set() | ||
| self.connected_notebooks = set() | ||
| self.save_triggered = False | ||
| dbg("SaveLastSessionLayout: init") | ||
| # Defer all setup to avoid blocking the preferences UI | ||
| GLib.idle_add(self.deferred_connect) | ||
|
|
||
| def connect_to_terminal(self, terminal): | ||
| """Connect to a terminal's pre-close-term signal if not already connected.""" | ||
| if terminal not in self.connected_terminals: | ||
| dbg("SaveLastSessionLayout: connecting to terminal %s" % id(terminal)) | ||
| terminal.connect('pre-close-term', self.on_close, None) | ||
| self.connected_terminals.add(terminal) | ||
|
|
||
| def connect_to_notebooks(self): | ||
| """Scan all windows and connect to any new notebooks.""" | ||
| terminator = Terminator() | ||
| for window in terminator.windows: | ||
| child = window.get_child() | ||
| # Only connect to Notebook widgets (they have 'append_page' method) | ||
| if child is not None and hasattr(child, 'append_page'): | ||
| if child not in self.connected_notebooks: | ||
| dbg("SaveLastSessionLayout: connecting to notebook %s" % id(child)) | ||
| child.connect('page-added', self.on_page_added) | ||
| self.connected_notebooks.add(child) | ||
|
|
||
| def on_page_added(self, notebook, child, page_num): | ||
| """Handle new page added to notebook - connect to any new terminals.""" | ||
| dbg("SaveLastSessionLayout: page added to notebook") | ||
| # New tabs are usually single terminals - connect directly if possible | ||
| if hasattr(child, 'connect') and child not in self.connected_terminals: | ||
| try: | ||
| child.connect('pre-close-term', self.on_close, None) | ||
| self.connected_terminals.add(child) | ||
| except TypeError: | ||
| # Not a terminal widget, ignore | ||
| pass | ||
|
|
||
| def deferred_connect(self): | ||
| """Deferred setup - runs once after startup completes via idle_add.""" | ||
| dbg("SaveLastSessionLayout: deferred connect running") | ||
|
|
||
| # OS signal handlers for shutdown scenarios | ||
| signal.signal(signal.SIGTERM, self.signal_handler) | ||
| signal.signal(signal.SIGHUP, self.signal_handler) | ||
|
|
||
| #not used, but capability can be used to load automatically | ||
| def load_session_layout(self, debugtab=False, widget=None, cwd=None, metadata=None, profile=None): | ||
| dbg("SaveLastSessionLayout load layout") | ||
| terminator = Terminator() | ||
| util.spawn_new_terminator(terminator.origcwd, ['-u', '-l', 'SaveLastSessionLayout']) | ||
| terminator = Terminator() | ||
|
|
||
| def save_session_layout(self, debugtab=False, widget=None, cwd=None, metadata=None, profile=None): | ||
| # Connect to all existing terminals | ||
| for terminal in terminator.terminals: | ||
| self.connect_to_terminal(terminal) | ||
|
|
||
| config = Config() | ||
| terminator = Terminator() | ||
| current_layout = terminator.describe_layout(save_cwd = True) | ||
| dbg("SaveLastSessionLayout: save layout(%s)" % current_layout) | ||
| res = config.replace_layout("SaveLastSessionLayout", current_layout) | ||
| if (not res): | ||
| r = config.add_layout("SaveLastSessionLayout", current_layout) | ||
| config.save() | ||
| return True | ||
| # Connect to all existing notebooks for new tab detection | ||
| self.connect_to_notebooks() | ||
|
|
||
| def signal_handler(self,signum, frame): | ||
| return False # Don't repeat - run only once | ||
|
|
||
| # Not used, but capability can be used to load automatically | ||
| def load_session_layout(self, debugtab=False, widget=None, cwd=None, metadata=None, profile=None): | ||
| """Load the saved session layout in a new Terminator instance.""" | ||
| dbg("SaveLastSessionLayout: load layout") | ||
| terminator = Terminator() | ||
| util.spawn_new_terminator(terminator.origcwd, ['-u', '-l', 'SaveLastSessionLayout']) | ||
|
|
||
| def save_session_layout(self): | ||
| """Save the current layout to config.""" | ||
| config = Config() | ||
| terminator = Terminator() | ||
| current_layout = terminator.describe_layout(save_cwd=True) | ||
| dbg("SaveLastSessionLayout: save layout(%s)" % current_layout) | ||
| res = config.replace_layout("SaveLastSessionLayout", current_layout) | ||
| if not res: | ||
| config.add_layout("SaveLastSessionLayout", current_layout) | ||
| config.save() | ||
| return True | ||
|
|
||
| def signal_handler(self, signum, frame): | ||
| """Handle OS signals (SIGTERM, SIGHUP).""" | ||
| signame = signal.Signals(signum).name | ||
| dbg('signal handler called:signal %s (%s)' % | ||
| (signame, signum)) | ||
| dbg('SaveLastSessionLayout: signal handler called: %s (%s)' % (signame, signum)) | ||
| self.save_session_layout() | ||
|
|
||
| def connect_signals(self): | ||
| dbg("SaveLastSessionLayout connect_signals") | ||
|
|
||
| signal.signal(signal.SIGTERM, self.signal_handler) | ||
| signal.signal(signal.SIGCHLD, self.signal_handler) | ||
|
henricook marked this conversation as resolved.
|
||
| signal.signal(signal.SIGHUP, self.signal_handler) | ||
|
|
||
| n = 0 | ||
| for term in Terminator().terminals: | ||
| dbg("SaveLastSessionLayout connect_signals to term num:(%d)" % n) | ||
| n = n + 1 | ||
| # event close-term works, and does not require an additional | ||
| # event but has a race condition when | ||
| # there is only one terminal we are unable to get the | ||
| # describe_layout section | ||
|
|
||
| #term.connect('close-term', self.close, None) | ||
| term.connect('pre-close-term', self.close, None) | ||
|
|
||
| #Can connect signal from terminal | ||
| #term.connect('load-layout', self.load_session_layout, None) | ||
|
|
||
| def close(self, term, event, arg1 = None): | ||
| if (self.emit_close_count == 0): | ||
| self.emit_close_count = self.emit_close_count + 1 | ||
| self.save_session_layout("", "") | ||
|
|
||
| def on_close(self, terminal, event, arg1=None): | ||
| """Handle terminal pre-close-term signal.""" | ||
| if not self.save_triggered: | ||
| self.save_triggered = True | ||
| dbg("SaveLastSessionLayout: terminal closing, saving layout") | ||
| self.save_session_layout() | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.