1
0
mirror of https://github.com/rkd77/elinks.git synced 2024-09-28 03:06:20 -04:00
elinks/src/terminal/terminal.c

494 lines
11 KiB
C
Raw Normal View History

2007-07-27 09:50:37 -04:00
/** Terminal interface - low-level displaying implementation.
* @file */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "elinks.h"
#include "bookmarks/bookmarks.h"
#include "config/options.h"
#include "main/main.h"
2006-05-20 08:59:40 -04:00
#include "main/module.h"
#include "main/object.h"
#include "main/select.h"
#include "osdep/osdep.h"
#include "osdep/signals.h"
#ifdef CONFIG_SCRIPTING_SPIDERMONKEY
# include "scripting/smjs/smjs.h"
#endif
#include "session/session.h"
#include "terminal/draw.h"
#include "terminal/event.h"
#include "terminal/hardio.h"
#include "terminal/kbd.h"
#include "terminal/screen.h"
#include "terminal/terminal.h"
#ifdef CONFIG_TERMINFO
#include "terminal/terminfo.h"
#endif
#include "terminal/window.h"
#include "util/error.h"
#include "util/memory.h"
#include "util/string.h"
#include "viewer/text/textarea.h"
2007-07-26 15:39:08 -04:00
INIT_LIST_OF(struct terminal, terminals);
static void check_if_no_terminal(void);
void
redraw_terminal(struct terminal *term)
{
struct term_event ev;
set_redraw_term_event(&ev, term->width, term->height);
term_send_event(term, &ev);
}
void
redraw_terminal_cls(struct terminal *term)
{
struct term_event ev;
set_resize_term_event(&ev, term->width, term->height);
term_send_event(term, &ev);
}
void
cls_redraw_all_terminals(void)
{
struct terminal *term;
foreach (term, terminals)
redraw_terminal_cls(term);
}
/** Get the terminal in which message boxes should be displayed, if
* there is no specific reason to use some other terminal. This
* returns NULL if all terminals have been closed. (ELinks keeps
* running anyway if ui.sessions.keep_session_active is true.) */
struct terminal *
get_default_terminal(void)
{
if (list_empty(terminals))
return NULL;
else
return terminals.next;
}
struct terminal *
init_term(int fdin, int fdout)
{
char name[MAX_TERM_LEN + 9] = "terminal.";
struct terminal *term = mem_calloc(1, sizeof(*term));
if (!term) {
check_if_no_terminal();
return NULL;
}
term->screen = init_screen();
if (!term->screen) {
mem_free(term);
return NULL;
}
#ifdef CONFIG_TERMINFO
terminfo_setupterm(NULL, fdout);
#endif
init_list(term->windows);
term->fdin = fdin;
term->fdout = fdout;
term->master = (term->fdout == get_output_handle());
term->blocked = -1;
get_terminal_name(name + 9);
term->spec = get_opt_rec(config_options, name);
object_lock(term->spec);
/* It's a new terminal, so assume the user is using it right now,
* and sort it to the front of the list. */
add_to_list(terminals, term);
set_handlers(fdin, (select_handler_T) in_term, NULL,
(select_handler_T) destroy_terminal, term);
return term;
}
/** Get the codepage of a terminal. The UTF-8 I/O option does not
* affect this.
*
* @todo Perhaps cache the value in struct terminal?
*
* @bug Bug 1064: If the charset has been set as "System", this should
* apply the locale environment variables of the slave ELinks process,
* not those of the master ELinks process that parsed the configuration
* file. That is why the parameter points to struct terminal and not
* merely to its option tree (term->spec).
*
* @see get_translation_table(), get_cp_mime_name() */
int
get_terminal_codepage(const struct terminal *term)
{
return get_opt_codepage_tree(term->spec, "charset", NULL);
}
void
redraw_all_terminals(void)
{
struct terminal *term;
foreach (term, terminals)
redraw_screen(term);
}
void
destroy_terminal(struct terminal *term)
{
#ifdef CONFIG_SCRIPTING_SPIDERMONKEY
smjs_detach_terminal_object(term);
#endif
#ifdef CONFIG_BOOKMARKS
bookmark_auto_save_tabs(term);
#endif
detach_downloads_from_terminal(term);
free_textarea_data(term);
/* delete_window doesn't update term->current_tab, but it
calls redraw_terminal, which requires term->current_tab
to be valid if there are any tabs left. So set a value
that will be valid for that long. */
term->current_tab = 0;
while (!list_empty(term->windows))
delete_window(term->windows.next);
/* mem_free_if(term->cwd); */
mem_free_if(term->title);
if (term->screen) done_screen(term->screen);
clear_handlers(term->fdin);
mem_free_if(term->interlink);
if (term->blocked != -1) {
close(term->blocked);
clear_handlers(term->blocked);
}
del_from_list(term);
close(term->fdin);
if (term->fdout != 1) {
if (term->fdout != term->fdin) close(term->fdout);
} else {
unhandle_terminal_signals(term);
free_all_itrms();
#ifndef NO_FORK_ON_EXIT
if (!list_empty(terminals)) {
if (fork()) exit(0);
}
#endif
}
object_unlock(term->spec);
mem_free(term);
check_if_no_terminal();
}
void
destroy_all_terminals(void)
{
while (!list_empty(terminals))
destroy_terminal(terminals.next);
}
static void
check_if_no_terminal(void)
{
program.terminate = list_empty(terminals)
&& !get_opt_bool("ui.sessions.keep_session_active", NULL);
}
void
exec_thread(char *path, int p)
{
int plen = strlen(path + 1) + 2;
2006-01-11 14:12:59 -05:00
#if defined(HAVE_SETPGID) && !defined(CONFIG_OS_BEOS) && !defined(HAVE_BEGINTHREAD)
2007-07-14 05:26:45 -04:00
if (path[0] == TERM_EXEC_NEWWIN) setpgid(0, 0);
#endif
exe(path + 1);
if (path[plen]) unlink(path + plen);
}
void
close_handle(void *h)
{
close((long) h);
clear_handlers((long) h);
}
static void
unblock_terminal(struct terminal *term)
{
close_handle((void *) (long) term->blocked);
term->blocked = -1;
set_handlers(term->fdin, (select_handler_T) in_term, NULL,
(select_handler_T) destroy_terminal, term);
unblock_itrm();
redraw_terminal_cls(term);
2007-09-01 05:10:54 -04:00
if (term->textarea_data) /* XXX */
textarea_edit(1, term, NULL, NULL, NULL);
}
#ifndef CONFIG_FASTMEM
void
assert_terminal_ptr_not_dangling(const struct terminal *suspect)
{
struct terminal *term;
if (suspect == NULL)
return;
foreach (term, terminals) {
if (term == suspect)
return;
}
assertm(0, "Dangling pointer to struct terminal");
}
#endif /* !CONFIG_FASTMEM */
static void
exec_on_master_terminal(struct terminal *term,
char *path, int plen,
char *delete_, int dlen,
2007-07-14 05:26:45 -04:00
enum term_exec fg)
{
int blockh;
2006-01-06 19:57:11 -05:00
int param_size = plen + dlen + 2 /* 2 null char */ + 1 /* fg */;
char *param = fmem_alloc(param_size);
if (!param) return;
param[0] = fg;
memcpy(param + 1, path, plen + 1);
memcpy(param + 1 + plen + 1, delete_, dlen + 1);
2007-07-14 05:26:45 -04:00
if (fg == TERM_EXEC_FG) block_itrm();
blockh = start_thread((void (*)(void *, int)) exec_thread,
param, param_size);
fmem_free(param);
if (blockh == -1) {
2007-07-14 05:26:45 -04:00
if (fg == TERM_EXEC_FG) unblock_itrm();
return;
}
2007-07-14 05:26:45 -04:00
if (fg == TERM_EXEC_FG) {
term->blocked = blockh;
set_handlers(blockh,
(select_handler_T) unblock_terminal,
NULL,
(select_handler_T) unblock_terminal,
term);
set_handlers(term->fdin, NULL, NULL,
(select_handler_T) destroy_terminal,
term);
} else {
set_handlers(blockh, close_handle, NULL,
close_handle, (void *) (long) blockh);
}
}
static void
exec_on_slave_terminal( struct terminal *term,
char *path, int plen,
char *delete_, int dlen,
2007-07-14 05:26:45 -04:00
enum term_exec fg)
{
int data_size = plen + dlen + 1 /* 0 */ + 1 /* fg */ + 2 /* 2 null char */;
char *data = fmem_alloc(data_size);
if (!data) return;
2006-01-06 19:57:11 -05:00
data[0] = 0;
data[1] = fg;
memcpy(data + 2, path, plen + 1);
memcpy(data + 2 + plen + 1, delete_, dlen + 1);
hard_write(term->fdout, data, data_size);
fmem_free(data);
}
void
exec_on_terminal(struct terminal *term, char *path,
char *delete_, enum term_exec fg)
{
2006-01-06 19:28:54 -05:00
if (path) {
if (!*path) return;
} else {
2006-01-06 19:28:54 -05:00
path = "";
}
#ifdef NO_FG_EXEC
2007-07-14 05:26:45 -04:00
fg = TERM_EXEC_BG;
#endif
if (term->master) {
if (!*path) {
dispatch_special(delete_);
return;
}
/* TODO: Should this be changed to allow TERM_EXEC_NEWWIN
* in a blocked terminal? There is similar code in
* in_sock(). --KON, 2007 */
2007-07-14 05:26:45 -04:00
if (fg != TERM_EXEC_BG && is_blocked()) {
unlink(delete_);
return;
}
exec_on_master_terminal(term,
path, strlen(path),
delete_, strlen(delete_),
fg);
} else {
exec_on_slave_terminal( term,
path, strlen(path),
delete_, strlen(delete_),
fg);
}
}
void
exec_shell(struct terminal *term)
{
char *sh;
if (!can_open_os_shell(term->environment)) return;
sh = get_shell();
if (sh && *sh)
2007-07-14 05:26:45 -04:00
exec_on_terminal(term, sh, "", TERM_EXEC_FG);
}
void
do_terminal_function(struct terminal *term, unsigned char code,
char *data)
{
int data_len = strlen(data);
char *x_data = fmem_alloc(data_len + 1 /* code */ + 1 /* null char */);
if (!x_data) return;
x_data[0] = code;
memcpy(x_data + 1, data, data_len + 1);
2007-07-14 05:26:45 -04:00
exec_on_terminal(term, NULL, x_data, TERM_EXEC_BG);
fmem_free(x_data);
}
Bug 885: Proper charset support in xterm window title When ELinks runs in an X11 terminal emulator (e.g. xterm), or in GNU Screen, it tries to update the title of the window to match the title of the current document. To do this, ELinks sends an "OSC 1 ; Pt BEL" sequence to the terminal. Unfortunately, xterm expects the Pt string to be in the ISO-8859-1 charset, making it impossible to display e.g. Cyrillic characters. In xterm patch #210 (2006-03-12) however, there is a menu item and a resource that can make xterm take the Pt string in UTF-8 instead, allowing characters from all around the world. The downside is that ELinks apparently cannot ask xterm whether the setting is on or off; so add a terminal._template_.latin1_title option to ELinks and let the user edit that instead. Complete list of changes: - Add the terminal._template_.latin1_title option. But do not add that to the terminal options window because it's already rather crowded there. - In set_window_title(), take a new codepage argument. Use it to decode the title into Unicode characters, and remove only actual control characters. For example, CP437 has graphical characters in the 0x80...0x9F range, so don't remove those, even though ISO-8859-1 has control characters in the same range. Likewise, don't misinterpret single bytes of UTF-8 characters as control characters. - In set_window_title(), do not truncate the title to the width of the window. The font is likely to be different and proportional anyway. But do truncate before 1024 bytes, an xterm limit. - In struct itrm, add a title_codepage member to remember which charset the master said it was going to use in the terminal window title. Initialize title_codepage in handle_trm(), update it in dispatch_special() if the master sends the new request TERM_FN_TITLE_CODEPAGE, and use it in most set_window_title() calls; but not in the one that sets $TERM as the title, because that string was not received from the master and should consist of ASCII characters only. - In set_terminal_title(), convert the caller-provided title to ISO-8859-1 or UTF-8 if appropriate, and report the codepage to the slave with the new TERM_FN_TITLE_CODEPAGE request. The conversion can run out of memory, so return a success/error flag, rather than void. In display_window_title(), check this result and don't update caches on error. - Add a NEWS entry for all of this.
2008-12-28 20:09:53 -05:00
/** @return negative on error; zero or positive on success. */
int
set_terminal_title(struct terminal *term, char *title)
{
Bug 885: Proper charset support in xterm window title When ELinks runs in an X11 terminal emulator (e.g. xterm), or in GNU Screen, it tries to update the title of the window to match the title of the current document. To do this, ELinks sends an "OSC 1 ; Pt BEL" sequence to the terminal. Unfortunately, xterm expects the Pt string to be in the ISO-8859-1 charset, making it impossible to display e.g. Cyrillic characters. In xterm patch #210 (2006-03-12) however, there is a menu item and a resource that can make xterm take the Pt string in UTF-8 instead, allowing characters from all around the world. The downside is that ELinks apparently cannot ask xterm whether the setting is on or off; so add a terminal._template_.latin1_title option to ELinks and let the user edit that instead. Complete list of changes: - Add the terminal._template_.latin1_title option. But do not add that to the terminal options window because it's already rather crowded there. - In set_window_title(), take a new codepage argument. Use it to decode the title into Unicode characters, and remove only actual control characters. For example, CP437 has graphical characters in the 0x80...0x9F range, so don't remove those, even though ISO-8859-1 has control characters in the same range. Likewise, don't misinterpret single bytes of UTF-8 characters as control characters. - In set_window_title(), do not truncate the title to the width of the window. The font is likely to be different and proportional anyway. But do truncate before 1024 bytes, an xterm limit. - In struct itrm, add a title_codepage member to remember which charset the master said it was going to use in the terminal window title. Initialize title_codepage in handle_trm(), update it in dispatch_special() if the master sends the new request TERM_FN_TITLE_CODEPAGE, and use it in most set_window_title() calls; but not in the one that sets $TERM as the title, because that string was not received from the master and should consist of ASCII characters only. - In set_terminal_title(), convert the caller-provided title to ISO-8859-1 or UTF-8 if appropriate, and report the codepage to the slave with the new TERM_FN_TITLE_CODEPAGE request. The conversion can run out of memory, so return a success/error flag, rather than void. In display_window_title(), check this result and don't update caches on error. - Add a NEWS entry for all of this.
2008-12-28 20:09:53 -05:00
int from_cp;
int to_cp;
char *converted = NULL;
Bug 885: Proper charset support in xterm window title When ELinks runs in an X11 terminal emulator (e.g. xterm), or in GNU Screen, it tries to update the title of the window to match the title of the current document. To do this, ELinks sends an "OSC 1 ; Pt BEL" sequence to the terminal. Unfortunately, xterm expects the Pt string to be in the ISO-8859-1 charset, making it impossible to display e.g. Cyrillic characters. In xterm patch #210 (2006-03-12) however, there is a menu item and a resource that can make xterm take the Pt string in UTF-8 instead, allowing characters from all around the world. The downside is that ELinks apparently cannot ask xterm whether the setting is on or off; so add a terminal._template_.latin1_title option to ELinks and let the user edit that instead. Complete list of changes: - Add the terminal._template_.latin1_title option. But do not add that to the terminal options window because it's already rather crowded there. - In set_window_title(), take a new codepage argument. Use it to decode the title into Unicode characters, and remove only actual control characters. For example, CP437 has graphical characters in the 0x80...0x9F range, so don't remove those, even though ISO-8859-1 has control characters in the same range. Likewise, don't misinterpret single bytes of UTF-8 characters as control characters. - In set_window_title(), do not truncate the title to the width of the window. The font is likely to be different and proportional anyway. But do truncate before 1024 bytes, an xterm limit. - In struct itrm, add a title_codepage member to remember which charset the master said it was going to use in the terminal window title. Initialize title_codepage in handle_trm(), update it in dispatch_special() if the master sends the new request TERM_FN_TITLE_CODEPAGE, and use it in most set_window_title() calls; but not in the one that sets $TERM as the title, because that string was not received from the master and should consist of ASCII characters only. - In set_terminal_title(), convert the caller-provided title to ISO-8859-1 or UTF-8 if appropriate, and report the codepage to the slave with the new TERM_FN_TITLE_CODEPAGE request. The conversion can run out of memory, so return a success/error flag, rather than void. In display_window_title(), check this result and don't update caches on error. - Add a NEWS entry for all of this.
2008-12-28 20:09:53 -05:00
if (term->title && !strcmp(title, term->title)) return 0;
/* In which codepage was the title parameter given? */
from_cp = get_terminal_codepage(term);
/* In which codepage does the terminal want the title? */
if (get_opt_bool_tree(term->spec, "latin1_title", NULL))
Bug 885: Proper charset support in xterm window title When ELinks runs in an X11 terminal emulator (e.g. xterm), or in GNU Screen, it tries to update the title of the window to match the title of the current document. To do this, ELinks sends an "OSC 1 ; Pt BEL" sequence to the terminal. Unfortunately, xterm expects the Pt string to be in the ISO-8859-1 charset, making it impossible to display e.g. Cyrillic characters. In xterm patch #210 (2006-03-12) however, there is a menu item and a resource that can make xterm take the Pt string in UTF-8 instead, allowing characters from all around the world. The downside is that ELinks apparently cannot ask xterm whether the setting is on or off; so add a terminal._template_.latin1_title option to ELinks and let the user edit that instead. Complete list of changes: - Add the terminal._template_.latin1_title option. But do not add that to the terminal options window because it's already rather crowded there. - In set_window_title(), take a new codepage argument. Use it to decode the title into Unicode characters, and remove only actual control characters. For example, CP437 has graphical characters in the 0x80...0x9F range, so don't remove those, even though ISO-8859-1 has control characters in the same range. Likewise, don't misinterpret single bytes of UTF-8 characters as control characters. - In set_window_title(), do not truncate the title to the width of the window. The font is likely to be different and proportional anyway. But do truncate before 1024 bytes, an xterm limit. - In struct itrm, add a title_codepage member to remember which charset the master said it was going to use in the terminal window title. Initialize title_codepage in handle_trm(), update it in dispatch_special() if the master sends the new request TERM_FN_TITLE_CODEPAGE, and use it in most set_window_title() calls; but not in the one that sets $TERM as the title, because that string was not received from the master and should consist of ASCII characters only. - In set_terminal_title(), convert the caller-provided title to ISO-8859-1 or UTF-8 if appropriate, and report the codepage to the slave with the new TERM_FN_TITLE_CODEPAGE request. The conversion can run out of memory, so return a success/error flag, rather than void. In display_window_title(), check this result and don't update caches on error. - Add a NEWS entry for all of this.
2008-12-28 20:09:53 -05:00
to_cp = get_cp_index("ISO-8859-1");
else if (get_opt_bool_tree(term->spec, "utf_8_io", NULL))
Bug 885: Proper charset support in xterm window title When ELinks runs in an X11 terminal emulator (e.g. xterm), or in GNU Screen, it tries to update the title of the window to match the title of the current document. To do this, ELinks sends an "OSC 1 ; Pt BEL" sequence to the terminal. Unfortunately, xterm expects the Pt string to be in the ISO-8859-1 charset, making it impossible to display e.g. Cyrillic characters. In xterm patch #210 (2006-03-12) however, there is a menu item and a resource that can make xterm take the Pt string in UTF-8 instead, allowing characters from all around the world. The downside is that ELinks apparently cannot ask xterm whether the setting is on or off; so add a terminal._template_.latin1_title option to ELinks and let the user edit that instead. Complete list of changes: - Add the terminal._template_.latin1_title option. But do not add that to the terminal options window because it's already rather crowded there. - In set_window_title(), take a new codepage argument. Use it to decode the title into Unicode characters, and remove only actual control characters. For example, CP437 has graphical characters in the 0x80...0x9F range, so don't remove those, even though ISO-8859-1 has control characters in the same range. Likewise, don't misinterpret single bytes of UTF-8 characters as control characters. - In set_window_title(), do not truncate the title to the width of the window. The font is likely to be different and proportional anyway. But do truncate before 1024 bytes, an xterm limit. - In struct itrm, add a title_codepage member to remember which charset the master said it was going to use in the terminal window title. Initialize title_codepage in handle_trm(), update it in dispatch_special() if the master sends the new request TERM_FN_TITLE_CODEPAGE, and use it in most set_window_title() calls; but not in the one that sets $TERM as the title, because that string was not received from the master and should consist of ASCII characters only. - In set_terminal_title(), convert the caller-provided title to ISO-8859-1 or UTF-8 if appropriate, and report the codepage to the slave with the new TERM_FN_TITLE_CODEPAGE request. The conversion can run out of memory, so return a success/error flag, rather than void. In display_window_title(), check this result and don't update caches on error. - Add a NEWS entry for all of this.
2008-12-28 20:09:53 -05:00
to_cp = get_cp_index("UTF-8");
else
to_cp = from_cp;
if (from_cp != to_cp) {
struct conv_table *convert_table;
convert_table = get_translation_table(from_cp, to_cp);
if (!convert_table) return -1;
converted = convert_string(convert_table, title, strlen(title),
to_cp, CSM_NONE, NULL, NULL, NULL);
if (!converted) return -1;
}
mem_free_set(&term->title, stracpy(title));
Bug 885: Proper charset support in xterm window title When ELinks runs in an X11 terminal emulator (e.g. xterm), or in GNU Screen, it tries to update the title of the window to match the title of the current document. To do this, ELinks sends an "OSC 1 ; Pt BEL" sequence to the terminal. Unfortunately, xterm expects the Pt string to be in the ISO-8859-1 charset, making it impossible to display e.g. Cyrillic characters. In xterm patch #210 (2006-03-12) however, there is a menu item and a resource that can make xterm take the Pt string in UTF-8 instead, allowing characters from all around the world. The downside is that ELinks apparently cannot ask xterm whether the setting is on or off; so add a terminal._template_.latin1_title option to ELinks and let the user edit that instead. Complete list of changes: - Add the terminal._template_.latin1_title option. But do not add that to the terminal options window because it's already rather crowded there. - In set_window_title(), take a new codepage argument. Use it to decode the title into Unicode characters, and remove only actual control characters. For example, CP437 has graphical characters in the 0x80...0x9F range, so don't remove those, even though ISO-8859-1 has control characters in the same range. Likewise, don't misinterpret single bytes of UTF-8 characters as control characters. - In set_window_title(), do not truncate the title to the width of the window. The font is likely to be different and proportional anyway. But do truncate before 1024 bytes, an xterm limit. - In struct itrm, add a title_codepage member to remember which charset the master said it was going to use in the terminal window title. Initialize title_codepage in handle_trm(), update it in dispatch_special() if the master sends the new request TERM_FN_TITLE_CODEPAGE, and use it in most set_window_title() calls; but not in the one that sets $TERM as the title, because that string was not received from the master and should consist of ASCII characters only. - In set_terminal_title(), convert the caller-provided title to ISO-8859-1 or UTF-8 if appropriate, and report the codepage to the slave with the new TERM_FN_TITLE_CODEPAGE request. The conversion can run out of memory, so return a success/error flag, rather than void. In display_window_title(), check this result and don't update caches on error. - Add a NEWS entry for all of this.
2008-12-28 20:09:53 -05:00
do_terminal_function(term, TERM_FN_TITLE_CODEPAGE,
get_cp_mime_name(to_cp));
do_terminal_function(term, TERM_FN_TITLE,
converted ? converted : title);
mem_free_if(converted);
return 0;
}
static int terminal_pipe[2];
int
check_terminal_pipes(void)
{
return c_pipe(terminal_pipe);
}
void
close_terminal_pipes(void)
{
close(terminal_pipe[0]);
close(terminal_pipe[1]);
}
struct terminal *
attach_terminal(int in, int out, int ctl, void *info, int len)
{
struct terminal *term;
if (set_nonblocking_fd(terminal_pipe[0]) < 0) return NULL;
if (set_nonblocking_fd(terminal_pipe[1]) < 0) return NULL;
handle_trm(in, out, out, terminal_pipe[1], ctl, info, len, 0);
term = init_term(terminal_pipe[0], out);
if (!term) {
close_terminal_pipes();
return NULL;
}
return term;
}
2006-05-20 08:59:40 -04:00
static struct module *terminal_submodules[] = {
&terminal_screen_module,
NULL
};
struct module terminal_module = struct_module(
/* Because this module is listed in main_modules rather than
* in builtin_modules, its name does not appear in the user
* interface and so need not be translatable. */
/* name: */ "Terminal",
2006-05-20 08:59:40 -04:00
/* options: */ NULL,
/* hooks: */ NULL,
/* submodules: */ terminal_submodules,
/* data: */ NULL,
/* init: */ NULL,
/* done: */ NULL
);