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.

Reply via email to