This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch feature/session-save-restore
in repository terminology.
View the commit online.
commit d92862889921d8d2d9aeb89209fd4fbbf832abc8
Author: [email protected] <[email protected]>
AuthorDate: Sun Mar 15 14:51:06 2026 -0600
feat: inject session directly into virgin windows instead of creating a duplicate
When loading a session from a single-terminal window, the user now sees the
restored session replace that window's content in-place. Previously, session
loading would spawn an extra empty shell window alongside the session.
To enable in-place injection:
- Add win_clear_content(Win *wn) to cleanly tear down an existing Solo terminal
in a virgin window, preparing it for session injection. The function disconnects
the term from its container to prevent stale pointer dereferences during cleanup.
- Modify session_load() to check if caller_wn is a virgin single-child window via
win_has_single_child(), and if so, reuse it for the first restored session window
instead of creating a new one.
- Update the hoversel callback in controls.c to pass ctx->wn to session_load(),
enabling automatic in-place injection without extra window management logic.
- Fix four NULL-deref bugs found during review: add NULL guards on term->container
in _cb_bell, _cb_title, _cb_close, and cancel pending wn->size_job in
win_clear_content to prevent assertions.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
---
src/bin/controls.c | 7 +++----
src/bin/session.c | 23 +++++++++++++++++++----
src/bin/win.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++-
src/bin/win.h | 1 +
4 files changed, 70 insertions(+), 9 deletions(-)
diff --git a/src/bin/controls.c b/src/bin/controls.c
index 6510f0b4..9d3f2e49 100644
--- a/src/bin/controls.c
+++ b/src/bin/controls.c
@@ -248,10 +248,9 @@ _cb_session_hoversel_sel(void *data,
if (name)
{
- /* Pass NULL for caller_wn: on catastrophic failure session_load will
- * open a fresh fallback window rather than reusing the current one.
- * This is intentional — the user explicitly chose to load a session. */
- session_load(name, NULL);
+ /* session_load injects directly into ctx->wn when it is a virgin
+ * single-terminal window, so no extra window management is needed. */
+ session_load(name, ctx->wn);
controls_hide(ctx, EINA_TRUE);
}
}
diff --git a/src/bin/session.c b/src/bin/session.c
index cf8a82bc..6cd31645 100644
--- a/src/bin/session.c
+++ b/src/bin/session.c
@@ -876,6 +876,12 @@ session_load(const char *name, Win *caller_wn)
WRN("session_load: file version %d < compiled %d, some data may be missing",
sf->version, SESSION_VERSION);
+ /* If caller_wn is a virgin single-terminal window, inject the first
+ * session window directly into it so the user doesn't get an extra shell
+ * window alongside the restored session. */
+ Win *inject_wn = (caller_wn && win_has_single_child(caller_wn))
+ ? caller_wn : NULL;
+
EINA_LIST_FOREACH(sf->windows, l, win_node)
{
Win *wn;
@@ -885,10 +891,19 @@ session_load(const char *name, Win *caller_wn)
if (win_node->type != SESSION_NODE_WIN) continue;
- wn = win_new(NULL, NULL, NULL, NULL, main_config_get(),
- EINA_FALSE, EINA_FALSE, EINA_FALSE, EINA_FALSE,
- EINA_FALSE);
- if (!wn) continue;
+ if (inject_wn)
+ {
+ wn = inject_wn;
+ win_clear_content(wn);
+ inject_wn = NULL;
+ }
+ else
+ {
+ wn = win_new(NULL, NULL, NULL, NULL, main_config_get(),
+ EINA_FALSE, EINA_FALSE, EINA_FALSE, EINA_FALSE,
+ EINA_FALSE);
+ if (!wn) continue;
+ }
/* Build stub tree from the WIN node's child */
{
diff --git a/src/bin/win.c b/src/bin/win.c
index 11af92b2..cd790351 100644
--- a/src/bin/win.c
+++ b/src/bin/win.c
@@ -1005,6 +1005,47 @@ win_tc_set(Win *wn, Term_Container *tc)
tc_win->swallow(tc_win, NULL, tc);
}
+/* Tear down the existing Solo terminal in a virgin window, leaving it empty
+ * so that session_load can inject a session tree into it. The caller window
+ * must have exactly one Solo child (i.e. win_has_single_child returns true). */
+void
+win_clear_content(Win *wn)
+{
+ Term_Container *tc = wn->child;
+ Solo *solo;
+ Term *t;
+
+ if (!tc || tc->type != TERM_CONTAINER_TYPE_SOLO)
+ return;
+
+ solo = (Solo *)tc;
+ /* Disconnect the term from its container before any deletion so that
+ * callbacks that check term->container don't follow a stale pointer.
+ * _cb_bell, _cb_title, _cb_close guard against NULL container, so
+ * any callbacks that fire during term_unref below will be no-ops. */
+ solo->term->container = NULL;
+
+ /* Cancel any pending size job: _win_size_eval asserts wn->child != NULL,
+ * which would fire if the job ran while the window is empty. */
+ if (wn->size_job)
+ {
+ ecore_job_del(wn->size_job);
+ wn->size_job = NULL;
+ }
+
+ /* Unswallow the Solo's Evas object (term->bg) from the window layout. */
+ elm_layout_content_unset(wn->base, "terminology.content");
+
+ /* Free the Solo container struct. */
+ eina_stringshare_del(tc->title);
+ free(tc);
+ wn->child = NULL;
+
+ /* Free every term in the window (just one for a virgin window). */
+ EINA_LIST_FREE(wn->terms, t)
+ term_unref(t);
+}
+
/* Prime size-hint fields on every term in the window so that
* win_sizing_handle() can compute a valid window geometry.
* win_term_set() does this for a single term; session restore needs it
@@ -5097,6 +5138,7 @@ _cb_close(void *data,
Term *term = data;
Term_Container *tc = term->container;
+ if (!tc) return;
term_close(tc->wn->win, term->termio, EINA_FALSE);
}
@@ -6794,7 +6836,10 @@ _cb_title(void *data,
{
Term *term = data;
Term_Container *tc = term->container;
- const char *title = termio_title_get(term->termio);
+ const char *title;
+
+ if (!tc) return;
+ title = termio_title_get(term->termio);
if (title)
tc->set_title(tc, tc, title);
@@ -7449,6 +7494,7 @@ _cb_bell(void *data,
Term_Container *tc;
tc = term->container;
+ if (!tc) return;
tc->bell(tc, tc);
}
diff --git a/src/bin/win.h b/src/bin/win.h
index 1cb0b213..4728dc6a 100644
--- a/src/bin/win.h
+++ b/src/bin/win.h
@@ -103,6 +103,7 @@ Term_Container *win_tabs_create(Term_Container *first_solo);
void win_tabs_term_append(Term_Container *tabs_tc, Term *term);
void win_tabs_focus_index(Term_Container *tabs_tc, int index);
void win_tc_set(Win *wn, Term_Container *tc);
+void win_clear_content(Win *wn);
void win_refresh_size_hints(Win *wn);
/* Session save helpers — access opaque Win internals without exposing the struct */
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.