diff --git a/acconfig.h b/acconfig.h index 75350596..d7d1dfea 100644 --- a/acconfig.h +++ b/acconfig.h @@ -23,12 +23,13 @@ #undef SCO_FLAVOR /* our own curses checks */ -#undef USE_CURSES_WINDOWS #undef HAVE_NCURSES_USE_DEFAULT_COLORS #undef HAVE_CURSES_IDCOK #undef HAVE_CURSES_RESIZETERM #undef HAVE_CURSES_WRESIZE -#undef USE_CURSES_WINDOWS + +/* terminfo/termcap */ +#undef HAVE_TERMINFO /* nls */ #undef ENABLE_NLS diff --git a/configure.in b/configure.in index 37d05636..2197f09c 100644 --- a/configure.in +++ b/configure.in @@ -90,6 +90,18 @@ AC_ARG_WITH(proxy, fi, want_irssiproxy=no) +AC_ARG_WITH(terminfo, +[ --with-terminfo Use terminfo directly instead of curses], + if test x$withval = xyes; then + want_terminfo=yes + else + if test "x$withval" = xno; then + want_terminfo=no + else + want_terminfo=yes + fi + fi, + want_terminfo=auto) AC_ARG_WITH(modules, [ --with-modules Specify what modules to build in binary], @@ -173,13 +185,6 @@ AC_ARG_WITH(tests, TEST_DIR=) AC_SUBST(TEST_DIR) -AC_ARG_ENABLE(curses-windows, -[ --enable-curses-windows Use curses windows], - if test x$enableval != xno; then - AC_DEFINE(USE_CURSES_WINDOWS) - fi, - AC_DEFINE(USE_CURSES_WINDOWS)) - AC_ARG_ENABLE(memdebug, [ --enable-memdebug Enable memory debugging], if test x$enableval = xyes; then @@ -213,11 +218,11 @@ dnl ** AC_CHECK_FUNCS(mkfifo fcntl) AC_CHECK_LIB(socket, socket, [ - PROG_LIBS="$PROG_LIBS -lsocket" + LIBS="$LIBS -lsocket" ]) AC_CHECK_LIB(nsl, inet_addr, [ - PROG_LIBS="$PROG_LIBS -lnsl" + LIBS="$LIBS -lnsl" ], -lsocket) dnl * gcc specific options @@ -256,7 +261,7 @@ dnl ** if test "x$want_socks" = "xyes"; then AC_CHECK_LIB(socks, connect, [ - PROG_LIBS="$PROG_LIBS -lsocks" + LIBS="$LIBS -lsocks" AC_CHECK_HEADER(socks.h, [ AC_DEFINE(HAVE_SOCKS_H) CFLAGS="$CFLAGS -DSOCKS" @@ -369,7 +374,7 @@ if test -z "$GLIB_DIR"; then fi fi -PROG_LIBS="$PROG_LIBS $GLIB_LIBS" +LIBS="$LIBS $GLIB_LIBS" dnl ** dnl ** check if we can link dynamic libraries to modules @@ -434,35 +439,38 @@ dnl ** if test "x$want_textui" = "xyes"; then AC_CHECK_CURSES - if test -n "$has_ncurses"; then - AC_CHECK_LIB(ncurses, use_default_colors, [ - AC_DEFINE(HAVE_NCURSES_USE_DEFAULT_COLORS) - ],, $CURSES_LIBS) - AC_CHECK_LIB(ncurses, idcok, [ - AC_DEFINE(HAVE_CURSES_IDCOK) - ],, $CURSES_LIBS) - AC_CHECK_LIB(ncurses, resizeterm, [ - AC_DEFINE(HAVE_CURSES_RESIZETERM) - ],, $CURSES_LIBS) - AC_CHECK_LIB(ncurses, wresize, [ - AC_DEFINE(HAVE_CURSES_WRESIZE) - ],, $CURSES_LIBS) - elif test "x$has_curses" = "xtrue"; then - AC_CHECK_LIB(curses, idcok, [ - AC_DEFINE(HAVE_CURSES_IDCOK) - ],, $CURSES_LIBS) - AC_CHECK_LIB(curses, resizeterm, [ - AC_DEFINE(HAVE_CURSES_RESIZETERM) - ],, $CURSES_LIBS) - AC_CHECK_LIB(curses, wresize, [ - AC_DEFINE(HAVE_CURSES_WRESIZE) - ],, $CURSES_LIBS) + LIBS="$LIBS $CURSES_LIBS" + if test "x$has_curses" = "xtrue"; then + AC_CHECK_FUNC(use_default_colors, AC_DEFINE(HAVE_NCURSES_USE_DEFAULT_COLORS)) + AC_CHECK_FUNC(idcok, AC_DEFINE(HAVE_CURSES_IDCOK)) + AC_CHECK_FUNC(resizeterm, AC_DEFINE(HAVE_CURSES_RESIZETERM)) + AC_CHECK_FUNC(wresize, AC_DEFINE(HAVE_CURSES_WRESIZE)) + if test "x$want_terminfo" = "xauto" -a "x$has_ncurses" != "xtrue"; then + dnl * we'd rather use terminfo/termcap than plain curses + want_terminfo=yes + AC_CHECK_FUNC(setupterm,, want_termcap=yes) + fi else - want_textui=no - curses_error=yes + AC_CHECK_LIB(tinfo, setupterm, [ + LIBS="$LIBS -ltinfo" + want_terminfo=yes + ], AC_CHECK_LIB(termlib, tgetent, [ + LIBS="$LIBS -ltermlib" + want_termcap=yes + ], AC_CHECK_LIB(termcap, tgetent, [ + LIBS="$LIBS -ltermcap" + want_termcap=yes + ], [ + AC_MSG_WARN(Terminfo/termcap not found) + want_textui=no + curses_error=yes + ]))) fi -else - has_curses=false + if test "x$want_termcap" = "xyes"; then + AC_CHECK_FUNC(tparm,, need_tparm=yes) + else + AC_DEFINE(HAVE_TERMINFO) + fi fi dnl ** @@ -642,8 +650,8 @@ AM_CONDITIONAL(BUILD_PLUGINS, test "$want_plugins" = "yes") AM_CONDITIONAL(BUILD_SERVERTEST, test -n "$TEST_DIR") AM_CONDITIONAL(HAVE_PERL, test "$want_perl" != "no") AM_CONDITIONAL(HAVE_STATIC_PERL, test "$want_perl" = "static") - -AC_SUBST(PROG_LIBS) +AM_CONDITIONAL(NEED_TPARM, test "$need_tparm" = "yes") +AM_CONDITIONAL(USE_CURSES, test "$want_terminfo" != "yes" -a "$want_termcap" != "yes") dnl ** dnl ** Keep all the libraries here so each frontend doesn't need to @@ -811,7 +819,16 @@ fi echo if test "x$curses_error" != "xyes"; then - echo "Building text frontend ..... : $want_textui" + if test "x$want_textui" = "xno"; then + text=no + elif test "x$want_termcap" = "xyes"; then + text="yes, using termcap" + elif test "x$want_terminfo" = "xyes"; then + text="yes, using terminfo" + else + text="yes, using curses" + fi + echo "Building text frontend ..... : $text" else echo "Building text frontend ..... : NO!!" echo " - Because curses was not found, specify the path to it with" diff --git a/default.theme b/default.theme index 3b457830..4c19d41c 100644 --- a/default.theme +++ b/default.theme @@ -47,9 +47,9 @@ ############################################################################# -# default foreground color (%N) - 0 is the "default terminal color" -default_color = 0; -# default foreground color when "0" can't be used, +# default foreground color (%N) - -1 is the "default terminal color" +default_color = -1; +# default foreground color when -1 can't be used, # such as with bolds and reverses. white is default. default_real_color = 7; diff --git a/src/fe-common/core/formats.c b/src/fe-common/core/formats.c index dbdd51f2..cb4a2829 100644 --- a/src/fe-common/core/formats.c +++ b/src/fe-common/core/formats.c @@ -990,8 +990,16 @@ void format_send_to_gui(TEXT_DEST_REC *dest, const char *text) } } ptr++; - if (*ptr != FORMAT_COLOR_NOCHANGE) + if (*ptr != FORMAT_COLOR_NOCHANGE) { bgcolor = *ptr-'0'; + if (bgcolor <= 7) + flags &= ~GUI_PRINT_FLAG_BLINK; + else { + /* blink */ + bgcolor -= 8; + flags |= GUI_PRINT_FLAG_BLINK; + } + } } ptr++; break; diff --git a/src/fe-common/core/themes.c b/src/fe-common/core/themes.c index c7f8cf7b..62c1ec8a 100644 --- a/src/fe-common/core/themes.c +++ b/src/fe-common/core/themes.c @@ -799,9 +799,11 @@ static int theme_read(THEME_REC *theme, const char *path, const char *data) } theme->default_color = - config_get_int(config, NULL, "default_color", 0); - theme->default_real_color = - config_get_int(config, NULL, "default_real_color", 7); + config_get_int(config, NULL, "default_color", -1); + /* FIXME: remove after 0.7.99 */ + if (theme->default_color == 0 && + config_get_int(config, NULL, "default_real_color", -1) != -1) + theme->default_color = -1; theme_read_replaces(config, theme); if (data == NULL) { @@ -1141,8 +1143,7 @@ static void themes_read(void) if (current_theme == NULL) { fname = g_strdup_printf("%s/default.theme", get_irssi_dir()); current_theme = theme_create(fname, "default"); - current_theme->default_color = 0; - current_theme->default_real_color = 7; + current_theme->default_color = -1; theme_read(current_theme, NULL, default_theme); g_free(fname); } diff --git a/src/fe-common/core/themes.h b/src/fe-common/core/themes.h index 96f23400..3693d4f4 100644 --- a/src/fe-common/core/themes.h +++ b/src/fe-common/core/themes.h @@ -16,11 +16,8 @@ typedef struct { time_t last_modify; int default_color; /* default color to use with text with default - background. default is 0 which means the default - color set by terminal */ - int default_real_color; /* default color to use with background set. - this shouldn't be 0, unless black is really - wanted. default is 7 (white). */ + background. default is -1 which means the + default color set by terminal */ GHashTable *modules; int replace_keys[256]; /* index to replace_values for each char */ diff --git a/src/fe-text/Makefile.am b/src/fe-text/Makefile.am index 00e0f856..faf1494f 100644 --- a/src/fe-text/Makefile.am +++ b/src/fe-text/Makefile.am @@ -19,9 +19,27 @@ irssi_LDADD = \ @COMMON_LIBS@ \ @PERL_LINK_LIBS@ \ @PERL_FE_LINK_LIBS@ \ - @PERL_LINK_FLAGS@ \ - $(PROG_LIBS) \ - $(CURSES_LIBS) + @PERL_LINK_FLAGS@ + +tparm_sources = \ + tparm.c + +terminfo_sources = \ + term-terminfo.c \ + terminfo-core.c + +curses_sources = \ + term-curses.c + +if NEED_TPARM +use_tparm_sources = $(tparm_sources) +endif + +if USE_CURSES +use_term_sources = $(curses_sources) +else +use_term_sources = $(terminfo_sources) +endif irssi_SOURCES = \ gui-entry.c \ @@ -33,10 +51,12 @@ irssi_SOURCES = \ mainwindows.c \ mainwindow-activity.c \ mainwindows-layout.c \ - screen.c \ statusbar.c \ statusbar-config.c \ statusbar-items.c \ + term.c \ + $(use_tparm_sources) \ + $(use_term_sources) \ textbuffer.c \ textbuffer-commands.c \ textbuffer-reformat.c \ @@ -50,12 +70,18 @@ noinst_HEADERS = \ gui-readline.h \ gui-windows.h \ mainwindows.h \ - screen.h \ statusbar.h \ statusbar-config.h \ statusbar-items.h \ + term.h \ + terminfo-core.h \ textbuffer.h \ textbuffer-view.h \ textbuffer-reformat.h \ module.h \ module-formats.h + +EXTRA_DIST = \ + $(tparm_sources) \ + $(terminfo_sources) \ + $(curses_sources) diff --git a/src/fe-text/gui-entry.c b/src/fe-text/gui-entry.c index bff316e8..f3f2463e 100644 --- a/src/fe-text/gui-entry.c +++ b/src/fe-text/gui-entry.c @@ -23,7 +23,7 @@ #include "gui-entry.h" #include "gui-printtext.h" -#include "screen.h" +#include "term.h" GUI_ENTRY_REC *active_entry; @@ -76,36 +76,36 @@ static void gui_entry_draw_from(GUI_ENTRY_REC *entry, int pos) if (xpos > end_xpos) return; - screen_set_color(screen_root, 0); - screen_move(screen_root, xpos, entry->ypos); + term_set_color(root_window, ATTR_RESET); + term_move(root_window, xpos, entry->ypos); p = entry->scrstart + pos >= entry->text->len ? "" : entry->text->str + entry->scrstart + pos; for (; *p != '\0' && xpos < end_xpos; p++, xpos++) { if (entry->hidden) - screen_addch(screen_root, ' '); + term_addch(root_window, ' '); else if ((unsigned char) *p >= 32) - screen_addch(screen_root, (unsigned char) *p); + term_addch(root_window, (unsigned char) *p); else { - screen_set_color(screen_root, ATTR_REVERSE); - screen_addch(screen_root, *p+'A'-1); - screen_set_color(screen_root, 0); + term_set_color(root_window, ATTR_RESET|ATTR_REVERSE); + term_addch(root_window, *p+'A'-1); + term_set_color(root_window, ATTR_RESET); } } /* clear the rest of the input line */ - if (end_xpos == screen_width) - screen_clrtoeol(screen_root); + if (end_xpos == term_width) + term_clrtoeol(root_window); else { while (xpos < end_xpos) { - screen_addch(screen_root, ' '); + term_addch(root_window, ' '); xpos++; } } - screen_move_cursor(entry->xpos + entry->scrpos + entry->promptlen, - entry->ypos); - screen_refresh(NULL); + term_move_cursor(entry->xpos + entry->scrpos + entry->promptlen, + entry->ypos); + term_refresh(NULL); } static void gui_entry_draw(GUI_ENTRY_REC *entry) @@ -153,9 +153,9 @@ void gui_entry_set_active(GUI_ENTRY_REC *entry) active_entry = entry; if (entry != NULL) { - screen_move_cursor(entry->xpos + entry->scrpos + - entry->promptlen, entry->ypos); - screen_refresh(NULL); + term_move_cursor(entry->xpos + entry->scrpos + + entry->promptlen, entry->ypos); + term_refresh(NULL); } } diff --git a/src/fe-text/gui-printtext.c b/src/fe-text/gui-printtext.c index 79f86674..5c882dc4 100644 --- a/src/fe-text/gui-printtext.c +++ b/src/fe-text/gui-printtext.c @@ -25,14 +25,14 @@ #include "formats.h" #include "printtext.h" -#include "screen.h" +#include "term.h" #include "gui-printtext.h" #include "gui-windows.h" int mirc_colors[] = { 15, 0, 1, 2, 12, 6, 5, 4, 14, 10, 3, 11, 9, 13, 8, 7 }; static int scrollback_lines, scrollback_hours, scrollback_burst_remove; -static int last_color, last_flags; +static int last_fg, last_bg, last_flags; static int next_xpos, next_ypos; static GHashTable *indent_functions; @@ -137,51 +137,51 @@ static void remove_old_lines(TEXT_BUFFER_VIEW_REC *view) } } -static void get_colors(int flags, int *fg, int *bg) +static void get_colors(int flags, int *fg, int *bg, int *attr) { if (flags & GUI_PRINT_FLAG_MIRC_COLOR) { /* mirc colors - real range is 0..15, but after 16 colors wrap to 0, 1, ... */ - *bg = *bg < 0 ? 0 : mirc_colors[*bg % 16]; - if (*fg > 0) *fg = mirc_colors[*fg % 16]; - } else { - /* default colors */ - *bg = *bg < 0 || *bg > 15 ? 0 : *bg; - if (*fg > 8) *fg &= ~8; + if (*bg >= 0) *bg = mirc_colors[*bg % 16]; + if (*fg >= 0) *fg = mirc_colors[*fg % 16]; } - if (*fg < 0 || *fg > 15) { - *fg = *bg == 0 ? current_theme->default_color : - current_theme->default_real_color; - } + if (*fg < 0 || *fg > 15) + *fg = current_theme->default_color; + if (*bg < 0 || *bg > 15) + *bg = -1; - if (flags & GUI_PRINT_FLAG_REVERSE) - *fg |= ATTR_REVERSE; - - if (*fg == 8) *fg |= ATTR_COLOR8; - if (flags & GUI_PRINT_FLAG_BOLD) { - if (*fg == 0) *fg = current_theme->default_real_color; - *fg |= 8; - } - if (flags & GUI_PRINT_FLAG_UNDERLINE) *fg |= ATTR_UNDERLINE; - if (flags & GUI_PRINT_FLAG_BLINK) *bg |= 0x08; + *attr = 0; + if (flags & GUI_PRINT_FLAG_REVERSE) *attr |= ATTR_REVERSE; + if (flags & GUI_PRINT_FLAG_BOLD) *attr |= ATTR_BOLD; + if (flags & GUI_PRINT_FLAG_UNDERLINE) *attr |= ATTR_UNDERLINE; + if (flags & GUI_PRINT_FLAG_BLINK) *attr |= ATTR_BLINK; } static void line_add_colors(TEXT_BUFFER_REC *buffer, LINE_REC **line, int fg, int bg, int flags) { unsigned char data[20]; - int color, pos; + int pos; + + /* get the fg & bg command chars */ + fg = fg < 0 ? LINE_COLOR_DEFAULT : fg & 0x0f; + bg = LINE_COLOR_BG | (bg < 0 ? LINE_COLOR_DEFAULT : bg & 0x0f); + if (flags & GUI_PRINT_FLAG_BOLD) + fg |= LINE_COLOR_BOLD; + if (flags & GUI_PRINT_FLAG_BLINK) + bg |= LINE_COLOR_BLINK; - /* color should never have last bit on or it would be treated as a - command! */ - color = (fg & 0x0f) | ((bg & 0x07) << 4); pos = 0; - - if (((fg & ATTR_COLOR8) == 0 && (fg|(bg << 4)) != last_color) || - ((fg & ATTR_COLOR8) && (fg & 0xf0) != (last_color & 0xf0))) { + if (fg != last_fg) { + last_fg = fg; data[pos++] = 0; - data[pos++] = color == 0 ? LINE_CMD_COLOR0 : color; + data[pos++] = fg == 0 ? LINE_CMD_COLOR0 : fg; + } + if (bg != last_bg) { + last_bg = bg; + data[pos++] = 0; + data[pos++] = bg; } if ((flags & GUI_PRINT_FLAG_UNDERLINE) != (last_flags & GUI_PRINT_FLAG_UNDERLINE)) { @@ -192,14 +192,6 @@ static void line_add_colors(TEXT_BUFFER_REC *buffer, LINE_REC **line, data[pos++] = 0; data[pos++] = LINE_CMD_REVERSE; } - if (fg & ATTR_COLOR8) { - data[pos++] = 0; - data[pos++] = LINE_CMD_COLOR8; - } - if (bg & 0x08) { - data[pos++] = 0; - data[pos++] = LINE_CMD_BLINK; - } if (flags & GUI_PRINT_FLAG_INDENT) { data[pos++] = 0; data[pos++] = LINE_CMD_INDENT; @@ -209,7 +201,6 @@ static void line_add_colors(TEXT_BUFFER_REC *buffer, LINE_REC **line, *line = textbuffer_insert(buffer, *line, data, pos, NULL); last_flags = flags; - last_color = fg | (bg << 4); } static void line_add_indent_func(TEXT_BUFFER_REC *buffer, LINE_REC **line, @@ -243,24 +234,24 @@ static void sig_gui_print_text(WINDOW_REC *window, void *fgcolor, TEXT_BUFFER_VIEW_REC *view; LINE_REC *insert_after; LINE_INFO_REC lineinfo; - int fg, bg, flags; + int fg, bg, flags, attr; flags = GPOINTER_TO_INT(pflags); fg = GPOINTER_TO_INT(fgcolor); bg = GPOINTER_TO_INT(bgcolor); - get_colors(flags, &fg, &bg); + get_colors(flags, &fg, &bg, &attr); if (window == NULL) { g_return_if_fail(next_xpos != -1); - screen_move(screen_root, next_xpos, next_ypos); - if (flags & GUI_PRINT_FLAG_CLRTOEOL) { - screen_set_bg(screen_root, fg | (bg << 4)); - screen_clrtoeol(screen_root); - screen_set_bg(screen_root, 0); - } - screen_set_color(screen_root, fg | (bg << 4)); - screen_addstr(screen_root, str); + attr |= fg > 0 ? fg : ATTR_RESETFG; + attr |= bg > 0 ? (bg << 4) : ATTR_RESETBG; + term_set_color(root_window, attr); + + term_move(root_window, next_xpos, next_ypos); + if (flags & GUI_PRINT_FLAG_CLRTOEOL) + term_clrtoeol(root_window); + term_addstr(root_window, str); next_xpos += strlen(str); return; } @@ -293,7 +284,7 @@ static void sig_gui_printtext_finished(WINDOW_REC *window) TEXT_BUFFER_VIEW_REC *view; LINE_REC *insert_after; - last_color = 0; + last_fg = last_bg = -1; last_flags = 0; view = WINDOW_GUI(window)->view; diff --git a/src/fe-text/gui-readline.c b/src/fe-text/gui-readline.c index a9417de7..8008d6d6 100644 --- a/src/fe-text/gui-readline.c +++ b/src/fe-text/gui-readline.c @@ -29,7 +29,7 @@ #include "keyboard.h" #include "translation.h" -#include "screen.h" +#include "term.h" #include "gui-entry.h" #include "gui-windows.h" @@ -330,7 +330,7 @@ void readline(void) int key; for (;;) { - key = screen_getch(); + key = term_getch(); if (key == -1) break; @@ -485,6 +485,11 @@ static void key_insert_text(const char *data) g_free(str); } +static void key_sig_stop(void) +{ + term_stop(); +} + static void sig_window_auto_changed(void) { command_history_next(active_win, gui_entry_get_text(active_entry)); @@ -615,9 +620,11 @@ void gui_readline_init(void) /* inserting special input characters to line.. */ key_bind("insert_text", "Append text to line", NULL, NULL, (SIGNAL_FUNC) key_insert_text); + /* autoreplaces */ key_bind("multi", NULL, "return", "check_replaces;send_line", NULL); key_bind("multi", NULL, "space", "check_replaces;insert_text ", NULL); + /* moving between windows */ for (n = 0; changekeys[n] != '\0'; n++) { key = g_strdup_printf("meta-%c", changekeys[n]); ltoa(data, n+1); @@ -625,6 +632,9 @@ void gui_readline_init(void) g_free(key); } + /* misc */ + key_bind("stop_irc", "Send SIGSTOP to client", "^Z", NULL, (SIGNAL_FUNC) key_sig_stop); + key_configure_thaw(); signal_add("window changed automatic", (SIGNAL_FUNC) sig_window_auto_changed); @@ -681,6 +691,7 @@ void gui_readline_deinit(void) key_unbind("insert_text", (SIGNAL_FUNC) key_insert_text); key_unbind("change_window", (SIGNAL_FUNC) key_change_window); + key_unbind("stop_irc", (SIGNAL_FUNC) key_sig_stop); keyboard_destroy(keyboard); key_configure_thaw(); diff --git a/src/fe-text/gui-windows.c b/src/fe-text/gui-windows.c index f3266828..5c6f00e5 100644 --- a/src/fe-text/gui-windows.c +++ b/src/fe-text/gui-windows.c @@ -24,7 +24,7 @@ #include "settings.h" #include "special-vars.h" -#include "screen.h" +#include "term.h" #include "gui-entry.h" #include "gui-windows.h" #include "gui-printtext.h" diff --git a/src/fe-text/irssi.c b/src/fe-text/irssi.c index 543972fc..e625e595 100644 --- a/src/fe-text/irssi.c +++ b/src/fe-text/irssi.c @@ -32,7 +32,7 @@ #include "fe-common-irc.h" #include "themes.h" -#include "screen.h" +#include "term.h" #include "gui-entry.h" #include "mainwindows.h" #include "gui-printtext.h" @@ -91,8 +91,8 @@ static void sig_exit(void) /* redraw irssi's screen.. */ void irssi_redraw(void) { - screen_clear(); - screen_refresh(NULL); + term_clear(); + term_refresh(NULL); /* windows */ mainwindows_redraw(); @@ -116,7 +116,7 @@ static void textui_finish_init(void) { quitting = FALSE; - screen_refresh_freeze(); + term_refresh_freeze(); textbuffer_init(); textbuffer_view_init(); textbuffer_commands_init(); @@ -130,7 +130,7 @@ static void textui_finish_init(void) mainwindows_layout_init(); gui_windows_init(); statusbar_init(); - screen_refresh_thaw(); + term_refresh_thaw(); settings_check(); module_register("core", "fe-text"); @@ -155,7 +155,7 @@ static void textui_deinit(void) quitting = TRUE; signal(SIGINT, SIG_DFL); - screen_refresh_freeze(); + term_refresh_freeze(); while (modules != NULL) module_unload(modules->data); @@ -180,8 +180,8 @@ static void textui_deinit(void) textbuffer_view_deinit(); textbuffer_deinit(); - screen_refresh_thaw(); - deinit_screen(); + term_refresh_thaw(); + term_deinit(); theme_unregister(); @@ -274,7 +274,7 @@ int main(int argc, char **argv) textui_init(); args_execute(argc, argv); - if (!init_screen()) + if (!term_init()) g_error("Can't initialize screen handling, quitting.\n"); textui_finish_init(); diff --git a/src/fe-text/lastlog.c b/src/fe-text/lastlog.c index 1602fdad..e4155611 100644 --- a/src/fe-text/lastlog.c +++ b/src/fe-text/lastlog.c @@ -38,7 +38,7 @@ static void window_lastlog_clear(WINDOW_REC *window) TEXT_BUFFER_VIEW_REC *view; LINE_REC *line, *next; - screen_refresh_freeze(); + term_refresh_freeze(); view = WINDOW_GUI(window)->view; line = textbuffer_view_get_lines(view); @@ -50,7 +50,7 @@ static void window_lastlog_clear(WINDOW_REC *window) line = next; } textbuffer_view_redraw(view); - screen_refresh_thaw(); + term_refresh_thaw(); } /* Only unknown keys in `optlist' should be levels. diff --git a/src/fe-text/mainwindows-layout.c b/src/fe-text/mainwindows-layout.c index 0e6af783..d3df6b86 100644 --- a/src/fe-text/mainwindows-layout.c +++ b/src/fe-text/mainwindows-layout.c @@ -123,7 +123,7 @@ static void sig_layout_restore(void) windows_count = g_slist_length(sorted_config); /* calculate the saved terminal height */ - avail_height = screen_height - + avail_height = term_height - screen_reserved_top - screen_reserved_bottom; height = 0; heights = g_new0(int, windows_count); diff --git a/src/fe-text/mainwindows.c b/src/fe-text/mainwindows.c index 32ceeeb9..0d1c3f19 100644 --- a/src/fe-text/mainwindows.c +++ b/src/fe-text/mainwindows.c @@ -27,7 +27,7 @@ #include "settings.h" #include "printtext.h" -#include "screen.h" +#include "term.h" #include "gui-windows.h" #define NEW_WINDOW_SIZE (WINDOW_MIN_SIZE + 1) @@ -39,16 +39,16 @@ int screen_reserved_top, screen_reserved_bottom; static int old_screen_width, old_screen_height; #define mainwindow_create_screen(window) \ - screen_window_create(0, \ - (window)->first_line + (window)->statusbar_lines_top, \ - (window)->width, \ - (window)->height + (window)->statusbar_lines) - -#define mainwindow_set_screen_size(window) \ - screen_window_move((window)->screen_win, 0, \ + term_window_create(0, \ (window)->first_line + (window)->statusbar_lines_top, \ (window)->width, \ - (window)->height - (window)->statusbar_lines); + (window)->height + (window)->statusbar_lines) + +#define mainwindow_set_screen_size(window) \ + term_window_move((window)->screen_win, 0, \ + (window)->first_line + (window)->statusbar_lines_top, \ + (window)->width, \ + (window)->height - (window)->statusbar_lines); static MAIN_WINDOW_REC *find_window_with_room(void) @@ -176,13 +176,13 @@ MAIN_WINDOW_REC *mainwindow_create(void) int space; rec = g_new0(MAIN_WINDOW_REC, 1); - rec->width = screen_width; + rec->width = term_width; if (mainwindows == NULL) { active_mainwin = rec; rec->first_line = screen_reserved_top; - rec->last_line = screen_height-1 - screen_reserved_bottom; + rec->last_line = term_height-1 - screen_reserved_bottom; rec->height = rec->last_line-rec->first_line+1; } else { parent = WINDOW_MAIN(active_win); @@ -204,7 +204,7 @@ MAIN_WINDOW_REC *mainwindow_create(void) } rec->screen_win = mainwindow_create_screen(rec); - screen_refresh(NULL); + term_refresh(NULL); mainwindows = g_slist_append(mainwindows, rec); signal_emit("mainwindow created", 1, rec); @@ -290,7 +290,7 @@ void mainwindow_destroy(MAIN_WINDOW_REC *window) mainwindows = g_slist_remove(mainwindows, window); signal_emit("mainwindow destroyed", 1, window); - screen_window_destroy(window->screen_win); + term_window_destroy(window->screen_win); if (!quitting && mainwindows != NULL) { gui_windows_remove_parent(window); @@ -308,13 +308,13 @@ void mainwindows_redraw(void) { GSList *tmp; - screen_refresh_freeze(); + term_refresh_freeze(); for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) { MAIN_WINDOW_REC *rec = tmp->data; gui_window_redraw(rec->active); } - screen_refresh_thaw(); + term_refresh_thaw(); } static int mainwindows_compare(MAIN_WINDOW_REC *w1, MAIN_WINDOW_REC *w2) @@ -453,7 +453,7 @@ void mainwindows_resize(int width, int height) old_screen_width = width; old_screen_height = height; - screen_refresh_freeze(); + term_refresh_freeze(); if (ydiff < 0) mainwindows_resize_smaller(xdiff, ydiff); else if (ydiff > 0) @@ -462,7 +462,7 @@ void mainwindows_resize(int width, int height) mainwindows_resize_horiz(xdiff); signal_emit("terminal resized", 0); - screen_refresh_thaw(); + term_refresh_thaw(); irssi_redraw(); } @@ -492,7 +492,7 @@ int mainwindows_reserve_lines(int top, int bottom) ret = screen_reserved_bottom; screen_reserved_bottom += bottom; - window = mainwindows_find_upper(screen_height); + window = mainwindows_find_upper(term_height); if (window != NULL) { window->last_line -= bottom; mainwindow_resize(window, 0, -bottom); @@ -706,7 +706,7 @@ static void cmd_window_balance(void) windows = g_slist_length(mainwindows); if (windows == 1) return; - avail_size = screen_height - screen_reserved_top-screen_reserved_bottom; + avail_size = term_height - screen_reserved_top-screen_reserved_bottom; unit_size = avail_size/windows; bigger_units = avail_size%windows; @@ -1033,8 +1033,8 @@ static void sig_window_print_info(WINDOW_REC *win) void mainwindows_init(void) { - old_screen_width = screen_width; - old_screen_height = screen_height; + old_screen_width = term_width; + old_screen_height = term_height; mainwindows = NULL; active_mainwin = NULL; diff --git a/src/fe-text/mainwindows.h b/src/fe-text/mainwindows.h index 4021b8f1..6306758f 100644 --- a/src/fe-text/mainwindows.h +++ b/src/fe-text/mainwindows.h @@ -2,7 +2,7 @@ #define __MAINWINDOWS_H #include "fe-windows.h" -#include "screen.h" +#include "term.h" #define WINDOW_MIN_SIZE 2 @@ -12,7 +12,7 @@ typedef struct { WINDOW_REC *active; - SCREEN_WINDOW *screen_win; + TERM_WINDOW *screen_win; int sticky_windows; /* number of sticky windows */ int first_line, last_line; /* first/last line used by this window (0..x) (includes statusbars) */ diff --git a/src/fe-text/statusbar-items.c b/src/fe-text/statusbar-items.c index 6ef22dbc..70547daf 100644 --- a/src/fe-text/statusbar-items.c +++ b/src/fe-text/statusbar-items.c @@ -277,8 +277,8 @@ static void item_input(SBAR_ITEM_REC *item, int get_size_only) GUI_ENTRY_REC *rec; if (get_size_only) { - item->min_size = 2+screen_width/10; - item->max_size = screen_width; + item->min_size = 2+term_width/10; + item->max_size = term_width; return; } diff --git a/src/fe-text/statusbar.c b/src/fe-text/statusbar.c index ed9a8ea1..0ed75fed 100644 --- a/src/fe-text/statusbar.c +++ b/src/fe-text/statusbar.c @@ -235,7 +235,7 @@ static void statusbar_redraw_items(STATUSBAR_REC *bar) if (bar->parent_window != NULL) active_win = bar->parent_window->active; - statusbar_resize_items(bar, screen_width); + statusbar_resize_items(bar, term_width); xpos = 0; for (tmp = bar->items; tmp != NULL; tmp = tmp->next) { @@ -248,7 +248,7 @@ static void statusbar_redraw_items(STATUSBAR_REC *bar) } } - rxpos = screen_width; + rxpos = term_width; for (tmp = bar->items; tmp != NULL; tmp = tmp->next) { SBAR_ITEM_REC *rec = tmp->data; @@ -282,10 +282,10 @@ void statusbar_redraw(STATUSBAR_REC *bar) if (bar == NULL) { if (active_statusbar_group != NULL) { - screen_refresh_freeze(); + term_refresh_freeze(); g_slist_foreach(active_statusbar_group->bars, (GFunc) statusbar_redraw, NULL); - screen_refresh_thaw(); + term_refresh_thaw(); } return; } @@ -295,7 +295,7 @@ void statusbar_redraw(STATUSBAR_REC *bar) g_free(str); statusbar_redraw_items(bar); - screen_refresh(NULL); + term_refresh(NULL); } void statusbar_item_redraw(SBAR_ITEM_REC *item) @@ -317,7 +317,7 @@ void statusbar_item_redraw(SBAR_ITEM_REC *item) } else { /*FIXME:fprintf(stderr, "%s redrawing", item->config->name);*/ item->func(item, FALSE); - screen_refresh(NULL); + term_refresh(NULL); } active_win = old_active_win; @@ -360,7 +360,7 @@ static void statusbars_recalc_ypos(STATUSBAR_REC *bar) /* get the Y-position for the first statusbar */ if (bar->config->type == STATUSBAR_TYPE_ROOT) { ypos = bar->config->placement == STATUSBAR_TOP ? 0 : - screen_height - g_slist_length(bar_group); + term_height - g_slist_length(bar_group); } else { ypos = bar->config->placement == STATUSBAR_TOP ? bar->parent_window->first_line : diff --git a/src/fe-text/term-curses.c b/src/fe-text/term-curses.c new file mode 100644 index 00000000..02ac826b --- /dev/null +++ b/src/fe-text/term-curses.c @@ -0,0 +1,368 @@ +/* + term-curses.c : irssi + + Copyright (C) 1999-2001 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "settings.h" + +#include "term.h" +#include "mainwindows.h" + +#if defined(USE_NCURSES) && !defined(RENAMED_NCURSES) +# include +#else +# include +#endif +#include +#include + +#ifndef COLOR_PAIRS +# define COLOR_PAIRS 64 +#endif + +#if defined (TIOCGWINSZ) && defined (HAVE_CURSES_RESIZETERM) +# define USE_RESIZE_TERM +#endif + +#ifndef _POSIX_VDISABLE +# define _POSIX_VDISABLE 0 +#endif + +struct _TERM_WINDOW { + int x, y; + int width, height; + WINDOW *win; +}; + +TERM_WINDOW *root_window; +int term_width, term_height; + +static int curs_x, curs_y; +static int freeze_refresh; +static struct termios old_tio; + +static int init_curses(void) +{ + char ansi_tab[8] = { 0, 4, 2, 6, 1, 5, 3, 7 }; + int num; + struct termios tio; + + if (!initscr()) + return FALSE; + + cbreak(); noecho(); idlok(stdscr, 1); +#ifdef HAVE_CURSES_IDCOK + /*idcok(stdscr, 1); - disabled currently, causes redrawing problems with NetBSD */ +#endif + intrflush(stdscr, FALSE); nodelay(stdscr, TRUE); + + /* Disable INTR, QUIT, VDSUSP and SUSP keys */ + if (tcgetattr(0, &old_tio) == 0) { + memcpy(&tio, &old_tio, sizeof(tio)); + tio.c_cc[VINTR] = _POSIX_VDISABLE; + tio.c_cc[VQUIT] = _POSIX_VDISABLE; +#ifdef VDSUSP + tio.c_cc[VDSUSP] = _POSIX_VDISABLE; +#endif +#ifdef VSUSP + tio.c_cc[VSUSP] = _POSIX_VDISABLE; +#endif + tcsetattr(0, 0, &tio); + } + + if (has_colors()) + start_color(); + else if (term_use_colors) + term_use_colors = FALSE; + +#ifdef HAVE_NCURSES_USE_DEFAULT_COLORS + /* this lets us to use the "default" background color for colors <= 7 so + background pixmaps etc. show up right */ + use_default_colors(); + + for (num = 1; num < COLOR_PAIRS; num++) + init_pair(num, ansi_tab[num & 7], num <= 7 ? -1 : ansi_tab[num >> 3]); + + init_pair(63, 0, -1); /* hm.. not THAT good idea, but probably more + people want dark grey than white on white.. */ +#else + for (num = 1; num < COLOR_PAIRS; num++) + init_pair(num, ansi_tab[num & 7], ansi_tab[num >> 3]); + init_pair(63, 0, 0); +#endif + + clear(); + return TRUE; +} + +static int term_init_int(void) +{ + int ret; + + ret = init_curses(); + if (!ret) return 0; + + curs_x = curs_y = 0; + freeze_refresh = 0; + + root_window = g_new0(TERM_WINDOW, 1); + root_window->win = stdscr; + + term_width = COLS; + term_height = LINES; + return ret; +} + +static void term_deinit_int(void) +{ + tcsetattr(0, 0, &old_tio); + + endwin(); + g_free_and_null(root_window); +} + +int term_init(void) +{ + if (!term_init_int()) + return FALSE; + + settings_add_int("lookandfeel", "default_color", 7); + term_common_init(); + return TRUE; +} + +void term_deinit(void) +{ + term_common_deinit(); + term_deinit_int(); +} + +/* Resize terminal - if width or height is negative, + the new size is unknown and should be figured out somehow */ +void term_resize(int width, int height) +{ +#ifdef HAVE_CURSES_RESIZETERM + if (width < 0 || height < 0) { +#endif + term_deinit_int(); + term_init_int(); + mainwindows_recreate(); +#ifdef HAVE_CURSES_RESIZETERM + } else if (term_width != width || term_height != height) { + term_width = width; + term_height = height; + resizeterm(term_height, term_width); + } +#endif +} + +/* Returns TRUE if terminal has colors */ +int term_has_colors(void) +{ + return has_colors(); +} + +/* Force the colors on any way you can */ +void term_force_colors(int set) +{ + /* don't do anything with curses */ +} + +/* Clear screen */ +void term_clear(void) +{ + clear(); +} + +/* Beep */ +void term_beep(void) +{ + beep(); +} + +/* Create a new window in terminal */ +TERM_WINDOW *term_window_create(int x, int y, int width, int height) +{ + TERM_WINDOW *window; + + window = g_new0(TERM_WINDOW, 1); + window->x = x; window->y = y; + window->width = width; window->height = height; + window->win = newwin(height, width, y, x); + idlok(window->win, 1); + + return window; +} + +/* Destroy a terminal window */ +void term_window_destroy(TERM_WINDOW *window) +{ + delwin(window->win); + g_free(window); +} + +/* Move/resize a window */ +void term_window_move(TERM_WINDOW *window, int x, int y, + int width, int height) +{ + /* some checks to make sure the window is visible in screen, + otherwise curses could get nasty and not show our window anymore. */ + if (width < 1) width = 1; + if (height < 1) height = 1; + if (x+width > term_width) x = term_width-width; + if (y+height > term_height) y = term_height-height; + +#ifdef HAVE_CURSES_WRESIZE + if (window->width != width || window->height != height) + wresize(window->win, height, width); + if (window->x != x || window->y != y) + mvwin(window->win, y, x); +#else + if (window->width != width || window->height != height || + window->x != x || window->y != y) { + delwin(window->win); + window->win = newwin(height, width, y, x); + idlok(window->win, 1); + } +#endif + window->x = x; window->y = y; + window->width = width; window->height = height; +} + +/* Clear window */ +void term_window_clear(TERM_WINDOW *window) +{ + werase(window->win); +} + +/* Scroll window up/down */ +void term_window_scroll(TERM_WINDOW *window, int count) +{ + scrollok(window->win, TRUE); + wscrl(window->win, count); + scrollok(window->win, FALSE); +} + +static int get_attr(int color) +{ + int attr; + + if (!term_use_colors) + attr = (color & 0x70) ? A_REVERSE : 0; + else if (((color & 0x0f) == 8) && (color & ATTR_BOLD) == 0) + attr = (A_DIM | COLOR_PAIR(63)); + else if ((color & 0x77) == 0) + attr = A_NORMAL; + else { + if (color & ATTR_RESETFG) { + color &= ~0x0f; + color |= settings_get_int("default_color"); + } + attr = (COLOR_PAIR((color&7) + (color&0x70)/2)); + } + + if ((color & 0x08) || (color & ATTR_BOLD)) attr |= A_BOLD; + if (color & ATTR_BLINK) attr |= A_BLINK; + + if (color & ATTR_UNDERLINE) attr |= A_UNDERLINE; + if (color & ATTR_REVERSE) attr |= A_REVERSE; + return attr; +} + +/* Change active color */ +void term_set_color(TERM_WINDOW *window, int col) +{ + wattrset(window->win, get_attr(col)); + wbkgdset(window->win, ' ' | get_attr(col)); +} + +void term_move(TERM_WINDOW *window, int x, int y) +{ + wmove(window->win, y, x); +} + +void term_addch(TERM_WINDOW *window, int chr) +{ + waddch(window->win, chr); +} + +void term_addstr(TERM_WINDOW *window, char *str) +{ + waddstr(window->win, str); +} + +void term_clrtoeol(TERM_WINDOW *window) +{ + wclrtoeol(window->win); +} + +void term_move_cursor(int x, int y) +{ + curs_x = x; + curs_y = y; +} + +void term_refresh_freeze(void) +{ + freeze_refresh++; +} + +void term_refresh_thaw(void) +{ + if (freeze_refresh > 0) { + freeze_refresh--; + if (freeze_refresh == 0) term_refresh(NULL); + } +} + +void term_refresh(TERM_WINDOW *window) +{ + if (window != NULL) + wnoutrefresh(window->win); + + if (freeze_refresh == 0) { + move(curs_y, curs_x); + wnoutrefresh(stdscr); + doupdate(); + } +} + +void term_stop(void) +{ + term_deinit_int(); + kill(getpid(), SIGSTOP); + term_init_int(); + irssi_redraw(); +} + +int term_getch(void) +{ + int key; + + key = getch(); + if (key == ERR) + return -1; + +#ifdef KEY_RESIZE + if (key == KEY_RESIZE) + return -1; +#endif + + return key; +} diff --git a/src/fe-text/term-terminfo.c b/src/fe-text/term-terminfo.c new file mode 100644 index 00000000..1f0e8974 --- /dev/null +++ b/src/fe-text/term-terminfo.c @@ -0,0 +1,296 @@ +/* + term-terminfo.c : irssi + + Copyright (C) 2001 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "signals.h" +#include "term.h" +#include "terminfo-core.h" + +#include + +struct _TERM_WINDOW { + /* Terminal to use for window */ + TERM_REC *term; + + /* Area for window in terminal */ + int x, y; + int width, height; +}; + +TERM_WINDOW *root_window; +int term_width, term_height; + +static int curs_x, curs_y; +static int last_fg, last_bg, last_attrs; +static int redraw_needed, redraw_tag; + +/* SIGCONT handler */ +static void sig_cont(int p) +{ + redraw_needed = TRUE; + terminfo_cont(current_term); +} + +static int redraw_timeout(void) +{ + if (redraw_needed) { + irssi_redraw(); + redraw_needed = FALSE; + } + + return 1; +} + +int term_init(void) +{ + struct sigaction act; + + last_fg = last_bg = -1; + last_attrs = 0; + + current_term = terminfo_core_init(stdin, stdout); + if (current_term == NULL) + return FALSE; + + /* grab CONT signal */ + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + act.sa_handler = sig_cont; + sigaction(SIGCONT, &act, NULL); + redraw_tag = g_timeout_add(500, (GSourceFunc) redraw_timeout, NULL); + + curs_x = curs_y = 0; + term_width = current_term->width; + term_height = current_term->height; + root_window = term_window_create(0, 0, term_width, term_height); + + term_common_init(); + return TRUE; +} + +void term_deinit(void) +{ + g_source_remove(redraw_tag); + + term_common_deinit(); + terminfo_core_deinit(current_term); +} + +/* Resize terminal - if width or height is negative, + the new size is unknown and should be figured out somehow */ +void term_resize(int width, int height) +{ + if (width < 0 || height < 0) { + terminfo_resize(current_term); + width = current_term->width; + height = current_term->height; + } + + if (term_width != width || term_height != height) { + term_width = current_term->width = width; + term_height = current_term->height = height; + term_window_move(root_window, 0, 0, term_width, term_height); + } +} + +/* Returns TRUE if terminal has colors */ +int term_has_colors(void) +{ + return terminfo_has_colors(current_term); +} + +/* Force the colors on any way you can */ +void term_force_colors(int set) +{ + terminfo_setup_colors(current_term, set); +} + +/* Clear screen */ +void term_clear(void) +{ + terminfo_clear(); +} + +/* Beep */ +void term_beep(void) +{ + /* FIXME */ +} + +/* Create a new window in terminal */ +TERM_WINDOW *term_window_create(int x, int y, int width, int height) +{ + TERM_WINDOW *window; + + window = g_new0(TERM_WINDOW, 1); + window->term = current_term; + window->x = x; window->y = y; + window->width = width; window->height = height; + return window; +} + +/* Destroy a terminal window */ +void term_window_destroy(TERM_WINDOW *window) +{ + g_free(window); +} + +/* Move/resize a window */ +void term_window_move(TERM_WINDOW *window, int x, int y, + int width, int height) +{ + window->x = x; + window->y = y; + window->width = width; + window->height = height; +} + +/* Clear window */ +void term_window_clear(TERM_WINDOW *window) +{ + int y; + + terminfo_set_normal(); + for (y = 0; y < window->height; y++) { + terminfo_move(0, window->y+y); + terminfo_clrtoeol(); + } +} + +/* Scroll window up/down */ +void term_window_scroll(TERM_WINDOW *window, int count) +{ + terminfo_scroll(window->y, window->y+window->height-1, count); +} + +/* Change active color */ +void term_set_color(TERM_WINDOW *window, int col) +{ + int set_normal; + + set_normal = ((col & ATTR_RESETFG) && last_fg != -1) || + ((col & ATTR_RESETBG) && last_bg != -1); + if (((last_attrs & ATTR_BOLD) && (col & ATTR_BOLD) == 0) || + ((last_attrs & ATTR_BLINK) && (col & ATTR_BLINK) == 0)) { + /* we'll need to get rid of bold/blink - this can only be + done with setting the default color */ + set_normal = TRUE; + } + + if (set_normal) { + last_fg = last_bg = -1; + last_attrs = 0; + terminfo_set_normal(); + } + + /* reversed text (use standout) */ + if (col & ATTR_REVERSE) { + if ((last_attrs & ATTR_REVERSE) == 0) + terminfo_set_standout(TRUE); + } else if (last_attrs & ATTR_REVERSE) + terminfo_set_standout(FALSE); + + /* set foreground color */ + if ((col & 0x0f) != last_fg && + ((col & 0x0f) != 0 || (col & ATTR_RESETFG) == 0)) { + last_fg = col & 0x0f; + terminfo_set_fg(last_fg); + } + + /* set background color */ + if (col & ATTR_BLINK) + col |= 0x80; + else if (col & 0x80) + col |= ATTR_BLINK; + + if ((col & 0xf0) >> 4 != last_bg && + ((col & 0xf0) != 0 || (col & ATTR_RESETBG) == 0)) { + last_bg = (col & 0xf0) >> 4; + terminfo_set_bg(last_bg); + } + + /* bold */ + if (col & 0x08) + col |= ATTR_BOLD; + else if (col & ATTR_BOLD) + terminfo_set_bold(); + + /* underline */ + if (col & ATTR_UNDERLINE) { + if ((last_attrs & ATTR_UNDERLINE) == 0) + terminfo_set_uline(TRUE); + } else if (last_attrs & ATTR_UNDERLINE) + terminfo_set_uline(FALSE); + + last_attrs = col & ~0xff; +} + +void term_move(TERM_WINDOW *window, int x, int y) +{ + terminfo_move(x+window->x, y+window->y); +} + +void term_addch(TERM_WINDOW *window, int chr) +{ + putc(chr, window->term->out); +} + +void term_addstr(TERM_WINDOW *window, char *str) +{ + fputs(str, window->term->out); +} + +void term_clrtoeol(TERM_WINDOW *window) +{ + terminfo_clrtoeol(); +} + +void term_move_cursor(int x, int y) +{ + curs_x = x; + curs_y = y; +} + +void term_refresh(TERM_WINDOW *window) +{ + terminfo_move(curs_x, curs_y); + fflush(window != NULL ? window->term->out : current_term->out); +} + +void term_refresh_freeze(void) +{ +} + +void term_refresh_thaw(void) +{ +} + +void term_stop(void) +{ + terminfo_stop(current_term); + kill(getpid(), SIGSTOP); + terminfo_cont(current_term); + irssi_redraw(); +} + +int term_getch(void) +{ + return fgetc(current_term->in); +} diff --git a/src/fe-text/term.c b/src/fe-text/term.c new file mode 100644 index 00000000..97764ac1 --- /dev/null +++ b/src/fe-text/term.c @@ -0,0 +1,140 @@ +/* + term.c : irssi + + Copyright (C) 2001 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "signals.h" +#include "commands.h" +#include "settings.h" + +#include "term.h" +#include "mainwindows.h" + +#ifdef HAVE_SYS_IOCTL_H +# include +#endif +#include +#include + +#define RESIZE_TIMEOUT 500 /* how often to check if the terminal has been resized */ +#define MIN_SCREEN_WIDTH 20 + +int term_use_colors; + +#ifdef SIGWINCH +static int resize_timeout_tag; +#endif +static int resize_needed; + +static int resize_timeout(void) +{ +#ifdef TIOCGWINSZ + struct winsize ws; +#endif + + if (!resize_needed) + return TRUE; + + resize_needed = FALSE; + +#ifdef TIOCGWINSZ + /* Get new window size */ + if (ioctl(0, TIOCGWINSZ, &ws) < 0) + return TRUE; + + if (ws.ws_row == term_height && ws.ws_col == term_width) { + /* Same size, abort. */ + return TRUE; + } + + if (ws.ws_col < MIN_SCREEN_WIDTH) + ws.ws_col = MIN_SCREEN_WIDTH; + + term_resize(ws.ws_col, ws.ws_row); +#else + term_resize(-1, -1); +#endif + mainwindows_resize(term_width, term_height); + + return TRUE; +} + +#ifdef SIGWINCH +static void sig_winch(int p) +{ + resize_needed = TRUE; +} +#endif + +static void cmd_resize(void) +{ + resize_needed = TRUE; + resize_timeout(); +} + +static void read_settings(void) +{ + int old_colors = term_use_colors; + + if (settings_get_bool("force_colors")) + term_use_colors = TRUE; + else { + term_use_colors = settings_get_bool("colors"); + if (term_use_colors && !term_has_colors()) + term_use_colors = FALSE; + } + + if (term_use_colors != old_colors) + irssi_redraw(); +} + +void term_common_init(void) +{ +#ifdef SIGWINCH + struct sigaction act; +#endif + settings_add_bool("lookandfeel", "colors", TRUE); + settings_add_bool("lookandfeel", "force_colors", FALSE); + term_use_colors = settings_get_bool("colors"); + + signal_add("beep", (SIGNAL_FUNC) term_beep); + signal_add("setup changed", (SIGNAL_FUNC) read_settings); + command_bind("resize", NULL, (SIGNAL_FUNC) cmd_resize); + +#ifdef SIGWINCH + sigemptyset (&act.sa_mask); + act.sa_flags = 0; + act.sa_handler = sig_winch; + sigaction(SIGWINCH, &act, NULL); + + resize_timeout_tag = g_timeout_add(RESIZE_TIMEOUT, + (GSourceFunc) resize_timeout, NULL); +#endif +} + +void term_common_deinit(void) +{ +#ifdef SIGWINCH + g_source_remove(resize_timeout_tag); +#endif + + command_unbind("resize", (SIGNAL_FUNC) cmd_resize); + signal_remove("beep", (SIGNAL_FUNC) term_beep); + signal_remove("setup changed", (SIGNAL_FUNC) read_settings); +} diff --git a/src/fe-text/term.h b/src/fe-text/term.h new file mode 100644 index 00000000..5aa89e59 --- /dev/null +++ b/src/fe-text/term.h @@ -0,0 +1,81 @@ +#ifndef __TERM_H +#define __TERM_H + +typedef struct _TERM_WINDOW TERM_WINDOW; + +#define ATTR_RESETFG 0x0100 +#define ATTR_RESETBG 0x0200 +#define ATTR_BOLD 0x0400 +#define ATTR_BLINK 0x0800 +#define ATTR_UNDERLINE 0x1000 +#define ATTR_REVERSE 0x2000 + +#define ATTR_RESET (ATTR_RESETFG|ATTR_RESETBG) + +#define ATTR_NOCOLORS (ATTR_UNDERLINE|ATTR_REVERSE) + +#ifdef WANT_BIG5 +/* XXX I didn't check the encoding range of big5+. This is standard big5. */ +# define is_big5_los(lo) (((char)0x40<=lo)&&(lo<=(char)0x7E)) /* standard */ +# define is_big5_lox(lo) (((char)0x80<=lo)&&(lo<=(char)0xFE)) /* extended */ +# define is_big5_hi(hi) (((char)0x81<=hi)&&(hi<=(char)0xFE)) +# define is_big5(hi,lo) is_big5_hi(hi) && (is_big5_los(lo) || is_big5_lox(lo)) +#endif + +extern TERM_WINDOW *root_window; +extern int term_width, term_height, term_use_colors; + +/* Initialize / deinitialize terminal */ +int term_init(void); +void term_deinit(void); + +/* Resize terminal - if width or height is negative, + the new size is unknown and should be figured out somehow */ +void term_resize(int width, int height); + +/* Returns TRUE if terminal has colors */ +int term_has_colors(void); +/* Force the colors on any way you can */ +void term_force_colors(int set); + +/* Clear screen */ +void term_clear(void); +/* Beep */ +void term_beep(void); + +/* Create a new window in terminal */ +TERM_WINDOW *term_window_create(int x, int y, int width, int height); +/* Destroy a terminal window */ +void term_window_destroy(TERM_WINDOW *window); + +/* Move/resize window */ +void term_window_move(TERM_WINDOW *window, int x, int y, + int width, int height); +/* Clear window */ +void term_window_clear(TERM_WINDOW *window); +/* Scroll window up/down */ +void term_window_scroll(TERM_WINDOW *window, int count); + +void term_set_color(TERM_WINDOW *window, int col); + +void term_move(TERM_WINDOW *window, int x, int y); +void term_addch(TERM_WINDOW *window, int chr); +void term_addstr(TERM_WINDOW *window, char *str); +void term_clrtoeol(TERM_WINDOW *window); + +void term_move_cursor(int x, int y); + +void term_refresh_freeze(void); +void term_refresh_thaw(void); +void term_refresh(TERM_WINDOW *window); + +void term_stop(void); +int term_getch(void); + +/* internal */ +void term_common_init(void); +void term_common_deinit(void); + +void term_force_resize(void); + +#endif diff --git a/src/fe-text/terminfo-core.c b/src/fe-text/terminfo-core.c new file mode 100644 index 00000000..08c76d10 --- /dev/null +++ b/src/fe-text/terminfo-core.c @@ -0,0 +1,584 @@ +#include "module.h" +#include "signals.h" +#include "terminfo-core.h" + +#ifndef _POSIX_VDISABLE +# define _POSIX_VDISABLE 0 +#endif + +#define tput(s) tputs(s, 0, term_putchar) +inline static int term_putchar(int c) +{ + return fputc(c, current_term->out); +} + +/* Don't bother including curses.h because of these - + they might not even be defined there */ +char *tparm(); +int tputs(); + +#ifdef HAVE_TERMINFO +int setupterm(); +char *tigetstr(); +int tigetnum(); +int tigetflag(); +#define term_getstr(x, buffer) tigetstr(x.ti_name) +#define term_getnum(x) tigetnum(x.ti_name); +#define term_getflag(x) tigetflag(x.ti_name); +#else +int tgetent(); +char *tgetstr(); +int tgetnum(); +int tgetflag(); +#define term_getstr(x, buffer) tgetstr(x.tc_name, &buffer) +#define term_getnum(x) tgetnum(x.tc_name) +#define term_getflag(x) tgetflag(x.tc_name) +#endif + +#define CAP_TYPE_FLAG 0 +#define CAP_TYPE_INT 1 +#define CAP_TYPE_STR 2 + +typedef struct { + const char *ti_name; /* terminfo name */ + const char *tc_name; /* termcap name */ + int type; + void *ptr; +} TERMINFO_REC; + +TERM_REC *current_term; +static TERM_REC temp_term; /* not really used for anything */ + +/* Define only what we might need */ +static TERMINFO_REC tcaps[] = { + /* Terminal size */ + { "cols", "co", CAP_TYPE_INT, &temp_term.width }, + { "lines", "li", CAP_TYPE_INT, &temp_term.height }, + + /* Cursor movement */ + { "smcup", "ti", CAP_TYPE_STR, &temp_term.TI_smcup }, + { "rmcup", "te", CAP_TYPE_STR, &temp_term.TI_rmcup }, + { "cup", "cm", CAP_TYPE_STR, &temp_term.TI_cup }, + { "hpa", "ch", CAP_TYPE_STR, &temp_term.TI_hpa }, + { "vpa", "vh", CAP_TYPE_STR, &temp_term.TI_vpa }, + { "xvpa", "YD", CAP_TYPE_FLAG, &temp_term.TI_xvpa }, + { "hvpa", "YA", CAP_TYPE_FLAG, &temp_term.TI_xhpa }, + + /* Scrolling */ + { "csr", "cs", CAP_TYPE_STR, &temp_term.TI_csr }, + { "wind", "wi", CAP_TYPE_STR, &temp_term.TI_wind }, + { "ri", "sr", CAP_TYPE_STR, &temp_term.TI_ri }, + { "rin", "SR", CAP_TYPE_STR, &temp_term.TI_rin }, + { "ind", "sf", CAP_TYPE_STR, &temp_term.TI_ind }, + { "indn", "SF", CAP_TYPE_STR, &temp_term.TI_indn }, + { "il", "AL", CAP_TYPE_STR, &temp_term.TI_il }, + { "il1", "al", CAP_TYPE_STR, &temp_term.TI_il1 }, + { "dl", "DL", CAP_TYPE_STR, &temp_term.TI_dl }, + { "dl1", "dl", CAP_TYPE_STR, &temp_term.TI_dl1 }, + + /* Clearing screen */ + { "clear", "cl", CAP_TYPE_STR, &temp_term.TI_clear }, + { "ed", "cd", CAP_TYPE_STR, &temp_term.TI_ed }, + + /* Clearing to end of line */ + { "el", "ce", CAP_TYPE_STR, &temp_term.TI_el }, + + /* Colors */ + { "sgr0", "me", CAP_TYPE_STR, &temp_term.TI_sgr0 }, + { "smul", "us", CAP_TYPE_STR, &temp_term.TI_smul }, + { "rmul", "ue", CAP_TYPE_STR, &temp_term.TI_rmul }, + { "smso", "so", CAP_TYPE_STR, &temp_term.TI_smso }, + { "rmso", "se", CAP_TYPE_STR, &temp_term.TI_rmso }, + { "bold", "md", CAP_TYPE_STR, &temp_term.TI_bold }, + { "blink", "mb", CAP_TYPE_STR, &temp_term.TI_blink }, + { "setaf", "AF", CAP_TYPE_STR, &temp_term.TI_setaf }, + { "setab", "AB", CAP_TYPE_STR, &temp_term.TI_setab }, + { "setf", "Sf", CAP_TYPE_STR, &temp_term.TI_setf }, + { "setb", "Sb", CAP_TYPE_STR, &temp_term.TI_setb }, + + /* Beep */ + { "bel", "bl", CAP_TYPE_STR, &temp_term.TI_bel }, +}; + +/* Move cursor (cursor_address / cup) */ +static void _move_cup(TERM_REC *term, int x, int y) +{ + tput(tparm(term->TI_cup, y, x)); +} + +/* Move cursor (column_address+row_address / hpa+vpa) */ +static void _move_pa(TERM_REC *term, int x, int y) +{ + tput(tparm(term->TI_hpa, x)); + tput(tparm(term->TI_vpa, y)); +} + +#define scroll_region_setup(term, y1, y2) \ + if ((term)->TI_csr != NULL) \ + tput(tparm((term)->TI_csr, y1, y2)); \ + else \ + tput(tparm((term)->TI_wind, y1, y2, 0, (term)->width-1)); + +/* Scroll (change_scroll_region+parm_rindex+parm_index / csr+rin+indn) */ +static void _scroll_region(TERM_REC *term, int y1, int y2, int count) +{ + /* setup the scrolling region to wanted area */ + scroll_region_setup(term, y1, y2); + + term->move(term, 0, y1); + if (count > 0) { + term->move(term, 0, y2); + tput(tparm(term->TI_indn, count, count)); + } else if (count < 0) { + term->move(term, 0, y1); + tput(tparm(term->TI_rin, -count, -count)); + } + + /* reset the scrolling region to full screen */ + scroll_region_setup(term, 0, term->height-1); +} + +/* Scroll (change_scroll_region+scroll_reverse+scroll_forward / csr+ri+ind) */ +static void _scroll_region_1(TERM_REC *term, int y1, int y2, int count) +{ + int i; + + /* setup the scrolling region to wanted area */ + scroll_region_setup(term, y1, y2); + + if (count > 0) { + term->move(term, 0, y2); + for (i = 0; i < count; i++) + tput(tparm(term->TI_ind)); + } else if (count < 0) { + term->move(term, 0, y1); + for (i = count; i < 0; i++) + tput(tparm(term->TI_ri)); + tput(tparm(term->TI_rin, -count, -count)); + } + + /* reset the scrolling region to full screen */ + scroll_region_setup(term, 0, term->height-1); +} + +/* Scroll (parm_insert_line+parm_delete_line / il+dl) */ +static void _scroll_line(TERM_REC *term, int y1, int y2, int count) +{ + if (count > 0) { + term->move(term, 0, y1); + tput(tparm(term->TI_dl, count, count)); + term->move(term, 0, y2-count+1); + tput(tparm(term->TI_il, count, count)); + } else if (count < 0) { + term->move(term, 0, y2+count+1); + tput(tparm(term->TI_dl, -count, -count)); + term->move(term, 0, y1); + tput(tparm(term->TI_il, -count, -count)); + } +} + +/* Scroll (insert_line+delete_line / il1+dl1) */ +static void _scroll_line_1(TERM_REC *term, int y1, int y2, int count) +{ + int i; + + if (count > 0) { + term->move(term, 0, y1); + for (i = 0; i < count; i++) + tput(tparm(term->TI_dl1)); + term->move(term, 0, y2-count+1); + for (i = 0; i < count; i++) + tput(tparm(term->TI_il1)); + } else if (count < 0) { + term->move(term, 0, y2+count+1); + for (i = count; i < 0; i++) + tput(tparm(term->TI_dl1)); + term->move(term, 0, y1); + for (i = count; i < 0; i++) + tput(tparm(term->TI_il1)); + } +} + +/* Clear screen (clear_screen / clear) */ +static void _clear_screen(TERM_REC *term) +{ + tput(tparm(term->TI_clear)); +} + +/* Clear screen (clr_eos / ed) */ +static void _clear_eos(TERM_REC *term) +{ + term->move(term, 0, 0); + tput(tparm(term->TI_ed)); +} + +/* Clear screen (parm_delete_line / dl) */ +static void _clear_del(TERM_REC *term) +{ + term->move(term, 0, 0); + tput(tparm(term->TI_dl, term->height, term->height)); +} + +/* Clear screen (delete_line / dl1) */ +static void _clear_del_1(TERM_REC *term) +{ + int i; + + term->move(term, 0, 0); + for (i = 0; i < term->height; i++) + tput(tparm(term->TI_dl1)); +} + +/* Clear to end of line (clr_eol / el) */ +static void _clrtoeol(TERM_REC *term) +{ + tput(tparm(term->TI_el)); +} + +/* Reset all terminal attributes */ +static void _set_normal(TERM_REC *term) +{ + tput(tparm(term->TI_normal)); +} + +/* Bold on */ +static void _set_bold(TERM_REC *term) +{ + tput(tparm(term->TI_bold)); +} + +/* Underline on/off */ +static void _set_uline(TERM_REC *term, int set) +{ + tput(tparm(set ? term->TI_smul : term->TI_rmul)); +} + +/* Standout on/off */ +static void _set_standout(TERM_REC *term, int set) +{ + tput(tparm(set ? term->TI_smso : term->TI_rmso)); +} + +/* Change foreground color */ +static void _set_fg(TERM_REC *term, int color) +{ + tput(tparm(term->TI_fg[color & 0x0f])); +} + +/* Change background color */ +static void _set_bg(TERM_REC *term, int color) +{ + tput(tparm(term->TI_bg[color & 0x0f])); +} + +/* Beep */ +static void _beep(TERM_REC *term) +{ + tput(tparm(term->TI_bel)); +} + +static void _ignore(TERM_REC *term) +{ +} + +static void _ignore_parm(TERM_REC *term, int param) +{ +} + +static void term_fill_capabilities(TERM_REC *term) +{ + int i, ival; + char *sval; + void *ptr; + +#ifndef HAVE_TERMINFO + char *tptr = term->buffer2; +#endif + for (i = 0; i < sizeof(tcaps)/sizeof(tcaps[0]); i++) { + ptr = (char *) term + (int) ((char *) tcaps[i].ptr - (char *) &temp_term); + + switch (tcaps[i].type) { + case CAP_TYPE_FLAG: + ival = term_getflag(tcaps[i]); + *(int *)ptr = ival; + break; + case CAP_TYPE_INT: + ival = term_getnum(tcaps[i]); + *(int *)ptr = ival; + break; + case CAP_TYPE_STR: + sval = term_getstr(tcaps[i], tptr); + if (sval == (char *) -1) + *(char **)ptr = NULL; + else + *(char **)ptr = sval; + break; + } + } +} + +/* Terminal was resized - ask the width/height from terminfo again */ +void terminfo_resize(TERM_REC *term) +{ + /* easiest way to do this is just to refill everything, and since + termcap needs that buffer this is probably also the safest way */ + /* FIXME: leaks memory? does this even work? */ + term_fill_capabilities(term); +} + +static void terminfo_colors_deinit(TERM_REC *term) +{ + int i; + + if (terminfo_has_colors(term)) { + for (i = 0; i < 16; i++) { + g_free(term->TI_fg[i]); + g_free(term->TI_bg[i]); + } + + memset(term->TI_fg, 0, sizeof(term->TI_fg)); + memset(term->TI_bg, 0, sizeof(term->TI_fg)); + } +} + +/* Setup colors - if force is set, use ANSI-style colors if + terminal capabilities don't contain color codes */ +void terminfo_setup_colors(TERM_REC *term, int force) +{ + static char ansitab[8] = { 0, 4, 2, 6, 1, 5, 3, 7 }; + const char *bold, *blink; + int i; + + terminfo_colors_deinit(term); + + if (term->TI_setf) { + for (i = 0; i < 8; i++) + term->TI_fg[i] = g_strdup(tparm(term->TI_setf, i, 0)); + } else if (term->TI_setaf) { + for (i = 0; i < 8; i++) + term->TI_fg[i] = g_strdup(tparm(term->TI_setaf, ansitab[i], 0)); + } else if (force) { + for (i = 0; i < 8; i++) + term->TI_fg[i] = g_strdup_printf("\033[%dm", 30+ansitab[i]); + } + + if (term->TI_setb) { + for (i = 0; i < 8; i++) + term->TI_bg[i] = g_strdup(tparm(term->TI_setb, i, 0)); + } else if (term->TI_setab) { + for (i = 0; i < 8; i++) + term->TI_bg[i] = g_strdup(tparm(term->TI_setab, ansitab[i], 0)); + } else if (force) { + for (i = 0; i < 8; i++) + term->TI_bg[i] = g_strdup_printf("\033[%dm", 40+ansitab[i]); + } + + if (term->TI_setf || term->TI_setaf || force) { + term->set_fg = _set_fg; + term->set_bg = _set_bg; + + /* bold fg, blink bg */ + bold = term->TI_bold ? term->TI_bold : ""; + for (i = 0; i < 8; i++) + term->TI_fg[i+8] = g_strconcat(bold, term->TI_fg[i], NULL); + + blink = term->TI_blink ? term->TI_blink : ""; + for (i = 0; i < 8; i++) + term->TI_bg[i+8] = g_strconcat(blink, term->TI_bg[i], NULL); + } else { + /* no colors */ + term->set_fg = term->set_bg = _ignore_parm; + } +} + +static void terminfo_input_init(TERM_REC *term) +{ + tcgetattr(fileno(term->in), &term->old_tio); + memcpy(&term->tio, &term->old_tio, sizeof(term->tio)); + + term->tio.c_lflag &= ~(ICANON | ECHO); /* CBREAK, no ECHO */ + term->tio.c_cc[VMIN] = 0; /* non-blocking read */ + term->tio.c_cc[VTIME] = 0; /* No timer */ + + /* Disable INTR, QUIT, VDSUSP and SUSP keys */ + term->tio.c_cc[VINTR] = _POSIX_VDISABLE; + term->tio.c_cc[VQUIT] = _POSIX_VDISABLE; +#ifdef VDSUSP + term->tio.c_cc[VDSUSP] = _POSIX_VDISABLE; +#endif +#ifdef VSUSP + term->tio.c_cc[VSUSP] = _POSIX_VDISABLE; +#endif + + tcsetattr(fileno(term->in), 0, &term->tio); + +} + +static void terminfo_input_deinit(TERM_REC *term) +{ + tcsetattr(fileno(term->in), 0, &term->old_tio); +} + +void terminfo_cont(TERM_REC *term) +{ + if (term->TI_smcup) + tput(tparm(term->TI_smcup)); + terminfo_input_init(term); +} + +void terminfo_stop(TERM_REC *term) +{ + /* reset colors */ + terminfo_set_normal(); + /* move cursor to bottom of the screen */ + terminfo_move(0, term->height-1); + + /* stop cup-mode */ + if (term->TI_rmcup) + tput(tparm(term->TI_rmcup)); + + /* reset input settings */ + terminfo_input_deinit(term); + fflush(term->out); +} + +static int term_setup(TERM_REC *term) +{ + GString *str; +#ifdef HAVE_TERMINFO + int err; +#endif + char *term_env; + + term_env = getenv("TERM"); + if (term_env == NULL) { + fprintf(term->out, "TERM environment not set\n"); + return 0; + } + +#ifdef HAVE_TERMINFO + if (setupterm(term_env, 1, &err) != 0) { + fprintf(term->out, "setupterm() failed for TERM=%s: %d\n", term_env, err); + return 0; + } +#else + if (tgetent(term->buffer1, term_env) < 1) + { + fprintf(term->out, "Termcap not found for TERM=%s\n", term_env); + return -1; + } +#endif + + term_fill_capabilities(term); + + /* Cursor movement */ + if (term->TI_cup) + term->move = _move_cup; + else if (term->TI_hpa && term->TI_vpa && + !term->TI_xhpa && !term->TI_xvpa) + term->move = _move_pa; + else { + fprintf(term->out, "Terminal doesn't support cursor movement\n"); + return 0; + } + + /* Scrolling */ + if ((term->TI_csr || term->TI_wind) && term->TI_rin && term->TI_indn) + term->scroll = _scroll_region; + else if ((term->TI_csr || term->TI_wind) && term->TI_ri && term->TI_ind) + term->scroll = _scroll_region_1; + else if (term->TI_il && term->TI_dl) + term->scroll = _scroll_line; + else if (term->TI_il1 && term->TI_dl1) + term->scroll = _scroll_line_1; + else { + fprintf(term->out, "Terminal doesn't support scrolling\n"); + return 0; + } + + /* Clearing screen */ + if (term->TI_clear) + term->clear = _clear_screen; + else if (term->TI_ed) + term->clear = _clear_eos; + else if (term->TI_dl) + term->clear = _clear_del; + else if (term->TI_dl1) + term->clear = _clear_del_1; + else { + /* we could do this by line inserts as well, but don't + bother - if some terminal has insert line it most probably + has delete line as well, if not a regular clear screen */ + fprintf(term->out, "Terminal doesn't support clearing screen\n"); + return 0; + } + + /* Clearing to end of line */ + if (term->TI_el) + term->clrtoeol = _clrtoeol; + else { + fprintf(term->out, "Terminal doesn't support clearing to end of line\n"); + return 0; + } + + /* Bold, underline, standout */ + term->set_bold = term->TI_bold ? _set_bold : _ignore; + term->set_uline = term->TI_smul && term->TI_rmul ? + _set_uline : _ignore_parm; + term->set_standout = term->TI_smso && term->TI_rmso ? + _set_standout : _ignore_parm; + + /* Create a string to set all attributes off */ + str = g_string_new(NULL); + if (term->TI_sgr0) + g_string_append(str, term->TI_sgr0); + if (term->TI_rmul && (term->TI_sgr0 == NULL || strcmp(term->TI_rmul, term->TI_sgr0) != 0)) + g_string_append(str, term->TI_rmul); + if (term->TI_rmso && (term->TI_sgr0 == NULL || strcmp(term->TI_rmso, term->TI_sgr0) != 0)) + g_string_append(str, term->TI_rmso); + term->TI_normal = str->str; + g_string_free(str, FALSE); + term->set_normal = _set_normal; + + term->beep = term->TI_bel ? _beep : _ignore; + + terminfo_setup_colors(term, FALSE); + terminfo_cont(term); + return 1; +} + +TERM_REC *terminfo_core_init(FILE *in, FILE *out) +{ + TERM_REC *old_term, *term; + + old_term = current_term; + current_term = term = g_new0(TERM_REC, 1); + + term->in = in; + term->out = out; + + if (!term_setup(term)) { + g_free(term); + term = NULL; + } + + current_term = old_term; + return term; +} + +void terminfo_core_deinit(TERM_REC *term) +{ + TERM_REC *old_term; + + old_term = current_term; + current_term = term; + term->set_normal(term); + current_term = old_term; + + terminfo_stop(term); + + g_free(term->TI_normal); + terminfo_colors_deinit(term); + + g_free(term); +} diff --git a/src/fe-text/terminfo-core.h b/src/fe-text/terminfo-core.h new file mode 100644 index 00000000..480e80c1 --- /dev/null +++ b/src/fe-text/terminfo-core.h @@ -0,0 +1,91 @@ +#ifndef __TERMINFO_CORE_H +#define __TERMINFO_CORE_H + +#include + +#define terminfo_move(x, y) current_term->move(current_term, x, y) +#define terminfo_scroll(y1, y2, count) current_term->scroll(current_term, y1, y2, count) +#define terminfo_clear() current_term->clear(current_term) +#define terminfo_clrtoeol() current_term->clrtoeol(current_term) +#define terminfo_set_fg(color) current_term->set_fg(current_term, color) +#define terminfo_set_bg(color) current_term->set_bg(current_term, color) +#define terminfo_set_normal() current_term->set_normal(current_term) +#define terminfo_set_bold() current_term->set_bold(current_term) +#define terminfo_set_uline(set) current_term->set_uline(current_term, set) +#define terminfo_set_standout(set) current_term->set_standout(current_term, set) +#define terminfo_has_colors(term) (term->TI_fg[0] != NULL) + +typedef struct _TERM_REC TERM_REC; + +struct _TERM_REC { + /* Functions */ + void (*move)(TERM_REC *term, int x, int y); + void (*scroll)(TERM_REC *term, int y1, int y2, int count); + + void (*clear)(TERM_REC *term); + void (*clrtoeol)(TERM_REC *term); + + void (*set_fg)(TERM_REC *term, int color); + void (*set_bg)(TERM_REC *term, int color); + void (*set_normal)(TERM_REC *term); + void (*set_bold)(TERM_REC *term); + void (*set_uline)(TERM_REC *term, int set); + void (*set_standout)(TERM_REC *term, int set); + + void (*beep)(TERM_REC *term); + +#ifndef HAVE_TERMINFO + char buffer1[1024], buffer2[1024]; +#endif + FILE *in, *out; + struct termios tio, old_tio; + + /* Terminal size */ + int width, height; + + /* Cursor movement */ + const char *TI_smcup, *TI_rmcup, *TI_cup; + const char *TI_hpa, *TI_vpa; + int TI_xhpa, TI_xvpa; + + /* Scrolling */ + const char *TI_csr, *TI_wind; + const char *TI_ri, *TI_rin, *TI_ind, *TI_indn; + const char *TI_il, *TI_il1, *TI_dl, *TI_dl1; + + /* Clearing screen */ + const char *TI_clear, *TI_ed; /* + *TI_dl, *TI_dl1; */ + + /* Clearing to end of line */ + const char *TI_el; + + /* Colors */ + const char *TI_sgr0; /* turn off all attributes */ + const char *TI_smul, *TI_rmul; /* underline on/off */ + const char *TI_smso, *TI_rmso; /* standout on/off */ + const char *TI_bold, *TI_blink; + const char *TI_setaf, *TI_setab, *TI_setf, *TI_setb; + + /* Colors - generated and dynamically allocated */ + char *TI_fg[16], *TI_bg[16], *TI_normal; + + /* Beep */ + char *TI_bel; +}; + +extern TERM_REC *current_term; + +TERM_REC *terminfo_core_init(FILE *in, FILE *out); +void terminfo_core_deinit(TERM_REC *term); + +/* Setup colors - if force is set, use ANSI-style colors if + terminal capabilities don't contain color codes */ +void terminfo_setup_colors(TERM_REC *term, int force); + +/* Terminal was resized - ask the width/height from terminfo again */ +void terminfo_resize(TERM_REC *term); + +void terminfo_cont(TERM_REC *term); +void terminfo_stop(TERM_REC *term); + +#endif diff --git a/src/fe-text/textbuffer-commands.c b/src/fe-text/textbuffer-commands.c index 502b3881..348e9d06 100644 --- a/src/fe-text/textbuffer-commands.c +++ b/src/fe-text/textbuffer-commands.c @@ -239,7 +239,7 @@ static void cmd_scrollback_redraw(void) gui = WINDOW_GUI(active_win); - screen_refresh_freeze(); + term_refresh_freeze(); line = textbuffer_view_get_lines(gui->view); while (line != NULL) { next = line->next; @@ -248,7 +248,7 @@ static void cmd_scrollback_redraw(void) } gui_window_redraw(active_win); - screen_refresh_thaw(); + term_refresh_thaw(); } static void cmd_scrollback_status(void) diff --git a/src/fe-text/textbuffer-view.c b/src/fe-text/textbuffer-view.c index adb9a320..cc33f715 100644 --- a/src/fe-text/textbuffer-view.c +++ b/src/fe-text/textbuffer-view.c @@ -20,7 +20,6 @@ #include "module.h" #include "textbuffer-view.h" -#include "screen.h" typedef struct { char *name; @@ -102,6 +101,46 @@ static void textbuffer_cache_unref(TEXT_BUFFER_CACHE_REC *cache) textbuffer_cache_destroy(cache); } +#define FGATTR (ATTR_NOCOLORS | ATTR_RESETBG | ATTR_BLINK | 0xf0) +#define BGATTR (ATTR_NOCOLORS | ATTR_RESETFG | ATTR_BOLD | 0x0f) + +static void update_cmd_color(unsigned char cmd, int *color) +{ + if ((cmd & 0x80) == 0) { + if (cmd & LINE_COLOR_BG) { + /* set background color */ + *color &= BGATTR; + if ((cmd & LINE_COLOR_DEFAULT) == 0) + *color |= (cmd & 0x0f) << 4; + else { + *color |= ATTR_RESETBG; + if (cmd & LINE_COLOR_BLINK) + *color |= ATTR_BLINK; + } + } else { + /* set foreground color */ + *color &= FGATTR; + if ((cmd & LINE_COLOR_DEFAULT) == 0) + *color |= cmd & 0x0f; + else { + *color |= ATTR_RESETFG; + if (cmd & LINE_COLOR_BOLD) + *color |= ATTR_BOLD; + } + } + } else switch (cmd) { + case LINE_CMD_UNDERLINE: + *color ^= ATTR_UNDERLINE; + break; + case LINE_CMD_REVERSE: + *color ^= ATTR_REVERSE; + break; + case LINE_CMD_COLOR0: + *color &= BGATTR; + break; + } +} + static LINE_CACHE_REC * view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) { @@ -115,7 +154,8 @@ view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) g_return_val_if_fail(line->text != NULL, NULL); - xpos = 0; color = 0; indent_pos = view->default_indent; + color = ATTR_RESETFG | ATTR_RESETBG; + xpos = 0; indent_pos = view->default_indent; last_space = last_color = 0; last_space_ptr = NULL; sub = NULL; indent_func = view->default_indent_func; @@ -139,38 +179,17 @@ view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) continue; } - if ((cmd & 0x80) == 0) { - /* set color */ - color = (color & ATTR_NOCOLORS) | cmd; - } else switch (cmd) { - case LINE_CMD_UNDERLINE: - color ^= ATTR_UNDERLINE; - break; - case LINE_CMD_REVERSE: - color ^= ATTR_REVERSE; - break; - case LINE_CMD_COLOR0: - color = color & ATTR_NOCOLORS; - break; - case LINE_CMD_COLOR8: - color &= 0xfff0; - color |= 8|ATTR_COLOR8; - break; - case LINE_CMD_BLINK: - color |= 0x80; - break; - case LINE_CMD_INDENT: + if (cmd == LINE_CMD_INDENT) { /* set indentation position here - don't do it if we're too close to right border */ if (xpos < view->width-5) indent_pos = xpos; - break; - case LINE_CMD_INDENT_FUNC: + } else if (cmd == LINE_CMD_INDENT_FUNC) { memcpy(&indent_func, ptr, sizeof(INDENT_FUNC)); ptr += sizeof(INDENT_FUNC); if (indent_func == NULL) indent_func = view->default_indent_func; - break; - } + } else + update_cmd_color(cmd, &color); continue; } @@ -306,9 +325,9 @@ static int view_line_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, } /* first clear the line */ - screen_set_color(view->window, 0); - screen_move(view->window, 0, ypos); - screen_clrtoeol(view->window); + term_set_color(view->window, ATTR_RESET); + term_move(view->window, 0, ypos); + term_clrtoeol(view->window); if (subline > 0) { indent_func = cache->lines[subline-1].indent_func; @@ -318,8 +337,8 @@ static int view_line_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, color = cache->lines[subline-1].color; } - screen_move(view->window, xpos, ypos); - screen_set_color(view->window, color); + term_move(view->window, xpos, ypos); + term_set_color(view->window, color); /* get the beginning of the next subline */ text_newline = subline == cache->count-1 ? NULL : @@ -335,47 +354,28 @@ static int view_line_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, if (*text == LINE_CMD_EOL || *text == LINE_CMD_FORMAT) break; - if ((*text & 0x80) == 0) { - /* set color */ - color = (color & ATTR_NOCOLORS) | *text; - } else if (*text == LINE_CMD_CONTINUE) { + if (*text == LINE_CMD_CONTINUE) { /* jump to next block */ memcpy(&tmp, text+1, sizeof(unsigned char *)); text = tmp; continue; - } else switch (*text) { - case LINE_CMD_UNDERLINE: - color ^= ATTR_UNDERLINE; - break; - case LINE_CMD_REVERSE: - color ^= ATTR_REVERSE; - break; - case LINE_CMD_COLOR0: - color = color & ATTR_NOCOLORS; - break; - case LINE_CMD_COLOR8: - color &= 0xfff0; - color |= 8|ATTR_COLOR8; - break; - case LINE_CMD_BLINK: - color |= 0x80; - break; - case LINE_CMD_INDENT_FUNC: - text += sizeof(INDENT_FUNC); - break; + } else if (*text == LINE_CMD_INDENT_FUNC) { + text += sizeof(INDENT_FUNC); + } else { + update_cmd_color(*text, &color); + term_set_color(view->window, color); } - screen_set_color(view->window, color); text++; continue; } if ((*text & 127) >= 32) - screen_addch(view->window, *text); + term_addch(view->window, *text); else { /* low-ascii */ - screen_set_color(view->window, ATTR_REVERSE); - screen_addch(view->window, (*text & 127)+'A'-1); - screen_set_color(view->window, color); + term_set_color(view->window, ATTR_RESET|ATTR_REVERSE); + term_addch(view->window, (*text & 127)+'A'-1); + term_set_color(view->window, color); } text++; } @@ -594,9 +594,10 @@ static void view_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, } /* clear the rest of the view */ + term_set_color(view->window, ATTR_RESET); while (lines > 0) { - screen_move(view->window, 0, ypos); - screen_clrtoeol(view->window); + term_move(view->window, 0, ypos); + term_clrtoeol(view->window); ypos++; lines--; } } @@ -682,7 +683,7 @@ static int view_scroll(TEXT_BUFFER_VIEW_REC *view, LINE_REC **lines, whole view */ textbuffer_view_redraw(view); } else { - screen_window_scroll(view->window, realcount); + term_window_scroll(view->window, realcount); if (draw_nonclean) { if (realcount < 0) @@ -691,7 +692,7 @@ static int view_scroll(TEXT_BUFFER_VIEW_REC *view, LINE_REC **lines, view_draw_bottom(view, realcount); } - screen_refresh(view->window); + term_refresh(view->window); } } @@ -792,7 +793,7 @@ void textbuffer_view_scroll(TEXT_BUFFER_VIEW_REC *view, int lines) view->bottom = view_is_bottom(view); if (view->window != NULL) - screen_refresh(view->window); + term_refresh(view->window); } /* Scroll to specified line */ @@ -888,7 +889,7 @@ static void view_insert_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) } if (view->window != NULL) - screen_refresh(view->window); + term_refresh(view->window); } /* Update some line in the buffer which has been modified using @@ -1051,7 +1052,7 @@ static void view_remove_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, view->bottom = view_is_bottom(view); if (view->window != NULL) - screen_refresh(view->window); + term_refresh(view->window); } /* Remove one line from buffer. */ @@ -1147,7 +1148,7 @@ LINE_REC *textbuffer_view_get_bookmark(TEXT_BUFFER_VIEW_REC *view, /* Specify window where the changes in view should be drawn, NULL disables it. */ void textbuffer_view_set_window(TEXT_BUFFER_VIEW_REC *view, - SCREEN_WINDOW *window) + TERM_WINDOW *window) { g_return_if_fail(view != NULL); @@ -1164,9 +1165,9 @@ void textbuffer_view_redraw(TEXT_BUFFER_VIEW_REC *view) g_return_if_fail(view != NULL); if (view->window != NULL) { - screen_window_clear(view->window); + term_window_clear(view->window); view_draw_top(view, view->height); - screen_refresh(view->window); + term_refresh(view->window); } } diff --git a/src/fe-text/textbuffer-view.h b/src/fe-text/textbuffer-view.h index c5fc7ba2..e8b1262d 100644 --- a/src/fe-text/textbuffer-view.h +++ b/src/fe-text/textbuffer-view.h @@ -2,7 +2,7 @@ #define __TEXTBUFFER_VIEW_H #include "textbuffer.h" -#include "screen.h" +#include "term.h" typedef struct _TEXT_BUFFER_VIEW_REC TEXT_BUFFER_VIEW_REC; @@ -48,7 +48,7 @@ struct _TEXT_BUFFER_VIEW_REC { TEXT_BUFFER_REC *buffer; GSList *siblings; /* other views that use the same buffer */ - SCREEN_WINDOW *window; + TERM_WINDOW *window; int width, height; int default_indent; @@ -134,7 +134,7 @@ LINE_REC *textbuffer_view_get_bookmark(TEXT_BUFFER_VIEW_REC *view, /* Specify window where the changes in view should be drawn, NULL disables it. */ void textbuffer_view_set_window(TEXT_BUFFER_VIEW_REC *view, - SCREEN_WINDOW *window); + TERM_WINDOW *window); /* Redraw the view */ void textbuffer_view_redraw(TEXT_BUFFER_VIEW_REC *view); diff --git a/src/fe-text/textbuffer.c b/src/fe-text/textbuffer.c index f99aacca..b08e6fec 100644 --- a/src/fe-text/textbuffer.c +++ b/src/fe-text/textbuffer.c @@ -340,16 +340,55 @@ void textbuffer_remove_all_lines(TEXT_BUFFER_REC *buffer) buffer->last_eol = TRUE; } +static void set_color(GString *str, int cmd, int *last_fg, int *last_bg) +{ + if (cmd & LINE_COLOR_DEFAULT) { + g_string_sprintfa(str, "\004%c", FORMAT_STYLE_DEFAULTS); + + /* need to reset the fg/bg color */ + if (cmd & LINE_COLOR_BG) { + *last_bg = -1; + if (*last_fg != -1) { + g_string_sprintfa(str, "\004%c%c", + *last_fg, + FORMAT_COLOR_NOCHANGE); + } + } else { + *last_fg = -1; + if (*last_bg != -1) { + g_string_sprintfa(str, "\004%c%c", + FORMAT_COLOR_NOCHANGE, + *last_bg); + } + } + return; + } + + if ((cmd & LINE_COLOR_BG) == 0) { + /* change foreground color */ + *last_fg = (cmd & 0x0f)+'0'; + g_string_sprintfa(str, "\004%c%c", *last_fg, + FORMAT_COLOR_NOCHANGE); + } else { + /* change background color */ + *last_bg = (cmd & 0x0f)+'0'; + g_string_sprintfa(str, "\004%c%c", + FORMAT_COLOR_NOCHANGE, *last_bg); + } +} + void textbuffer_line2text(LINE_REC *line, int coloring, GString *str) { unsigned char cmd; char *ptr, *tmp; + int last_fg, last_bg; g_return_if_fail(line != NULL); g_return_if_fail(str != NULL); g_string_truncate(str, 0); + last_fg = last_bg = -1; for (ptr = line->text;;) { if (*ptr != 0) { g_string_append_c(str, *ptr); @@ -380,9 +419,7 @@ void textbuffer_line2text(LINE_REC *line, int coloring, GString *str) if ((cmd & 0x80) == 0) { /* set color */ - g_string_sprintfa(str, "\004%c%c", - (cmd & 0x0f)+'0', - ((cmd & 0xf0) >> 4)+'0'); + set_color(str, cmd, &last_fg, &last_bg); } else switch (cmd) { case LINE_CMD_UNDERLINE: g_string_append_c(str, 31); @@ -394,13 +431,6 @@ void textbuffer_line2text(LINE_REC *line, int coloring, GString *str) g_string_sprintfa(str, "\004%c%c", '0', FORMAT_COLOR_NOCHANGE); break; - case LINE_CMD_COLOR8: - g_string_sprintfa(str, "\004%c%c", - '8', FORMAT_COLOR_NOCHANGE); - break; - case LINE_CMD_BLINK: - g_string_sprintfa(str, "\004%c", FORMAT_STYLE_BLINK); - break; case LINE_CMD_INDENT: break; case LINE_CMD_INDENT_FUNC: diff --git a/src/fe-text/textbuffer.h b/src/fe-text/textbuffer.h index 8256309f..87e950fa 100644 --- a/src/fe-text/textbuffer.h +++ b/src/fe-text/textbuffer.h @@ -3,16 +3,19 @@ #define LINE_TEXT_CHUNK_SIZE 16384 +#define LINE_COLOR_BG 0x20 +#define LINE_COLOR_DEFAULT 0x10 +#define LINE_COLOR_BOLD 0x08 +#define LINE_COLOR_BLINK 0x08 + enum { LINE_CMD_EOL=0x80, /* line ends here */ LINE_CMD_CONTINUE, /* line continues in next block */ LINE_CMD_COLOR0, /* change to black, would be same as \0\0 but it breaks things.. */ - LINE_CMD_COLOR8, /* change to dark grey, normally 8 = bold black */ LINE_CMD_UNDERLINE, /* enable/disable underlining */ LINE_CMD_REVERSE, /* enable/disable reversed text */ LINE_CMD_INDENT, /* if line is split, indent it at this position */ LINE_CMD_INDENT_FUNC, /* if line is split, use the specified indentation function */ - LINE_CMD_BLINK, /* blinking background */ LINE_CMD_FORMAT, /* end of line, but next will come the format that was used to create the text in format - fields are separated with \0 and last argument ends with \0. \0 is allowed @@ -26,9 +29,17 @@ typedef struct { } LINE_INFO_REC; typedef struct _LINE_REC { - /* text in the line. \0 means that the next char will be a - color or command. <= 127 = color or if 8. bit is set, the - first 7 bits are the command. See LINE_CMD_xxxx. + /* Text in the line. \0 means that the next char will be a + color or command. + + If the 8th bit is set, the first 7 bits are the command + (see LINE_CMD_xxxx). Otherwise they specify a color change: + + Bit: + 5 - Setting a background color + 4 - Use "default terminal color" + 3 - Bold (fg) / blink (bg) - can be used with 4th bit + 0-2 - Color DO NOT ADD BLACK WITH \0\0 - this will break things. Use LINE_CMD_COLOR0 instead. */ diff --git a/src/fe-text/tparm.c b/src/fe-text/tparm.c new file mode 100644 index 00000000..5ec5c936 --- /dev/null +++ b/src/fe-text/tparm.c @@ -0,0 +1,740 @@ +/* + * tparm.c + * + * By Ross Ridge + * Public Domain + * 92/02/01 07:30:36 + * + */ +#include +#include +#include +#include + +#ifndef MAX_PUSHED +#define MAX_PUSHED 32 +#endif + +#define ARG 1 +#define NUM 2 + +#define INTEGER 1 +#define STRING 2 + +#define MAX_LINE 640 + +typedef void* anyptr; + +typedef struct stack_str { + int type; + int argnum; + int value; +} stack; + +static stack S[MAX_PUSHED]; +static stack vars['z'-'a'+1]; +static int pos = 0; + +static struct arg_str { + int type; + int integer; + char *string; +} arg_list[10]; + +static int argcnt; + +static va_list tparm_args; + +static int pusharg(int arg) +{ + if (pos == MAX_PUSHED) + return 1; + S[pos].type = ARG; + S[pos++].argnum = arg; + return 0; +} + +static int pushnum(int num) +{ + if (pos == MAX_PUSHED) + return 1; + S[pos].type = NUM; + S[pos++].value = num; + return 0; +} + +/* VARARGS2 */ +static int getarg(int argnum, int type, anyptr p) +{ + while (argcnt < argnum) { + arg_list[argcnt].type = INTEGER; + arg_list[argcnt++].integer = (int) va_arg(tparm_args, int); + } + if (argcnt > argnum) { + if (arg_list[argnum].type != type) + return 1; + else if (type == STRING) + *(char **)p = arg_list[argnum].string; + else + *(int *)p = arg_list[argnum].integer; + } else { + arg_list[argcnt].type = type; + if (type == STRING) + *(char **)p = arg_list[argcnt++].string + = (char *) va_arg(tparm_args, char *); + else + *(int *)p = arg_list[argcnt++].integer = (int) va_arg(tparm_args, int); + } + return 0; +} + + +static int popstring(char **str) +{ + if (pos-- == 0) + return 1; + if (S[pos].type != ARG) + return 1; + return(getarg(S[pos].argnum, STRING, (anyptr) str)); +} + +static int popnum(int *num) +{ + if (pos-- == 0) + return 1; + switch (S[pos].type) { + case ARG: + return (getarg(S[pos].argnum, INTEGER, (anyptr) num)); + case NUM: + *num = S[pos].value; + return 0; + } + return 1; +} + +static int cvtchar(char *sp, char *c) +{ + switch(*sp) { + case '\\': + switch(*++sp) { + case '\'': + case '$': + case '\\': + case '%': + *c = *sp; + return 2; + case '\0': + *c = '\\'; + return 1; + case '0': + if (sp[1] == '0' && sp[2] == '0') { + *c = '\0'; + return 4; + } + *c = '\200'; /* '\0' ???? */ + return 2; + default: + *c = *sp; + return 2; + } + default: + *c = *sp; + return 1; + } +} + +static int termcap; + +/* sigh... this has got to be the ugliest code I've ever written. + Trying to handle everything has its cost, I guess. + + It actually isn't to hard to figure out if a given % code is supposed + to be interpeted with its termcap or terminfo meaning since almost + all terminfo codes are invalid unless something has been pushed on + the stack and termcap strings will never push things on the stack + (%p isn't used by termcap). So where we have a choice we make the + decision by wether or not somthing has been pushed on the stack. + The static variable termcap keeps track of this; it starts out set + to 1 and is incremented as each argument processed by a termcap % code, + however if something is pushed on the stack it's set to 0 and the + rest of the % codes are interpeted as terminfo % codes. Another way + of putting it is that if termcap equals one we haven't decided either + way yet, if it equals zero we're looking for terminfo codes, and if + its greater than 1 we're looking for termcap codes. + + Terminfo % codes: + + %% output a '%' + %[[:][-+# ][width][.precision]][doxXs] + output pop according to the printf format + %c output pop as a char + %'c' push character constant c. + %{n} push decimal constant n. + %p[1-9] push paramter [1-9] + %g[a-z] push variable [a-z] + %P[a-z] put pop in variable [a-z] + %l push the length of pop (a string) + %+ add pop to pop and push the result + %- subtract pop from pop and push the result + %* multiply pop and pop and push the result + %& bitwise and pop and pop and push the result + %| bitwise or pop and pop and push the result + %^ bitwise xor pop and pop and push the result + %~ push the bitwise not of pop + %= compare if pop and pop are equal and push the result + %> compare if pop is less than pop and push the result + %< compare if pop is greater than pop and push the result + %A logical and pop and pop and push the result + %O logical or pop and pop and push the result + %! push the logical not of pop + %? condition %t if_true [%e if_false] %; + if condtion evaulates as true then evaluate if_true, + else evaluate if_false. elseif's can be done: +%? cond %t true [%e cond2 %t true2] ... [%e condN %t trueN] [%e false] %; + %i add one to parameters 1 and 2. (ANSI) + + Termcap Codes: + + %% output a % + %. output parameter as a character + %d output parameter as a decimal number + %2 output parameter in printf format %02d + %3 output parameter in printf format %03d + %+x add the character x to parameter and output it as a character +(UW) %-x subtract parameter FROM the character x and output it as a char +(UW) %ax add the character x to parameter +(GNU) %a[+*-/=][cp]x + GNU arithmetic. +(UW) %sx subtract parameter FROM the character x + %>xy if parameter > character x then add character y to parameter + %B convert to BCD (parameter = (parameter/10)*16 + parameter%16) + %D Delta Data encode (parameter = parameter - 2*(paramter%16)) + %i increment the first two parameters by one + %n xor the first two parameters by 0140 +(GNU) %m xor the first two parameters by 0177 + %r swap the first two parameters +(GNU) %b backup to previous parameter +(GNU) %f skip this parameter + + Note the two definitions of %a, the GNU defintion is used if the characters + after the 'a' are valid, otherwise the UW definition is used. + + (GNU) used by GNU Emacs termcap libraries + (UW) used by the University of Waterloo (MFCF) termcap libraries + +*/ + +char *tparm(const char *str, ...) { + static char OOPS[] = "OOPS"; + static char buf[MAX_LINE]; + register const char *sp; + register char *dp; + register char *fmt; + char conv_char; + char scan_for; + int scan_depth = 0, if_depth; + static int i, j; + static char *s, c; + char fmt_buf[MAX_LINE]; + char sbuf[MAX_LINE]; + + va_start(tparm_args, str); + + sp = str; + dp = buf; + scan_for = 0; + if_depth = 0; + argcnt = 0; + pos = 0; + termcap = 1; + while (*sp != '\0') { + switch(*sp) { + case '\\': + if (scan_for) { + if (*++sp != '\0') + sp++; + break; + } + *dp++ = *sp++; + if (*sp != '\0') + *dp++ = *sp++; + break; + case '%': + sp++; + if (scan_for) { + if (*sp == scan_for && if_depth == scan_depth) { + if (scan_for == ';') + if_depth--; + scan_for = 0; + } else if (*sp == '?') + if_depth++; + else if (*sp == ';') { + if (if_depth == 0) + return OOPS; + else + if_depth--; + } + sp++; + break; + } + fmt = NULL; + switch(*sp) { + case '%': + *dp++ = *sp++; + break; + case '+': + if (!termcap) { + if (popnum(&j) || popnum(&i)) + return OOPS; + i += j; + if (pushnum(i)) + return OOPS; + sp++; + break; + } + ;/* FALLTHROUGH */ + case 'C': + if (*sp == 'C') { + if (getarg(termcap - 1, INTEGER, &i)) + return OOPS; + if (i >= 96) { + i /= 96; + if (i == '$') + *dp++ = '\\'; + *dp++ = i; + } + } + fmt = "%c"; + /* FALLTHROUGH */ + case 'a': + if (!termcap) + return OOPS; + if (getarg(termcap - 1, INTEGER, (anyptr) &i)) + return OOPS; + if (*++sp == '\0') + return OOPS; + if ((sp[1] == 'p' || sp[1] == 'c') + && sp[2] != '\0' && fmt == NULL) { + /* GNU aritmitic parameter, what they + realy need is terminfo. */ + int val, lc; + if (sp[1] == 'p' + && getarg(termcap - 1 + sp[2] - '@', + INTEGER, (anyptr) &val)) + return OOPS; + if (sp[1] == 'c') { + lc = cvtchar(sp + 2, &c) + 2; + /* Mask out 8th bit so \200 can be + used for \0 as per GNU doc's */ + val = c & 0177; + } else + lc = 2; + switch(sp[0]) { + case '=': + break; + case '+': + val = i + val; + break; + case '-': + val = i - val; + break; + case '*': + val = i * val; + break; + case '/': + val = i / val; + break; + default: + /* Not really GNU's %a after all... */ + lc = cvtchar(sp, &c); + val = c + i; + break; + } + arg_list[termcap - 1].integer = val; + sp += lc; + break; + } + sp += cvtchar(sp, &c); + arg_list[termcap - 1].integer = c + i; + if (fmt == NULL) + break; + sp--; + /* FALLTHROUGH */ + case '-': + if (!termcap) { + if (popnum(&j) || popnum(&i)) + return OOPS; + i -= j; + if (pushnum(i)) + return OOPS; + sp++; + break; + } + fmt = "%c"; + /* FALLTHROUGH */ + case 's': + if (termcap && (fmt == NULL || *sp == '-')) { + if (getarg(termcap - 1, INTEGER, &i)) + return OOPS; + if (*++sp == '\0') + return OOPS; + sp += cvtchar(sp, &c); + arg_list[termcap - 1].integer = c - i; + if (fmt == NULL) + break; + sp--; + } + if (!termcap) + return OOPS; + ;/* FALLTHROUGH */ + case '.': + if (termcap && fmt == NULL) + fmt = "%c"; + ;/* FALLTHROUGH */ + case 'd': + if (termcap && fmt == NULL) + fmt = "%d"; + ;/* FALLTHROUGH */ + case '2': + if (termcap && fmt == NULL) + fmt = "%02d"; + ;/* FALLTHROUGH */ + case '3': + if (termcap && fmt == NULL) + fmt = "%03d"; + ;/* FALLTHROUGH */ + case ':': case ' ': case '#': case 'u': + case 'x': case 'X': case 'o': case 'c': + case '0': case '1': case '4': case '5': + case '6': case '7': case '8': case '9': + if (fmt == NULL) { + if (termcap) + return OOPS; + if (*sp == ':') + sp++; + fmt = fmt_buf; + *fmt++ = '%'; + while(*sp != 's' && *sp != 'x' && *sp != 'X' && *sp != 'd' && *sp != 'o' && *sp != 'c' && *sp != 'u') { + if (*sp == '\0') + return OOPS; + *fmt++ = *sp++; + } + *fmt++ = *sp; + *fmt = '\0'; + fmt = fmt_buf; + } + conv_char = fmt[strlen(fmt) - 1]; + if (conv_char == 's') { + if (popstring(&s)) + return OOPS; + sprintf(sbuf, fmt, s); + } else { + if (termcap) { + if (getarg(termcap++ - 1, + INTEGER, &i)) + return OOPS; + } else + if (popnum(&i)) + return OOPS; + if (i == 0 && conv_char == 'c') + *sbuf = 0; + else + sprintf(sbuf, fmt, i); + } + sp++; + fmt = sbuf; + while(*fmt != '\0') { + if (*fmt == '$') + *dp++ = '\\'; + *dp++ = *fmt++; + } + break; + case 'r': + if (!termcap || getarg(1, INTEGER, &i)) + return OOPS; + arg_list[1].integer = arg_list[0].integer; + arg_list[0].integer = i; + sp++; + break; + case 'i': + if (getarg(1, INTEGER, &i) + || arg_list[0].type != INTEGER) + return OOPS; + arg_list[1].integer++; + arg_list[0].integer++; + sp++; + break; + case 'n': + if (!termcap || getarg(1, INTEGER, &i)) + return OOPS; + arg_list[0].integer ^= 0140; + arg_list[1].integer ^= 0140; + sp++; + break; + case '>': + if (!termcap) { + if (popnum(&j) || popnum(&i)) + return OOPS; + i = (i > j); + if (pushnum(i)) + return OOPS; + sp++; + break; + } + if (getarg(termcap-1, INTEGER, &i)) + return OOPS; + sp += cvtchar(sp, &c); + if (i > c) { + sp += cvtchar(sp, &c); + arg_list[termcap-1].integer += c; + } else + sp += cvtchar(sp, &c); + sp++; + break; + case 'B': + if (!termcap || getarg(termcap-1, INTEGER, &i)) + return OOPS; + arg_list[termcap-1].integer = 16*(i/10)+i%10; + sp++; + break; + case 'D': + if (!termcap || getarg(termcap-1, INTEGER, &i)) + return OOPS; + arg_list[termcap-1].integer = i - 2 * (i % 16); + sp++; + break; + case 'p': + if (termcap > 1) + return OOPS; + if (*++sp == '\0') + return OOPS; + if (*sp == '0') + i = 9; + else + i = *sp - '1'; + if (i < 0 || i > 9) + return OOPS; + if (pusharg(i)) + return OOPS; + termcap = 0; + sp++; + break; + case 'P': + if (termcap || *++sp == '\0') + return OOPS; + i = *sp++ - 'a'; + if (i < 0 || i > 25) + return OOPS; + if (pos-- == 0) + return OOPS; + switch(vars[i].type = S[pos].type) { + case ARG: + vars[i].argnum = S[pos].argnum; + break; + case NUM: + vars[i].value = S[pos].value; + break; + } + break; + case 'g': + if (termcap || *++sp == '\0') + return OOPS; + i = *sp++ - 'a'; + if (i < 0 || i > 25) + return OOPS; + switch(vars[i].type) { + case ARG: + if (pusharg(vars[i].argnum)) + return OOPS; + break; + case NUM: + if (pushnum(vars[i].value)) + return OOPS; + break; + } + break; + case '\'': + if (termcap > 1) + return OOPS; + if (*++sp == '\0') + return OOPS; + sp += cvtchar(sp, &c); + if (pushnum(c) || *sp++ != '\'') + return OOPS; + termcap = 0; + break; + case '{': + if (termcap > 1) + return OOPS; + i = 0; + sp++; + while(isdigit(*sp)) + i = 10 * i + *sp++ - '0'; + if (*sp++ != '}' || pushnum(i)) + return OOPS; + termcap = 0; + break; + case 'l': + if (termcap || popstring(&s)) + return OOPS; + i = strlen(s); + if (pushnum(i)) + return OOPS; + sp++; + break; + case '*': + if (termcap || popnum(&j) || popnum(&i)) + return OOPS; + i *= j; + if (pushnum(i)) + return OOPS; + sp++; + break; + case '/': + if (termcap || popnum(&j) || popnum(&i)) + return OOPS; + i /= j; + if (pushnum(i)) + return OOPS; + sp++; + break; + case 'm': + if (termcap) { + if (getarg(1, INTEGER, &i)) + return OOPS; + arg_list[0].integer ^= 0177; + arg_list[1].integer ^= 0177; + sp++; + break; + } + if (popnum(&j) || popnum(&i)) + return OOPS; + i %= j; + if (pushnum(i)) + return OOPS; + sp++; + break; + case '&': + if (popnum(&j) || popnum(&i)) + return OOPS; + i &= j; + if (pushnum(i)) + return OOPS; + sp++; + break; + case '|': + if (popnum(&j) || popnum(&i)) + return OOPS; + i |= j; + if (pushnum(i)) + return OOPS; + sp++; + break; + case '^': + if (popnum(&j) || popnum(&i)) + return OOPS; + i ^= j; + if (pushnum(i)) + return OOPS; + sp++; + break; + case '=': + if (popnum(&j) || popnum(&i)) + return OOPS; + i = (i == j); + if (pushnum(i)) + return OOPS; + sp++; + break; + case '<': + if (popnum(&j) || popnum(&i)) + return OOPS; + i = (i < j); + if (pushnum(i)) + return OOPS; + sp++; + break; + case 'A': + if (popnum(&j) || popnum(&i)) + return OOPS; + i = (i && j); + if (pushnum(i)) + return OOPS; + sp++; + break; + case 'O': + if (popnum(&j) || popnum(&i)) + return OOPS; + i = (i || j); + if (pushnum(i)) + return OOPS; + sp++; + break; + case '!': + if (popnum(&i)) + return OOPS; + i = !i; + if (pushnum(i)) + return OOPS; + sp++; + break; + case '~': + if (popnum(&i)) + return OOPS; + i = ~i; + if (pushnum(i)) + return OOPS; + sp++; + break; + case '?': + if (termcap > 1) + return OOPS; + termcap = 0; + if_depth++; + sp++; + break; + case 't': + if (popnum(&i) || if_depth == 0) + return OOPS; + if (!i) { + scan_for = 'e'; + scan_depth = if_depth; + } + sp++; + break; + case 'e': + if (if_depth == 0) + return OOPS; + scan_for = ';'; + scan_depth = if_depth; + sp++; + break; + case ';': + if (if_depth-- == 0) + return OOPS; + sp++; + break; + case 'b': + if (--termcap < 1) + return OOPS; + sp++; + break; + case 'f': + if (!termcap++) + return OOPS; + sp++; + break; + } + break; + default: + if (scan_for) + sp++; + else + *dp++ = *sp++; + break; + } + } + va_end(tparm_args); + *dp = '\0'; + return buf; +}