This is an automated email from the git hooks/post-receive script.
git pushed a commit to reference refs/pull/34/head
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.