This is an automated email from the git hooks/post-receive script.

git pushed a commit to branch tytab
in repository terminology.

View the commit online.

commit a3c988c0ed80e8845baf526f18a52a15639f6e07
Author: [email protected] <[email protected]>
AuthorDate: Wed Jun 3 20:41:41 2026 -0600

    feat: add tytab tab and split management over IPC
    
    Drive tab and split creation in a running terminology from child
    processes over the local IPC socket.
    
    - Assign each terminal a unique id and export it as $TERMINOLOGY_ID so
      a client can name the terminal it lives in.
    - Serve one IPC endpoint per instance: the session hash in shared
      (single-process) mode, or hash+pid when running as independent
      processes, and export the bound endpoint as $TERMINOLOGY_IPC so a
      client always reaches the instance that spawned it.
    - Handle tab requests in the IPC server via tab_new_from_ipc(), which
      resolves the terminal by id, enforces the tytab config gate and the
      per-terminal freeze latch, and creates the tab/split.
    - Add the in-band escape commands for naming a panel (tn) and freezing
      tytab in a terminal (tf).
    - Add the tytab config field (default off).
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
---
 src/bin/config.c         |   5 +
 src/bin/config.h         |   1 +
 src/bin/ipc.c            | 121 +++++++++++------
 src/bin/ipc.h            |   2 +-
 src/bin/main.c           |   4 +-
 src/bin/term_container.h |   1 +
 src/bin/termio.c         |   2 +-
 src/bin/termpty.c        |  10 +-
 src/bin/termpty.h        |   2 +-
 src/bin/win.c            | 333 ++++++++++++++++++++++++++++++++++++++++++++---
 src/bin/win.h            |   6 +
 11 files changed, 425 insertions(+), 62 deletions(-)

diff --git a/src/bin/config.c b/src/bin/config.c
index f974bed7..5ad94938 100644
--- a/src/bin/config.c
+++ b/src/bin/config.c
@@ -165,6 +165,8 @@ config_init(void)
      (edd_base, Config, "drag_links", drag_links, EET_T_UCHAR);
    EET_DATA_DESCRIPTOR_ADD_BASIC
      (edd_base, Config, "login_shell", login_shell, EET_T_UCHAR);
+   EET_DATA_DESCRIPTOR_ADD_BASIC
+     (edd_base, Config, "tytab", tytab, EET_T_UCHAR);
    EET_DATA_DESCRIPTOR_ADD_BASIC
      (edd_base, Config, "mouse_over_focus",
       mouse_over_focus, EET_T_UCHAR);
@@ -311,6 +313,7 @@ config_sync(const Config *config_src, Config *config)
    config->temporary = config_src->temporary;
    config->custom_geometry = config_src->custom_geometry;
    config->login_shell = config_src->login_shell;
+   config->tytab = config_src->tytab;
    config->cg_width = config_src->cg_width;
    config->cg_height = config_src->cg_height;
    config->colors_use = config_src->colors_use;
@@ -595,6 +598,7 @@ config_new(void)
 #else
         config->login_shell = EINA_FALSE;
 #endif
+        config->tytab = EINA_FALSE;
         config->mouse_over_focus = EINA_TRUE;
         config->disable_focus_visuals = EINA_FALSE;
         config->colors_use = EINA_FALSE;
@@ -917,6 +921,7 @@ config_fork(const Config *config)
    CPY(erase_is_del);
    CPY(custom_geometry);
    CPY(login_shell);
+   CPY(tytab);
    CPY(cg_width);
    CPY(cg_height);
    CPY(colors_use);
diff --git a/src/bin/config.h b/src/bin/config.h
index a3218d60..61fc513d 100644
--- a/src/bin/config.h
+++ b/src/bin/config.h
@@ -92,6 +92,7 @@ struct tag_Config
    Eina_Bool         custom_geometry;
    Eina_Bool         drag_links;
    Eina_Bool         login_shell;
+   Eina_Bool         tytab;
    Eina_Bool         mouse_over_focus;
    Eina_Bool         disable_focus_visuals;
    Eina_Bool         colors_use;
diff --git a/src/bin/ipc.c b/src/bin/ipc.c
index 6cad2b9f..336a1b51 100644
--- a/src/bin/ipc.c
+++ b/src/bin/ipc.c
@@ -1,15 +1,23 @@
 #include "private.h"
 
+#include <unistd.h>
 #include <Ecore.h>
 #include <Ecore_Con.h>
 #include <Ecore_Ipc.h>
 #include <Eet.h>
 #include "ipc.h"
+#include "tytab_ipc.h"
+
+/* forward declaration to avoid pulling in win.h (which requires Elementary headers) */
+Eina_Bool tab_new_from_ipc(unsigned int term_id, const char *name,
+                           const char *direction, const char *cmd,
+                           const char **error);
 
 #define TY_IPC_MAJOR  3
 #define TY_IPC_MINOR  8
 
 static Ecore_Ipc_Server *ipc = NULL;
+static char *ipc_name = NULL;
 static Ecore_Event_Handler *hnd_data = NULL;
 static void (*func_new_inst) (Ipc_Instance *inst) = NULL;
 static Eet_Data_Descriptor *new_inst_edd = NULL;
@@ -38,47 +46,54 @@ _ipc_cb_client_data(void *_data EINA_UNUSED,
              free(inst);
           }
      }
+   else if ((e->major == TY_IPC_MAJOR_TAB) &&
+            (e->minor == TY_IPC_MINOR_TAB) &&
+            (e->data) && (e->size > 0))
+     {
+        Ipc_Tab_Request *req;
+
+        req = eet_data_descriptor_decode(tytab_ipc_req_edd_get(),
+                                           e->data, e->size);
+        if (req)
+          {
+             Ipc_Tab_Reply rep = { .ok = 0, .error = NULL };
+             const char *err = "rejected";
+             Eina_Bool ok;
+             void *rep_data;
+             int rep_size = 0;
+
+             ok = tab_new_from_ipc(req->term_id, req->name,
+                                   req->direction, req->cmd, &err);
+             if (ok)
+               {
+                  rep.ok = 1;
+                  rep.error = NULL;
+               }
+             else
+               {
+                  rep.error = (char *)err;
+               }
+
+             rep_data = eet_data_descriptor_encode(
+                            tytab_ipc_rep_edd_get(), &rep, &rep_size);
+             if (rep_data)
+               {
+                  ecore_ipc_client_send(e->client,
+                                        TY_IPC_MAJOR_TAB, TY_IPC_MINOR_TAB,
+                                        0, 0, 0, rep_data, rep_size);
+                  ecore_ipc_client_flush(e->client);
+                  free(rep_data);
+               }
+             free(req);
+          }
+     }
    return ECORE_CALLBACK_PASS_ON;
 }
 
 static char *
 _ipc_hash_get(void)
 {
-   char buf[1024], hash[64] = {};
-   const char *disp, *session, *xdg_session, *xdg_id, *xdg_seat, *xdg_vt;
-   char *s;
-   unsigned int i;
-
-   /* dumb stoopid hash - i'm feeling lazy */
-   disp = getenv("DISPLAY");
-   if (!disp) disp = "-unknown-";
-   session = getenv("DBUS_SESSION_BUS_ADDRESS");
-   if (!session) session = ":unknown:";
-   xdg_session = getenv("XDG_SESSION_COOKIE");
-   if (!xdg_session) xdg_session = "/unknown/";
-   xdg_id = getenv("XDG_SESSION_ID");
-   if (!xdg_id) xdg_id = "=unknown=";
-   xdg_seat = getenv("XDG_SEAT");
-   if (!xdg_seat) xdg_seat = "@unknown@";
-   xdg_vt = getenv("XDG_VTNR");
-   if (!xdg_vt) xdg_vt = "!unknown!";
-   snprintf(buf, sizeof(buf), "%s.%s.%s.%s.%s.%s",
-            disp, session, xdg_session,
-            xdg_id, xdg_seat, xdg_vt);
-   memcpy(hash, "terminology-", 12);
-   memset(hash+12, 'x', 32);
-   for (i = 0, s = buf; *s; s++)
-     {
-        unsigned char c1, c2;
-
-        c1 = (((unsigned char)*s) >> 4) & 0xf;
-        c2 = ((unsigned char)*s) & 0x0f;
-        hash[12 + (i % 32)] = (((hash[12 + (i % 32)] - 'a') ^ c1) % 26) + 'a';
-        i++;
-        hash[12 + (i % 32)] = (((hash[12 + (i % 32)] - 'a') ^ c2) % 26) + 'a';
-        i++;
-     }
-   return strdup(hash);
+   return tytab_ipc_hash_get();
 }
 
 void
@@ -152,29 +167,57 @@ ipc_init(void)
                                  "cursor_blink", active_links, EET_T_INT);
    EET_DATA_DESCRIPTOR_ADD_BASIC(new_inst_edd, Ipc_Instance,
                                  "visual_bell", active_links, EET_T_INT);
+   tytab_ipc_eet_init();
 }
 
 Eina_Bool
-ipc_serve(void)
+ipc_serve(Eina_Bool unique)
 {
    char *hash = _ipc_hash_get();
+   char buf[256];
+
    if (!hash) return EINA_FALSE;
-   ipc = ecore_ipc_server_add(ECORE_IPC_LOCAL_USER, hash, 0, NULL);
-   free(hash);
-   if (!ipc) return EINA_FALSE;
+
+   if (unique)
+     {
+        snprintf(buf, sizeof(buf), "%s-%d", hash, (int)getpid());
+        free(hash);
+        ipc_name = strdup(buf);
+     }
+   else
+     {
+        ipc_name = hash; /* take ownership */
+     }
+   if (!ipc_name) return EINA_FALSE;
+
+   ipc = ecore_ipc_server_add(ECORE_IPC_LOCAL_USER, ipc_name, 0, NULL);
+   if (!ipc)
+     {
+        free(ipc_name);
+        ipc_name = NULL;
+        return EINA_FALSE;
+     }
    hnd_data = ecore_event_handler_add
      (ECORE_IPC_EVENT_CLIENT_DATA, _ipc_cb_client_data, NULL);
+
+   /* advertise the endpoint to child processes so tytab can reach THIS
+    * instance regardless of single/multi-instance mode */
+   setenv("TERMINOLOGY_IPC", ipc_name, 1);
+
    return EINA_TRUE;
 }
 
 void
 ipc_shutdown(void)
 {
+   tytab_ipc_eet_shutdown();
    if (ipc)
      {
         ecore_ipc_server_del(ipc);
         ipc = NULL;
      }
+   free(ipc_name);
+   ipc_name = NULL;
    if (new_inst_edd)
      {
         eet_data_descriptor_free(new_inst_edd);
diff --git a/src/bin/ipc.h b/src/bin/ipc.h
index f9b43305..d9c53913 100644
--- a/src/bin/ipc.h
+++ b/src/bin/ipc.h
@@ -40,7 +40,7 @@ struct tag_Ipc_Instance
 
 void ipc_init(void);
 void ipc_shutdown(void);
-Eina_Bool ipc_serve(void);
+Eina_Bool ipc_serve(Eina_Bool unique);
 void ipc_instance_new_func_set(void (*func) (Ipc_Instance *inst));
 Eina_Bool ipc_instance_add(Ipc_Instance *inst);
 void ipc_instance_conn_free(void);
diff --git a/src/bin/main.c b/src/bin/main.c
index 5384e26c..54cc96e4 100644
--- a/src/bin/main.c
+++ b/src/bin/main.c
@@ -785,7 +785,7 @@ _start_multi(Ipc_Instance *instance,
         /* Could not start a new window remotely,
          * let's start our own server */
         ipc_instance_new_func_set(main_ipc_new);
-        if (ipc_serve())
+        if (ipc_serve(EINA_FALSE))
           {
              goto normal_start;
           }
@@ -1064,6 +1064,8 @@ elm_main(int argc, char **argv)
      }
    else
      {
+        ipc_instance_new_func_set(main_ipc_new);
+        ipc_serve(EINA_TRUE);
         _start(&instance, need_scale_wizard);
      }
    elm_run();
diff --git a/src/bin/term_container.h b/src/bin/term_container.h
index 08b15134..29e24d5d 100644
--- a/src/bin/term_container.h
+++ b/src/bin/term_container.h
@@ -43,6 +43,7 @@ struct tag_Term_Container {
      Evas_Object *selector_img;
      Eina_Bool is_focused;
      const char *title;
+     Eina_Stringshare *name; /* optional panel name for escape sequence targeting */
 
      Term *(*term_next)(const Term_Container *tc, const Term_Container *child);
      Term *(*term_prev)(const Term_Container *tc, const Term_Container *child);
diff --git a/src/bin/termio.c b/src/bin/termio.c
index 77bca231..e6151007 100644
--- a/src/bin/termio.c
+++ b/src/bin/termio.c
@@ -4329,7 +4329,7 @@ termio_add(Evas_Object *win, Config *config,
 
    window_id = elm_win_window_id_get(win);
    sd->pty = termpty_new(cmd, login_shell, cd, w, h, config, title,
-                         window_id);
+                         window_id, term_id_get(term));
    if (!sd->pty)
      {
         ERR(_("Could not allocate termpty"));
diff --git a/src/bin/termpty.c b/src/bin/termpty.c
index d4ab1e38..f70bd226 100644
--- a/src/bin/termpty.c
+++ b/src/bin/termpty.c
@@ -535,7 +535,7 @@ _is_shell_valid(const char *cmd)
 Termpty *
 termpty_new(const char *cmd, Eina_Bool login_shell, const char *cd,
             int w, int h, Config *config, const char *title,
-            Ecore_Window window_id)
+            Ecore_Window window_id, uint32_t term_id)
 {
    Termpty *ty;
    const char *pty;
@@ -789,6 +789,14 @@ termpty_new(const char *cmd, Eina_Bool login_shell, const char *cd,
              snprintf(buf, sizeof(buf), "WINDOWID=%lu", (unsigned long)window_id);
              putenv(buf);
           }
+        if (term_id)
+          {
+             char buf[256];
+
+             snprintf(buf, sizeof(buf), "TERMINOLOGY_ID=%u",
+                      (unsigned int)term_id);
+             putenv(strdup(buf));
+          }
 #if ((EFL_VERSION_MAJOR > 1) || (EFL_VERSION_MINOR >= 24)) || ((EFL_VERSION_MAJOR == 1) && (EFL_VERSION_MINOR == 23) && (EFL_VERSION_MICRO == 99))
         eina_file_close_from(3, NULL);
 #endif
diff --git a/src/bin/termpty.h b/src/bin/termpty.h
index c24a25b7..a6732c04 100644
--- a/src/bin/termpty.h
+++ b/src/bin/termpty.h
@@ -298,7 +298,7 @@ void       termpty_shutdown(void);
 
 Termpty   *termpty_new(const char *cmd, Eina_Bool login_shell, const char *cd,
                        int w, int h, Config *config, const char *title,
-                       Ecore_Window window_id);
+                       Ecore_Window window_id, uint32_t term_id);
 void       termpty_free(Termpty *ty);
 void       termpty_sync_output_reset(Termpty *ty);
 /* private — called from termptyesc.c _sync_output_end only */
diff --git a/src/bin/win.c b/src/bin/win.c
index 808bf06d..e8e21079 100644
--- a/src/bin/win.c
+++ b/src/bin/win.c
@@ -1,6 +1,9 @@
 #include "private.h"
 
 #include <assert.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <unistd.h>
 #include <Elementary.h>
 #include <Elementary_Cursor.h>
 #include <Ecore_Input.h>
@@ -140,6 +143,8 @@ struct tag_Term
    struct {
       int       x, y;
    } down;
+   Eina_Bool tytab_frozen; /* one-way latch: once set, tytab disabled forever */
+   uint32_t term_id; /* unique ID for IPC tab-creation routing */
    int refcnt;
    unsigned char hold : 1;
    unsigned char unswallowed : 1;
@@ -222,6 +227,7 @@ static Tab_Drag *_tab_drag = NULL;
 
 static Eina_Bool _win_is_focused(Win *wn);
 static Term_Container *_solo_new(Term *term, Win *wn);
+static Term_Container *_term_new_solo(Win *wn, const char *cmd, const char *wdir);
 static Term_Container *_split_new(Term_Container *tc1, Term_Container *tc2,
                                   double left_size, Eina_Bool is_horizontal);
 static Term_Container *_tabs_new(Term_Container *child, Term_Container *parent);
@@ -527,6 +533,7 @@ _solo_close(Term_Container *tc,
    tc->parent->close(tc->parent, tc);
 
    eina_stringshare_del(tc->title);
+   eina_stringshare_del(tc->name);
 
    term = solo->term;
    term->container = NULL;
@@ -1387,6 +1394,7 @@ _win_close(Term_Container *tc,
         return;
      }
    eina_stringshare_del(tc->title);
+   eina_stringshare_del(tc->name);
    win_free(wn);
 }
 
@@ -1528,7 +1536,7 @@ _win_split(Term_Container *tc, Term_Container *child,
 
    if (_term_container_is_splittable(tc, is_horizontal))
      {
-        Term *tm_new, *tm;
+        Term *tm;
         Term_Container *tc_split, *tc_solo_new;
         char *wdir = NULL;
         char buf[PATH_MAX];
@@ -1544,11 +1552,7 @@ _win_split(Term_Container *tc, Term_Container *child,
              if (tm && termio_cwd_get(tm->termio, buf, sizeof(buf)))
                wdir = buf;
           }
-        tm_new = term_new(wn, wn->config,
-                          cmd, wn->config->login_shell, wdir,
-                          80, 24, EINA_FALSE, NULL);
-        tc_solo_new = _solo_new(tm_new, wn);
-        evas_object_data_set(tm_new->termio, "sizedone", tm_new->termio);
+        tc_solo_new = _term_new_solo(wn, cmd, wdir);
 
         elm_layout_content_unset(wn->base, "terminology.content");
 
@@ -2771,6 +2775,7 @@ _split_close(Term_Container *tc, Term_Container *child)
    evas_object_del(split->panes);
 
    eina_stringshare_del(tc->title);
+   eina_stringshare_del(tc->name);
    free(tc);
 }
 
@@ -2883,7 +2888,7 @@ _split_split(Term_Container *tc, Term_Container *child,
 {
    Split *split;
    Win *wn;
-   Term *tm_new, *tm;
+   Term *tm;
    char *wdir = NULL;
    char buf[PATH_MAX];
    Term_Container *tc_split, *tc_solo_new;
@@ -2908,11 +2913,7 @@ _split_split(Term_Container *tc, Term_Container *child,
         if (tm && termio_cwd_get(tm->termio, buf, sizeof(buf)))
           wdir = buf;
      }
-   tm_new = term_new(wn, wn->config,
-                     cmd, wn->config->login_shell, wdir,
-                     80, 24, EINA_FALSE, NULL);
-   tc_solo_new = _solo_new(tm_new, wn);
-   evas_object_data_set(tm_new->termio, "sizedone", tm_new->termio);
+   tc_solo_new = _term_new_solo(wn, cmd, wdir);
 
    if (child == split->tc1)
      elm_object_part_content_unset(split->panes, PANES_TOP);
@@ -4706,6 +4707,7 @@ _tabs_close(Term_Container *tc, Term_Container *child)
           _solo_tab_show(next_child);
 
         eina_stringshare_del(tc->title);
+        eina_stringshare_del(tc->name);
 
         tc_parent->swallow(tc_parent, tc, next_child);
         if (tc->is_focused)
@@ -5025,7 +5027,6 @@ _tab_new_cb(void *data,
    Tabs *tabs = data;
    Term_Container *tc = (Term_Container*) tabs,
                   *tc_new;
-   Term *tm_new;
    Win *wn = tc->wn;
    char *wdir = NULL;
    char buf[PATH_MAX];
@@ -5042,11 +5043,7 @@ _tab_new_cb(void *data,
           wdir = buf;
      }
 
-   tm_new = term_new(wn, wn->config,
-                     NULL, wn->config->login_shell, wdir,
-                     80, 24, EINA_FALSE, NULL);
-   tc_new = _solo_new(tm_new, wn);
-   evas_object_data_set(tm_new->termio, "sizedone", tm_new->termio);
+   tc_new = _term_new_solo(wn, NULL, wdir);
 
    _tabs_attach(tc, tc_new);
 }
@@ -6532,6 +6529,258 @@ _sendfile_request(Term *term, const char *path)
    elm_object_focus_set(o, EINA_TRUE);
 }
 
+static Term *
+_term_find_by_id(uint32_t term_id)
+{
+   Eina_List *l, *ll;
+   Win *wn;
+   Term *term;
+
+   EINA_LIST_FOREACH(wins, l, wn)
+     {
+        EINA_LIST_FOREACH(wn->terms, ll, term)
+          {
+             if (term->term_id == term_id)
+               return term;
+          }
+     }
+   return NULL;
+}
+
+static Term_Container *
+_container_find_by_name(Term_Container *tc, Eina_Stringshare *name)
+{
+   if (!tc || !name)
+     return NULL;
+   if (tc->name == name)
+     return tc;
+
+   switch (tc->type)
+     {
+      case TERM_CONTAINER_TYPE_SOLO:
+        return NULL;
+      case TERM_CONTAINER_TYPE_WIN:
+          {
+             Win *wn = (Win *)tc;
+             return _container_find_by_name((Term_Container *)wn->child, name);
+          }
+      case TERM_CONTAINER_TYPE_SPLIT:
+          {
+             Split *sp = (Split *)tc;
+             Term_Container *found;
+             found = _container_find_by_name((Term_Container *)sp->tc1, name);
+             if (found) return found;
+             return _container_find_by_name((Term_Container *)sp->tc2, name);
+          }
+      case TERM_CONTAINER_TYPE_TABS:
+          {
+             Tabs *tabs = (Tabs *)tc;
+             Eina_List *l;
+             Tab_Item *tab;
+             EINA_LIST_FOREACH(tabs->tabs, l, tab)
+               {
+                  Term_Container *found;
+                  found = _container_find_by_name(tab->tc, name);
+                  if (found) return found;
+               }
+             return NULL;
+          }
+      default:
+        return NULL;
+     }
+}
+
+static uint32_t
+_term_id_generate(void)
+{
+   uint32_t id;
+   int fd;
+
+   fd = open("/dev/urandom", O_RDONLY);
+   if (fd < 0) return 0;
+
+   do
+     {
+        if (read(fd, &id, sizeof(id)) != sizeof(id))
+          {
+             close(fd);
+             return 0;
+          }
+     }
+   while (id == 0);
+
+   close(fd);
+   return id;
+}
+
+static Term_Container *
+_term_new_solo(Win *wn, const char *cmd, const char *wdir)
+{
+   Term *tm;
+   Term_Container *tc;
+
+   tm = term_new(wn, wn->config, cmd, wn->config->login_shell,
+                 wdir, 80, 24, EINA_FALSE, NULL);
+   if (!tm) return NULL;
+   tc = _solo_new(tm, wn);
+   evas_object_data_set(tm->termio, "sizedone", tm->termio);
+   return tc;
+}
+
+static Term_Container *
+_container_tab_attach_point(Term_Container *target)
+{
+   if (target->type == TERM_CONTAINER_TYPE_SOLO)
+     {
+        if (target->parent->type != TERM_CONTAINER_TYPE_TABS)
+          _tabs_new(target, target->parent);
+        return target->parent;
+     }
+   else if (target->type == TERM_CONTAINER_TYPE_TABS)
+     {
+        return target;
+     }
+   else
+     {
+        Term *tm_first = target->term_first(target);
+        if (tm_first)
+          {
+             Term_Container *tc_first = tm_first->container;
+             if (tc_first->parent->type != TERM_CONTAINER_TYPE_TABS)
+               _tabs_new(tc_first, tc_first->parent);
+             return tc_first->parent;
+          }
+     }
+   return NULL;
+}
+
+/* Shared tab/split creation used by the IPC entry point.
+ * `term` is the requesting terminal; CWD is inherited from it.
+ * name == panel to target (may be NULL), direction == 'l'/'r'/'t'/'b' or 0. */
+static void
+_tab_create(Term *term, const char *name_str, char direction, const char *cmd_str)
+{
+   Term_Container *tc = term->container;
+   Win *wn = tc->wn;
+   char buf[PATH_MAX];
+   char *wdir = NULL;
+   Eina_Stringshare *name = NULL;
+
+   if (termio_cwd_get(term->termio, buf, sizeof(buf)))
+     wdir = buf;
+
+   if (name_str && name_str[0])
+     name = eina_stringshare_add(name_str);
+
+   if (name && direction)
+     {
+        Term_Container *target;
+        target = _container_find_by_name((Term_Container *)wn, name);
+        if (target)
+          {
+             Term_Container *tc_attach;
+             tc_attach = _container_tab_attach_point(target);
+             if (tc_attach)
+               {
+                  Term_Container *tc_new;
+                  tc_new = _term_new_solo(wn, cmd_str, wdir);
+                  if (tc_new)
+                    _tabs_attach(tc_attach, tc_new);
+               }
+          }
+        else
+          {
+             Split_Direction dir;
+             Term_Container *tc_new;
+
+             switch (direction)
+               {
+                case 'l': dir = SPLIT_DIRECTION_LEFT; break;
+                case 'r': dir = SPLIT_DIRECTION_RIGHT; break;
+                case 't': dir = SPLIT_DIRECTION_TOP; break;
+                case 'b': dir = SPLIT_DIRECTION_BOTTOM; break;
+                default: goto done;
+               }
+
+             if (tc->type != TERM_CONTAINER_TYPE_SOLO)
+               goto done;
+
+             tc_new = _term_new_solo(wn, cmd_str, wdir);
+             if (tc_new)
+               {
+                  tc->parent->split_direction(tc->parent, tc, tc_new, dir);
+                  eina_stringshare_replace(&tc_new->name, name);
+               }
+          }
+     }
+   else if (name && !direction)
+     {
+        Term_Container *target;
+        target = _container_find_by_name((Term_Container *)wn, name);
+        if (target)
+          {
+             Term_Container *tc_attach;
+             tc_attach = _container_tab_attach_point(target);
+             if (tc_attach)
+               {
+                  Term_Container *tc_new;
+                  tc_new = _term_new_solo(wn, cmd_str, wdir);
+                  if (tc_new)
+                    _tabs_attach(tc_attach, tc_new);
+               }
+          }
+     }
+   else
+     {
+        Term_Container *tc_new;
+
+        if (!tc->parent) goto done;
+
+        if (tc->parent->type != TERM_CONTAINER_TYPE_TABS)
+          _tabs_new(tc, tc->parent);
+
+        tc_new = _term_new_solo(wn, cmd_str, wdir);
+        if (tc_new)
+          _tabs_attach(tc->parent, tc_new);
+     }
+done:
+   eina_stringshare_del(name);
+}
+
+Eina_Bool
+tab_new_from_ipc(unsigned int term_id, const char *name,
+                 const char *direction, const char *cmd,
+                 const char **error)
+{
+   Term *term;
+   Config *config;
+
+   term = _term_find_by_id(term_id);
+   if (!term)
+     {
+        if (error) *error = "unknown terminal";
+        return EINA_FALSE;
+     }
+
+   config = termio_config_get(term->termio);
+   if (!config || !config->tytab)
+     {
+        if (error) *error = "tytab is disabled";
+        return EINA_FALSE;
+     }
+   if (term->tytab_frozen)
+     {
+        if (error) *error = "tytab is frozen in this terminal";
+        return EINA_FALSE;
+     }
+
+   _tab_create(term,
+               (name && name[0]) ? name : NULL,
+               (direction && direction[0]) ? direction[0] : 0,
+               (cmd && cmd[0]) ? cmd : NULL);
+   return EINA_TRUE;
+}
+
 static void
 _cb_command(void *data,
             Evas_Object *_obj EINA_UNUSED,
@@ -6605,6 +6854,46 @@ _cb_command(void *data,
           {
           }
      }
+   else if (cmd[0] == 't') // tab/split escape commands
+     {
+        if (cmd[1] == 'f') // freeze: always accepted, bypasses config
+          {
+             term->tytab_frozen = EINA_TRUE;
+             return;
+          }
+
+        Config *config_tc = termio_config_get(term->termio);
+        if (!config_tc || !config_tc->tytab)
+          return;
+        if (term->tytab_frozen)
+          return;
+
+        if (cmd[1] == 'n') // name panel: tn;NAME
+          {
+             Term_Container *tc = term->container;
+             const char *name_str = (cmd[2] == ';') ? cmd + 3 : NULL;
+
+             if (name_str && name_str[0])
+               {
+                  Eina_Stringshare *name = eina_stringshare_add(name_str);
+                  Term_Container *existing;
+
+                  /* Steal: remove name from any existing container */
+                  existing = _container_find_by_name(
+                     (Term_Container *)tc->wn, name);
+                  if (existing)
+                    eina_stringshare_replace(&existing->name, NULL);
+
+                  eina_stringshare_replace(&tc->name, name);
+                  eina_stringshare_del(name);
+               }
+             else
+               {
+                  /* Empty name: clear */
+                  eina_stringshare_replace(&tc->name, NULL);
+               }
+          }
+     }
 }
 
 static void
@@ -7306,6 +7595,13 @@ term_termio_get(const Term *term)
    return term->termio;
 }
 
+uint32_t
+term_id_get(const Term *term)
+{
+   if (!term) return 0;
+   return term->term_id;
+}
+
 Evas_Object *
 term_miniview_get(const Term *term)
 {
@@ -7409,6 +7705,7 @@ term_new(Win *wn, Config *config, const char *cmd,
    term->wn = wn;
    term->hold = hold;
    term->config = config;
+   term->term_id = _term_id_generate();
 
    term->core = o = elm_layout_add(wn->win);
    theme_apply(o, term->config, "terminology/core", NULL, NULL, EINA_TRUE);
diff --git a/src/bin/win.h b/src/bin/win.h
index bac95987..e0538527 100644
--- a/src/bin/win.h
+++ b/src/bin/win.h
@@ -2,6 +2,7 @@
 #define TERMINOLOGY_WIN_H_ 1
 
 #include "config.h"
+#include <stdint.h>
 
 typedef struct tag_Win   Win;
 typedef struct tag_Term  Term;
@@ -74,6 +75,7 @@ term_imf_context_get(Term *term);
 
 Eina_Bool term_is_visible(const Term *term);
 Eina_Bool term_is_focused(const Term *term);
+uint32_t  term_id_get(const Term *term);
 
 void win_font_size_set(Win *wn, int new_size);
 void win_font_update(Term *term);
@@ -90,4 +92,8 @@ for_each_term_do(Win *wn, For_Each_Term cb, void *data);
 
 void main_trans_update(void);
 
+Eina_Bool tab_new_from_ipc(unsigned int term_id, const char *name,
+                           const char *direction, const char *cmd,
+                           const char **error);
+
 #endif

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.

Reply via email to