aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/interface-curses
diff options
context:
space:
mode:
Diffstat (limited to 'src/interface-curses')
-rw-r--r--src/interface-curses/Makefile.am3
-rw-r--r--src/interface-curses/curses-icons.c398
-rw-r--r--src/interface-curses/curses-icons.h28
-rw-r--r--src/interface-curses/curses-info-popup.c23
-rw-r--r--src/interface-curses/curses-info-popup.h2
-rw-r--r--src/interface-curses/curses-utils.c72
-rw-r--r--src/interface-curses/curses-utils.h17
-rw-r--r--src/interface-curses/interface.c246
8 files changed, 674 insertions, 115 deletions
diff --git a/src/interface-curses/Makefile.am b/src/interface-curses/Makefile.am
index 14fc920..44fb658 100644
--- a/src/interface-curses/Makefile.am
+++ b/src/interface-curses/Makefile.am
@@ -6,4 +6,5 @@ AM_CFLAGS = -std=gnu11 -Wall -Wno-initializer-overrides -Wno-unused-value
noinst_LTLIBRARIES = libsciteco-interface.la
libsciteco_interface_la_SOURCES = interface.c \
curses-utils.c curses-utils.h \
- curses-info-popup.c curses-info-popup.h
+ curses-info-popup.c curses-info-popup.h \
+ curses-icons.c curses-icons.h
diff --git a/src/interface-curses/curses-icons.c b/src/interface-curses/curses-icons.c
new file mode 100644
index 0000000..1a1ba3a
--- /dev/null
+++ b/src/interface-curses/curses-icons.c
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2012-2024 Robin Haberkorn
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include <curses.h>
+
+#include "sciteco.h"
+#include "curses-icons.h"
+
+typedef struct {
+ const gchar *name;
+ gunichar c;
+} teco_curses_icon_t;
+
+/*
+ * The following icons have initially been adapted from exa,
+ * but icons have since been added and removed.
+ *
+ * They require fonts with additional symbols, eg.
+ * Nerd Fonts (https://www.nerdfonts.com/).
+ *
+ * They MUST be kept presorted, so we can perform binary searches.
+ */
+
+/** Mapping of complete filenames to Unicode "icons" */
+static const teco_curses_icon_t teco_icons_file[] = {
+ {".Trash", 0xf1f8}, /*  */
+ {".atom", 0xe764}, /*  */
+ {".bash_history", 0xf489}, /*  */
+ {".bash_profile", 0xf489}, /*  */
+ {".bashrc", 0xf489}, /*  */
+ {".git", 0xf1d3}, /*  */
+ {".gitattributes", 0xf1d3}, /*  */
+ {".gitconfig", 0xf1d3}, /*  */
+ {".github", 0xf408}, /*  */
+ {".gitignore", 0xf1d3}, /*  */
+ {".gitmodules", 0xf1d3}, /*  */
+ {".rvm", 0xe21e}, /*  */
+ {".teco_ini", 0xedaa}, /*  */
+ {".teco_session", 0xedaa}, /*  */
+ {".vimrc", 0xe62b}, /*  */
+ {".vscode", 0xe70c}, /*  */
+ {".zshrc", 0xf489}, /*  */
+ {"COMMIT_EDITMSG", 0xf1d3}, /*  */
+ {"Cargo.lock", 0xe7a8}, /*  */
+ {"Dockerfile", 0xf308}, /*  */
+ {"GNUmakefile", 0xf489}, /*  */
+ {"MERGE_MSG", 0xf1d3}, /*  */
+ {"Makefile", 0xf489}, /*  */
+ {"PKGBUILD", 0xf303}, /*  */
+ {"TAG_EDITMSG", 0xf1d3}, /*  */
+ {"bin", 0xe5fc}, /*  */
+ {"config", 0xe5fc}, /*  */
+ {"docker-compose.yml", 0xf308}, /*  */
+ {"ds_store", 0xf179}, /*  */
+ {"git-rebase-todo", 0xf1d3}, /*  */
+ {"go.mod", 0xe626}, /*  */
+ {"go.sum", 0xe626}, /*  */
+ {"gradle", 0xe256}, /*  */
+ {"gruntfile.coffee", 0xe611}, /*  */
+ {"gruntfile.js", 0xe611}, /*  */
+ {"gruntfile.ls", 0xe611}, /*  */
+ {"gulpfile.coffee", 0xe610}, /*  */
+ {"gulpfile.js", 0xe610}, /*  */
+ {"gulpfile.ls", 0xe610}, /*  */
+ {"hidden", 0xf023}, /*  */
+ {"include", 0xe5fc}, /*  */
+ {"lib", 0xf121}, /*  */
+ {"localized", 0xf179}, /*  */
+ {"node_modules", 0xe718}, /*  */
+ {"npmignore", 0xe71e}, /*  */
+ {"rubydoc", 0xe73b}, /*  */
+ {"yarn.lock", 0xe718}, /*  */
+};
+
+/** Mapping of file extensions to Unicode "icons" */
+static const teco_curses_icon_t teco_icons_ext[] = {
+ {"DS_store", 0xf179}, /*  */
+ {"ai", 0xe7b4}, /*  */
+ {"android", 0xe70e}, /*  */
+ {"apk", 0xe70e}, /*  */
+ {"apple", 0xf179}, /*  */
+ {"avi", 0xf03d}, /*  */
+ {"avif", 0xf1c5}, /*  */
+ {"avro", 0xe60b}, /*  */
+ {"awk", 0xf489}, /*  */
+ {"bash", 0xf489}, /*  */
+ {"bat", 0xf17a}, /*  */
+ {"bats", 0xf489}, /*  */
+ {"bmp", 0xf1c5}, /*  */
+ {"bz", 0xf410}, /*  */
+ {"bz2", 0xf410}, /*  */
+ {"c", 0xe61e}, /*  */
+ {"c++", 0xe61d}, /*  */
+ {"cab", 0xe70f}, /*  */
+ {"cc", 0xe61d}, /*  */
+ {"cfg", 0xe615}, /*  */
+ {"class", 0xe256}, /*  */
+ {"clj", 0xe768}, /*  */
+ {"cljs", 0xe76a}, /*  */
+ {"cls", 0xf034}, /*  */
+ {"cmd", 0xe70f}, /*  */
+ {"coffee", 0xf0f4}, /*  */
+ {"conf", 0xe615}, /*  */
+ {"cp", 0xe61d}, /*  */
+ {"cpio", 0xf410}, /*  */
+ {"cpp", 0xe61d}, /*  */
+ {"cs", 0xf031b}, /* 󰌛 */
+ {"csh", 0xf489}, /*  */
+ {"cshtml", 0xf1fa}, /*  */
+ {"csproj", 0xf031b}, /* 󰌛 */
+ {"css", 0xe749}, /*  */
+ {"csv", 0xf1c3}, /*  */
+ {"csx", 0xf031b}, /* 󰌛 */
+ {"cxx", 0xe61d}, /*  */
+ {"d", 0xe7af}, /*  */
+ {"dart", 0xe798}, /*  */
+ {"db", 0xf1c0}, /*  */
+ {"deb", 0xe77d}, /*  */
+ {"diff", 0xf440}, /*  */
+ {"djvu", 0xf02d}, /*  */
+ {"dll", 0xe70f}, /*  */
+ {"doc", 0xf1c2}, /*  */
+ {"docx", 0xf1c2}, /*  */
+ {"ds_store", 0xf179}, /*  */
+ {"dump", 0xf1c0}, /*  */
+ {"ebook", 0xe28b}, /*  */
+ {"ebuild", 0xf30d}, /*  */
+ {"editorconfig", 0xe615}, /*  */
+ {"ejs", 0xe618}, /*  */
+ {"elm", 0xe62c}, /*  */
+ {"env", 0xf462}, /*  */
+ {"eot", 0xf031}, /*  */
+ {"epub", 0xe28a}, /*  */
+ {"erb", 0xe73b}, /*  */
+ {"erl", 0xe7b1}, /*  */
+ {"ex", 0xe62d}, /*  */
+ {"exe", 0xf17a}, /*  */
+ {"exs", 0xe62d}, /*  */
+ {"fish", 0xf489}, /*  */
+ {"flac", 0xf001}, /*  */
+ {"flv", 0xf03d}, /*  */
+ {"font", 0xf031}, /*  */
+ {"fs", 0xe7a7}, /*  */
+ {"fsi", 0xe7a7}, /*  */
+ {"fsx", 0xe7a7}, /*  */
+ {"gdoc", 0xf1c2}, /*  */
+ {"gem", 0xe21e}, /*  */
+ {"gemfile", 0xe21e}, /*  */
+ {"gemspec", 0xe21e}, /*  */
+ {"gform", 0xf298}, /*  */
+ {"gif", 0xf1c5}, /*  */
+ {"go", 0xe626}, /*  */
+ {"gradle", 0xe256}, /*  */
+ {"groovy", 0xe775}, /*  */
+ {"gsheet", 0xf1c3}, /*  */
+ {"gslides", 0xf1c4}, /*  */
+ {"guardfile", 0xe21e}, /*  */
+ {"gz", 0xf410}, /*  */
+ {"h", 0xf0fd}, /*  */
+ {"hbs", 0xe60f}, /*  */
+ {"hpp", 0xf0fd}, /*  */
+ {"hs", 0xe777}, /*  */
+ {"htm", 0xf13b}, /*  */
+ {"html", 0xf13b}, /*  */
+ {"hxx", 0xf0fd}, /*  */
+ {"ico", 0xf1c5}, /*  */
+ {"image", 0xf1c5}, /*  */
+ {"img", 0xe271}, /*  */
+ {"iml", 0xe7b5}, /*  */
+ {"ini", 0xf17a}, /*  */
+ {"ipynb", 0xe678}, /*  */
+ {"iso", 0xe271}, /*  */
+ {"j2c", 0xf1c5}, /*  */
+ {"j2k", 0xf1c5}, /*  */
+ {"jad", 0xe256}, /*  */
+ {"jar", 0xe256}, /*  */
+ {"java", 0xe256}, /*  */
+ {"jfi", 0xf1c5}, /*  */
+ {"jfif", 0xf1c5}, /*  */
+ {"jif", 0xf1c5}, /*  */
+ {"jl", 0xe624}, /*  */
+ {"jmd", 0xf48a}, /*  */
+ {"jp2", 0xf1c5}, /*  */
+ {"jpe", 0xf1c5}, /*  */
+ {"jpeg", 0xf1c5}, /*  */
+ {"jpg", 0xf1c5}, /*  */
+ {"jpx", 0xf1c5}, /*  */
+ {"js", 0xe74e}, /*  */
+ {"json", 0xe60b}, /*  */
+ {"jsx", 0xe7ba}, /*  */
+ {"jxl", 0xf1c5}, /*  */
+ {"ksh", 0xf489}, /*  */
+ {"latex", 0xf034}, /*  */
+ {"less", 0xe758}, /*  */
+ {"lhs", 0xe777}, /*  */
+ {"license", 0xf0219}, /* 󰈙 */
+ {"localized", 0xf179}, /*  */
+ {"lock", 0xf023}, /*  */
+ {"log", 0xf18d}, /*  */
+ {"lua", 0xe620}, /*  */
+ {"lz", 0xf410}, /*  */
+ {"lz4", 0xf410}, /*  */
+ {"lzh", 0xf410}, /*  */
+ {"lzma", 0xf410}, /*  */
+ {"lzo", 0xf410}, /*  */
+ {"m", 0xe61e}, /*  */
+ {"m4a", 0xf001}, /*  */
+ {"markdown", 0xf48a}, /*  */
+ {"md", 0xf48a}, /*  */
+ {"mjs", 0xe74e}, /*  */
+ {"mk", 0xf489}, /*  */
+ {"mkd", 0xf48a}, /*  */
+ {"mkv", 0xf03d}, /*  */
+ {"mm", 0xe61d}, /*  */
+ {"mobi", 0xe28b}, /*  */
+ {"mov", 0xf03d}, /*  */
+ {"mp3", 0xf001}, /*  */
+ {"mp4", 0xf03d}, /*  */
+ {"msi", 0xe70f}, /*  */
+ {"mustache", 0xe60f}, /*  */
+ {"nix", 0xf313}, /*  */
+ {"node", 0xf0399}, /* 󰎙 */
+ {"npmignore", 0xe71e}, /*  */
+ {"odp", 0xf1c4}, /*  */
+ {"ods", 0xf1c3}, /*  */
+ {"odt", 0xf1c2}, /*  */
+ {"ogg", 0xf001}, /*  */
+ {"ogv", 0xf03d}, /*  */
+ {"otf", 0xf031}, /*  */
+ {"part", 0xf43a}, /*  */
+ {"patch", 0xf440}, /*  */
+ {"pdf", 0xf1c1}, /*  */
+ {"php", 0xe73d}, /*  */
+ {"pl", 0xe769}, /*  */
+ {"plx", 0xe769}, /*  */
+ {"pm", 0xe769}, /*  */
+ {"png", 0xf1c5}, /*  */
+ {"pod", 0xe769}, /*  */
+ {"ppt", 0xf1c4}, /*  */
+ {"pptx", 0xf1c4}, /*  */
+ {"procfile", 0xe21e}, /*  */
+ {"properties", 0xe60b}, /*  */
+ {"ps1", 0xf489}, /*  */
+ {"psd", 0xe7b8}, /*  */
+ {"pxm", 0xf1c5}, /*  */
+ {"py", 0xe606}, /*  */
+ {"pyc", 0xe606}, /*  */
+ {"r", 0xf25d}, /*  */
+ {"rakefile", 0xe21e}, /*  */
+ {"rar", 0xf410}, /*  */
+ {"razor", 0xf1fa}, /*  */
+ {"rb", 0xe21e}, /*  */
+ {"rdata", 0xf25d}, /*  */
+ {"rdb", 0xe76d}, /*  */
+ {"rdoc", 0xf48a}, /*  */
+ {"rds", 0xf25d}, /*  */
+ {"readme", 0xf48a}, /*  */
+ {"rlib", 0xe7a8}, /*  */
+ {"rmd", 0xf48a}, /*  */
+ {"rpm", 0xe7bb}, /*  */
+ {"rs", 0xe7a8}, /*  */
+ {"rspec", 0xe21e}, /*  */
+ {"rspec_parallel", 0xe21e}, /*  */
+ {"rspec_status", 0xe21e}, /*  */
+ {"rss", 0xf09e}, /*  */
+ {"rtf", 0xf0219}, /* 󰈙 */
+ {"ru", 0xe21e}, /*  */
+ {"rubydoc", 0xe73b}, /*  */
+ {"sass", 0xe603}, /*  */
+ {"scala", 0xe737}, /*  */
+ {"scss", 0xe749}, /*  */
+ {"sh", 0xf489}, /*  */
+ {"shell", 0xf489}, /*  */
+ {"slim", 0xe73b}, /*  */
+ {"sln", 0xe70c}, /*  */
+ {"so", 0xf17c}, /*  */
+ {"sql", 0xf1c0}, /*  */
+ {"sqlite3", 0xe7c4}, /*  */
+ {"sty", 0xf034}, /*  */
+ {"styl", 0xe600}, /*  */
+ {"stylus", 0xe600}, /*  */
+ {"svg", 0xf1c5}, /*  */
+ {"swift", 0xe755}, /*  */
+ {"t", 0xe769}, /*  */
+ {"tar", 0xf410}, /*  */
+ {"taz", 0xf410}, /*  */
+ {"tbz", 0xf410}, /*  */
+ {"tbz2", 0xf410}, /*  */
+ {"tec", 0xedaa}, /*  */
+ {"tes", 0xedaa}, /*  */
+ {"tex", 0xf034}, /*  */
+ {"tgz", 0xf410}, /*  */
+ {"tiff", 0xf1c5}, /*  */
+ {"tlz", 0xf410}, /*  */
+ {"toml", 0xe615}, /*  */
+ {"torrent", 0xe275}, /*  */
+ {"ts", 0xe628}, /*  */
+ {"tsv", 0xf1c3}, /*  */
+ {"tsx", 0xe7ba}, /*  */
+ {"ttf", 0xf031}, /*  */
+ {"twig", 0xe61c}, /*  */
+ {"txt", 0xf15c}, /*  */
+ {"txz", 0xf410}, /*  */
+ {"tz", 0xf410}, /*  */
+ {"tzo", 0xf410}, /*  */
+ {"video", 0xf03d}, /*  */
+ {"vim", 0xe62b}, /*  */
+ {"vue", 0xf0844}, /* 󰡄 */
+ {"war", 0xe256}, /*  */
+ {"wav", 0xf001}, /*  */
+ {"webm", 0xf03d}, /*  */
+ {"webp", 0xf1c5}, /*  */
+ {"windows", 0xf17a}, /*  */
+ {"woff", 0xf031}, /*  */
+ {"woff2", 0xf031}, /*  */
+ {"woman", 0xeaa4}, /*  */
+ {"xhtml", 0xf13b}, /*  */
+ {"xls", 0xf1c3}, /*  */
+ {"xlsx", 0xf1c3}, /*  */
+ {"xml", 0xf05c0}, /* 󰗀 */
+ {"xul", 0xf05c0}, /* 󰗀 */
+ {"xz", 0xf410}, /*  */
+ {"yaml", 0xf481}, /*  */
+ {"yml", 0xf481}, /*  */
+ {"zip", 0xf410}, /*  */
+ {"zsh", 0xf489}, /*  */
+ {"zsh-theme", 0xf489}, /*  */
+ {"zst", 0xf410}, /*  */
+};
+
+static int
+teco_curses_icon_cmp(const void *a, const void *b)
+{
+ const gchar *str = a;
+ const teco_curses_icon_t *icon = b;
+
+ return strcmp(str, icon->name);
+}
+
+gunichar
+teco_curses_icons_lookup_file(const gchar *filename)
+{
+ g_autofree gchar *basename = g_path_get_basename(filename);
+ const teco_curses_icon_t *icon;
+
+ /* try to find icon by complete file name */
+ icon = bsearch(basename, teco_icons_file, G_N_ELEMENTS(teco_icons_file),
+ sizeof(teco_icons_file[0]), teco_curses_icon_cmp);
+ if (icon)
+ return icon->c;
+
+ /* try to find icon by extension */
+ const gchar *ext = strrchr(basename, '.');
+ if (ext) {
+ icon = bsearch(ext+1, teco_icons_ext, G_N_ELEMENTS(teco_icons_ext),
+ sizeof(teco_icons_ext[0]), teco_curses_icon_cmp);
+ return icon ? icon->c : 0xf15b; /*  */
+ }
+
+ /* default file icon for files without extension */
+ return 0xf016; /*  */
+}
+
+gunichar
+teco_curses_icons_lookup_dir(const gchar *dirname)
+{
+ g_autofree gchar *basename = g_path_get_basename(dirname);
+ const teco_curses_icon_t *icon;
+
+ icon = bsearch(basename, teco_icons_file, G_N_ELEMENTS(teco_icons_file),
+ sizeof(teco_icons_file[0]), teco_curses_icon_cmp);
+
+ /* default folder icon */
+ return icon ? icon->c : 0xf115; /*  */
+}
diff --git a/src/interface-curses/curses-icons.h b/src/interface-curses/curses-icons.h
new file mode 100644
index 0000000..c1be06f
--- /dev/null
+++ b/src/interface-curses/curses-icons.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2012-2024 Robin Haberkorn
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+
+#include <glib.h>
+
+/**
+ * Q-Register icon.
+ * 0xf04cf would look more similar to the current Gtk icon.
+ */
+#define TECO_CURSES_ICONS_QREG 0xe236 /*  */
+
+gunichar teco_curses_icons_lookup_file(const gchar *filename);
+gunichar teco_curses_icons_lookup_dir(const gchar *dirname);
diff --git a/src/interface-curses/curses-info-popup.c b/src/interface-curses/curses-info-popup.c
index a738f5d..e6e1549 100644
--- a/src/interface-curses/curses-info-popup.c
+++ b/src/interface-curses/curses-info-popup.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2023 Robin Haberkorn
+ * Copyright (C) 2012-2024 Robin Haberkorn
*
* 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
@@ -28,6 +28,7 @@
#include "interface.h"
#include "curses-utils.h"
#include "curses-info-popup.h"
+#include "curses-icons.h"
/*
* FIXME: This is redundant with gtk-info-popup.c.
@@ -75,8 +76,13 @@ teco_curses_info_popup_init_pad(teco_curses_info_popup_t *ctx, attr_t attr)
gint pad_cols; /**! entry columns */
gint pad_colwidth; /**! width per entry column */
- /* reserve 2 spaces between columns */
- pad_colwidth = MIN(ctx->longest + 2, cols - 2);
+ /*
+ * With Unicode icons enabled, we reserve 2 characters at the beginning and one
+ * after the filename/directory.
+ * Otherwise 2 characters after the entry.
+ */
+ gint reserve = teco_ed & TECO_ED_ICONS ? 2+1 : 2;
+ pad_colwidth = MIN(ctx->longest + reserve, cols - 2);
/* pad_cols = floor((cols - 2) / pad_colwidth) */
pad_cols = (cols - 2) / pad_colwidth;
@@ -111,8 +117,19 @@ teco_curses_info_popup_init_pad(teco_curses_info_popup_t *ctx, attr_t attr)
switch (entry->type) {
case TECO_POPUP_FILE:
+ g_assert(!teco_string_contains(&entry->name, '\0'));
+ if (teco_ed & TECO_ED_ICONS) {
+ teco_curses_add_wc(ctx->pad, teco_curses_icons_lookup_file(entry->name.data));
+ waddch(ctx->pad, ' ');
+ }
+ teco_curses_format_filename(ctx->pad, entry->name.data, -1);
+ break;
case TECO_POPUP_DIRECTORY:
g_assert(!teco_string_contains(&entry->name, '\0'));
+ if (teco_ed & TECO_ED_ICONS) {
+ teco_curses_add_wc(ctx->pad, teco_curses_icons_lookup_dir(entry->name.data));
+ waddch(ctx->pad, ' ');
+ }
teco_curses_format_filename(ctx->pad, entry->name.data, -1);
break;
default:
diff --git a/src/interface-curses/curses-info-popup.h b/src/interface-curses/curses-info-popup.h
index bcdb3b8..a6c28a5 100644
--- a/src/interface-curses/curses-info-popup.h
+++ b/src/interface-curses/curses-info-popup.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2023 Robin Haberkorn
+ * Copyright (C) 2012-2024 Robin Haberkorn
*
* 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
diff --git a/src/interface-curses/curses-utils.c b/src/interface-curses/curses-utils.c
index 8dc62f1..c751afd 100644
--- a/src/interface-curses/curses-utils.c
+++ b/src/interface-curses/curses-utils.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2023 Robin Haberkorn
+ * Copyright (C) 2012-2024 Robin Haberkorn
*
* 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
@@ -29,7 +29,21 @@
#include "string-utils.h"
#include "curses-utils.h"
-gsize
+/**
+ * Render UTF-8 string with TECO character representations.
+ *
+ * Strings are cut off with `...` at the end if necessary.
+ * The mapping is similar to teco_view_set_representations().
+ *
+ * @param win The Curses window to write to.
+ * @param str The string to format.
+ * @param len The length of the string in bytes.
+ * @param max_width The maximum width to consume in
+ * the window in characters. If smaller 0, take the
+ * entire remaining space in the window.
+ * @return Number of characters actually written.
+ */
+guint
teco_curses_format_str(WINDOW *win, const gchar *str, gsize len, gint max_width)
{
int old_x, old_y;
@@ -42,6 +56,12 @@ teco_curses_format_str(WINDOW *win, const gchar *str, gsize len, gint max_width)
while (len > 0) {
/*
+ * NOTE: It shouldn't be possible to meet any string,
+ * that is not valid UTF-8.
+ */
+ gsize clen = g_utf8_next_char(str) - str;
+
+ /*
* NOTE: This mapping is similar to
* teco_view_set_representations().
*/
@@ -85,12 +105,18 @@ teco_curses_format_str(WINDOW *win, const gchar *str, gsize len, gint max_width)
chars_added++;
if (chars_added > max_width)
goto truncate;
- waddch(win, *str);
+ /*
+ * FIXME: This works with UTF-8 on ncurses,
+ * since it detects multi-byte characters.
+ * However on other platforms wadd_wch() may be
+ * necessary, which requires a widechar Curses variant.
+ */
+ waddnstr(win, str, clen);
}
}
- str++;
- len--;
+ str += clen;
+ len -= clen;
}
return getcurx(win) - old_x;
@@ -108,23 +134,43 @@ truncate:
return getcurx(win) - old_x;
}
-gsize
-teco_curses_format_filename(WINDOW *win, const gchar *filename,
- gint max_width)
+/**
+ * Render UTF-8 filename.
+ *
+ * This cuts of overlong filenames with `...` at the beginning,
+ * possibly skipping any drive letter.
+ * Control characters are escaped, but not highlighted.
+ *
+ * @param win The Curses window to write to.
+ * @param filename Null-terminated filename to render.
+ * @param max_width The maximum width to consume in
+ * the window in characters. If smaller 0, take the
+ * entire remaining space in the window.
+ * @return Number of characters actually written.
+ */
+guint
+teco_curses_format_filename(WINDOW *win, const gchar *filename, gint max_width)
{
int old_x = getcurx(win);
g_autofree gchar *filename_printable = teco_string_echo(filename, strlen(filename));
- size_t filename_len = strlen(filename_printable);
+ glong filename_len = g_utf8_strlen(filename_printable, -1);
if (max_width < 0)
max_width = getmaxx(win) - old_x;
- if (filename_len <= (size_t)max_width) {
+ if (filename_len <= max_width) {
+ /*
+ * FIXME: This works with UTF-8 on ncurses,
+ * since it detects multi-byte characters.
+ * However on other platforms wadd_wch() may be
+ * necessary, which requires a widechar Curses variant.
+ */
waddstr(win, filename_printable);
- } else {
- const gchar *keep_post = filename_printable + filename_len -
- max_width + 3;
+ } else if (filename_len >= 3) {
+ const gchar *keep_post;
+ keep_post = g_utf8_offset_to_pointer(filename_printable + strlen(filename_printable),
+ -max_width + 3);
#ifdef G_OS_WIN32
const gchar *keep_pre = g_path_skip_root(filename_printable);
diff --git a/src/interface-curses/curses-utils.h b/src/interface-curses/curses-utils.h
index a91ab44..2c819ee 100644
--- a/src/interface-curses/curses-utils.h
+++ b/src/interface-curses/curses-utils.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2023 Robin Haberkorn
+ * Copyright (C) 2012-2024 Robin Haberkorn
*
* 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
@@ -20,6 +20,17 @@
#include <curses.h>
-gsize teco_curses_format_str(WINDOW *win, const gchar *str, gsize len, gint max_width);
+guint teco_curses_format_str(WINDOW *win, const gchar *str, gsize len, gint max_width);
-gsize teco_curses_format_filename(WINDOW *win, const gchar *filename, gint max_width);
+guint teco_curses_format_filename(WINDOW *win, const gchar *filename, gint max_width);
+
+/**
+ * Add Unicode character to window.
+ * This is just like wadd_wch(), but does not require wide-char APIs.
+ */
+static inline void
+teco_curses_add_wc(WINDOW *win, gunichar chr)
+{
+ gchar buf[6];
+ waddnstr(win, buf, g_unichar_to_utf8(chr, buf));
+}
diff --git a/src/interface-curses/interface.c b/src/interface-curses/interface.c
index ef3f0c7..95e86c9 100644
--- a/src/interface-curses/interface.c
+++ b/src/interface-curses/interface.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2023 Robin Haberkorn
+ * Copyright (C) 2012-2024 Robin Haberkorn
*
* 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
@@ -24,7 +24,6 @@
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
-#include <locale.h>
#include <errno.h>
#ifdef HAVE_WINDOWS_H
@@ -65,11 +64,12 @@
#include "qreg.h"
#include "ring.h"
#include "error.h"
-#include "curses-utils.h"
-#include "curses-info-popup.h"
#include "view.h"
#include "memory.h"
#include "interface.h"
+#include "curses-utils.h"
+#include "curses-info-popup.h"
+#include "curses-icons.h"
#if defined(__PDCURSES__) && defined(G_OS_WIN32) && \
!defined(PDCURSES_GUI)
@@ -340,12 +340,18 @@ static struct {
TECO_INFO_TYPE_QREG
} info_type;
teco_string_t info_current;
+ gboolean info_dirty;
WINDOW *msg_window;
WINDOW *cmdline_window, *cmdline_pad;
- gsize cmdline_len, cmdline_rubout_len;
+ guint cmdline_len, cmdline_rubout_len;
+ /**
+ * Pad used exclusively for wgetch() as it will not
+ * result in unwanted wrefresh().
+ */
+ WINDOW *input_pad;
GQueue *input_queue;
teco_curses_info_popup_t popup;
@@ -554,7 +560,7 @@ teco_interface_init_screen(void)
g_assert(teco_interface.screen_tty != NULL);
teco_interface.screen = newterm(NULL, teco_interface.screen_tty, teco_interface.screen_tty);
- if (!teco_interface.screen) {
+ if (G_UNLIKELY(!teco_interface.screen)) {
g_fprintf(stderr, "Error initializing interactive mode. "
"$TERM may be incorrect.\n");
exit(EXIT_FAILURE);
@@ -629,28 +635,6 @@ teco_interface_init_interactive(GError **error)
return FALSE;
/*
- * On UNIX terminals, the escape key is usually
- * delivered as the escape character even though function
- * keys are delivered as escape sequences as well.
- * That's why there has to be a timeout for detecting
- * escape presses if function key handling is enabled.
- * This timeout can be controlled using $ESCDELAY on
- * ncurses but its default is much too long.
- * We set it to 25ms as Vim does. In the very rare cases
- * this won't suffice, $ESCDELAY can still be set explicitly.
- *
- * NOTE: The only terminal emulator I'm aware of that lets
- * us send an escape sequence for the escape key is Mintty
- * (see "\e[?7727h").
- *
- * FIXME: This appears to be ineffective for netbsd-curses.
- */
-#ifdef CURSES_TTY
- if (!g_getenv("ESCDELAY"))
- set_escdelay(25);
-#endif
-
- /*
* $TERM must be unset or "#win32con" for the win32
* driver to load.
* So we always ignore any $TERM changes by the user.
@@ -679,12 +663,31 @@ teco_interface_init_interactive(GError **error)
PDC_set_function_key(FUNCTION_KEY_SHUT_DOWN, KEY_CLOSE);
#endif
- /* for displaying UTF-8 characters properly */
- setlocale(LC_CTYPE, "");
-
teco_interface_init_screen();
/*
+ * On UNIX terminals, the escape key is usually
+ * delivered as the escape character even though function
+ * keys are delivered as escape sequences as well.
+ * That's why there has to be a timeout for detecting
+ * escape presses if function key handling is enabled.
+ * This timeout can be controlled using $ESCDELAY on
+ * ncurses but its default is much too long.
+ * We set it to 25ms as Vim does. In the very rare cases
+ * this won't suffice, $ESCDELAY can still be set explicitly.
+ *
+ * NOTE: The only terminal emulator I'm aware of that lets
+ * us send an escape sequence for the escape key is Mintty
+ * (see "\e[?7727h").
+ *
+ * NOTE: The delay is overwritten by initscr() on netbsd-curses.
+ */
+#ifdef CURSES_TTY
+ if (!g_getenv("ESCDELAY"))
+ set_escdelay(25);
+#endif
+
+ /*
* We always have a CTRL handler on Windows, but doing it
* here again, ensures that we have a higher precedence
* than the one installed by PDCurses.
@@ -699,12 +702,22 @@ teco_interface_init_interactive(GError **error)
curs_set(0);
teco_interface.info_window = newwin(1, 0, 0, 0);
-
teco_interface.msg_window = newwin(1, 0, LINES - 2, 0);
-
teco_interface.cmdline_window = newwin(0, 0, LINES - 1, 0);
- keypad(teco_interface.cmdline_window, TRUE);
- nodelay(teco_interface.cmdline_window, TRUE);
+
+ teco_interface.input_pad = newpad(1, 1);
+ /*
+ * Controlling function key processing is important
+ * on Unix Curses, as ESCAPE is handled as the beginning
+ * of a escape sequence when terminal emulators are
+ * involved.
+ * Still, it's now enabled always since the ESCDELAY
+ * workaround works nicely.
+ * On some Curses variants (XCurses) keypad
+ * must always be TRUE so we receive KEY_RESIZE.
+ */
+ keypad(teco_interface.input_pad, TRUE);
+ nodelay(teco_interface.input_pad, TRUE);
teco_interface.input_queue = g_queue_new();
@@ -748,8 +761,8 @@ teco_interface_restore_batch(void)
* Set window title to a reasonable default,
* in case it is not reset immediately by the
* shell.
- * FIXME: See set_window_title() why this
- * is necessary.
+ * FIXME: See teco_interface_set_window_title()
+ * why this is necessary.
*/
#if defined(CURSES_TTY) && defined(HAVE_TIGETSTR)
teco_interface_set_window_title(g_getenv("TERM") ? : "");
@@ -978,10 +991,14 @@ teco_interface_draw_info(void)
const gchar *info_type_str;
+ waddstr(teco_interface.info_window, PACKAGE_NAME " ");
+
switch (teco_interface.info_type) {
case TECO_INFO_TYPE_QREG:
info_type_str = PACKAGE_NAME " - <QRegister> ";
- waddstr(teco_interface.info_window, info_type_str);
+ teco_curses_add_wc(teco_interface.info_window,
+ teco_ed & TECO_ED_ICONS ? TECO_CURSES_ICONS_QREG : '-');
+ waddstr(teco_interface.info_window, " <QRegister> ");
/* same formatting as in command lines */
teco_curses_format_str(teco_interface.info_window,
teco_interface.info_current.data,
@@ -990,10 +1007,15 @@ teco_interface_draw_info(void)
case TECO_INFO_TYPE_BUFFER:
info_type_str = PACKAGE_NAME " - <Buffer> ";
- waddstr(teco_interface.info_window, info_type_str);
g_assert(!teco_string_contains(&teco_interface.info_current, '\0'));
+ teco_curses_add_wc(teco_interface.info_window,
+ teco_ed & TECO_ED_ICONS ? teco_curses_icons_lookup_file(teco_interface.info_current.data) : '-');
+ waddstr(teco_interface.info_window, " <Buffer> ");
teco_curses_format_filename(teco_interface.info_window,
- teco_interface.info_current.data, -1);
+ teco_interface.info_current.data,
+ getmaxx(teco_interface.info_window) -
+ getcurx(teco_interface.info_window) - 1);
+ waddch(teco_interface.info_window, teco_interface.info_dirty ? '*' : ' ');
break;
default:
@@ -1003,13 +1025,13 @@ teco_interface_draw_info(void)
wclrtoeol(teco_interface.info_window);
/*
- * Make sure the title will consist only of printable
- * characters
+ * Make sure the title will consist only of printable characters.
*/
g_autofree gchar *info_current_printable;
info_current_printable = teco_string_echo(teco_interface.info_current.data,
teco_interface.info_current.len);
- g_autofree gchar *title = g_strconcat(info_type_str, info_current_printable, NULL);
+ g_autofree gchar *title = g_strconcat(info_type_str, info_current_printable,
+ teco_interface.info_dirty ? "*" : "", NULL);
teco_interface_set_window_title(title);
}
@@ -1019,6 +1041,7 @@ teco_interface_info_update_qreg(const teco_qreg_t *reg)
teco_string_clear(&teco_interface.info_current);
teco_string_init(&teco_interface.info_current,
reg->head.name.data, reg->head.name.len);
+ teco_interface.info_dirty = FALSE;
teco_interface.info_type = TECO_INFO_TYPE_QREG;
/* NOTE: drawn in teco_interface_event_loop_iter() */
}
@@ -1030,8 +1053,7 @@ teco_interface_info_update_buffer(const teco_buffer_t *buffer)
teco_string_clear(&teco_interface.info_current);
teco_string_init(&teco_interface.info_current, filename, strlen(filename));
- teco_string_append_c(&teco_interface.info_current,
- buffer->dirty ? '*' : ' ');
+ teco_interface.info_dirty = buffer->dirty;
teco_interface.info_type = TECO_INFO_TYPE_BUFFER;
/* NOTE: drawn in teco_interface_event_loop_iter() */
}
@@ -1044,7 +1066,8 @@ teco_interface_cmdline_update(const teco_cmdline_t *cmdline)
* We don't know if it is similar to the last one,
* so resizing makes no sense.
* We approximate the size of the new formatted command-line,
- * wasting a few bytes for control characters.
+ * wasting a few bytes for control characters and
+ * multi-byte Unicode sequences.
*/
if (teco_interface.cmdline_pad)
delwin(teco_interface.cmdline_pad);
@@ -1172,7 +1195,7 @@ teco_interface_set_clipboard(const gchar *name, const gchar *str, gsize str_len,
{
int rc = str ? PDC_setclipboard(str, str_len) : PDC_clearclipboard();
if (rc != PDC_CLIP_SUCCESS) {
- g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED,
+ g_set_error(error, TECO_ERROR, TECO_ERROR_CLIPBOARD,
"Error %d copying to clipboard", rc);
return FALSE;
}
@@ -1194,7 +1217,7 @@ teco_interface_get_clipboard(const gchar *name, gchar **str, gsize *len, GError
if (rc == PDC_CLIP_EMPTY)
return TRUE;
if (rc != PDC_CLIP_SUCCESS) {
- g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED,
+ g_set_error(error, TECO_ERROR, TECO_ERROR_CLIPBOARD,
"Error %d retrieving clipboard", rc);
return FALSE;
}
@@ -1232,9 +1255,17 @@ teco_interface_init_clipboard(void)
* must be enabled.
* There is no way to find out if they are but we must
* not register the clipboard registers if they aren't.
- * Therefore, a special XTerm clipboard ED flag an be set by the user.
+ * Still, XTerm clipboards are broken with Unicode characters.
+ * Also, there are other terminal emulators supporting OSC-52,
+ * so the XTerm version is only checked if the terminal identifies as XTerm.
+ * Also, a special clipboard ED flag must be set by the user.
+ *
+ * NOTE: Apparently there is also a terminfo entry Ms, but it's probably
+ * not worth using it since it won't always be set and even if set, does not
+ * tell you whether the terminal will actually answer to the escape sequence or not.
*/
- if (!(teco_ed & TECO_ED_XTERM_CLIPBOARD) || teco_xterm_version() < 203)
+ if (!(teco_ed & TECO_ED_OSC52) ||
+ (teco_xterm_version() >= 0 && teco_xterm_version() < 203))
return;
teco_qreg_table_insert(&teco_qreg_table_globals, teco_qreg_clipboard_new(""));
@@ -1300,6 +1331,8 @@ teco_interface_set_clipboard(const gchar *name, const gchar *str, gsize str_len,
gboolean
teco_interface_get_clipboard(const gchar *name, gchar **str, gsize *len, GError **error)
{
+ gboolean ret = TRUE;
+
/*
* Query the clipboard -- XTerm will reply with the
* OSC-52 command that would set the current selection.
@@ -1320,18 +1353,19 @@ teco_interface_get_clipboard(const gchar *name, gchar **str, gsize *len, GError
* to be on the safe side.
*/
halfdelay(1); /* 100ms timeout */
- keypad(stdscr, FALSE);
+ /* don't interpret escape sequences */
+ keypad(teco_interface.input_pad, FALSE);
/*
* Skip "\e]52;x;" (7 characters).
*/
for (gint i = 0; i < 7; i++) {
- if (getch() == ERR) {
+ ret = wgetch(teco_interface.input_pad) != ERR;
+ if (!ret) {
/* timeout */
- cbreak();
- g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED,
+ g_set_error_literal(error, TECO_ERROR, TECO_ERROR_CLIPBOARD,
"Timed out reading XTerm clipboard");
- return FALSE;
+ goto cleanup;
}
}
@@ -1347,17 +1381,22 @@ teco_interface_get_clipboard(const gchar *name, gchar **str, gsize *len, GError
*/
gchar buffer[MAX(3, 7)];
- gchar c = (gchar)getch();
- if (c == ERR) {
+ gchar c = (gchar)wgetch(teco_interface.input_pad);
+ ret = c != ERR;
+ if (!ret) {
/* timeout */
- cbreak();
g_string_free(str_base64, TRUE);
- g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED,
+ g_set_error_literal(error, TECO_ERROR, TECO_ERROR_CLIPBOARD,
"Timed out reading XTerm clipboard");
- return FALSE;
+ goto cleanup;
}
if (c == '\a')
break;
+ if (c == '\e') {
+ /* OSC escape sequence can also be terminated by "\e\\" */
+ c = (gchar)wgetch(teco_interface.input_pad);
+ break;
+ }
/*
* This could be simplified using sscanf() and
@@ -1372,14 +1411,16 @@ teco_interface_get_clipboard(const gchar *name, gchar **str, gsize *len, GError
g_string_append_len(str_base64, buffer, out_len);
}
- cbreak();
-
if (str)
*str = str_base64->str;
*len = str_base64->len;
g_string_free(str_base64, !str);
- return TRUE;
+
+cleanup:
+ keypad(teco_interface.input_pad, TRUE);
+ nodelay(teco_interface.input_pad, TRUE);
+ return ret;
}
#else /* !PDCURSES && !CURSES_TTY */
@@ -1489,13 +1530,17 @@ teco_interface_is_interrupted(void)
gboolean
teco_interface_is_interrupted(void)
{
- if (!teco_interface.cmdline_window)
+ if (!teco_interface.input_pad)
/* batch mode */
return teco_interrupted != FALSE;
- /* NOTE: getch() is configured to be nonblocking. */
+ /*
+ * NOTE: wgetch() is configured to be nonblocking.
+ * We wgetch() on a dummy pad, so this does not call any
+ * wrefresh().
+ */
gint key;
- while ((key = wgetch(teco_interface.cmdline_window)) != ERR) {
+ while ((key = wgetch(teco_interface.input_pad)) != ERR) {
if (G_UNLIKELY(key == TECO_CTL_KEY('C')))
return TRUE;
g_queue_push_tail(teco_interface.input_queue,
@@ -1535,35 +1580,19 @@ teco_interface_refresh(void)
static gint
teco_interface_blocking_getch(void)
{
- /*
- * Setting function key processing is important
- * on Unix Curses, as ESCAPE is handled as the beginning
- * of a escape sequence when terminal emulators are
- * involved.
- * On some Curses variants (XCurses) however, keypad
- * must always be TRUE so we receive KEY_RESIZE.
- *
- * FIXME: NetBSD's curses could be handled like ncurses,
- * but gets into an undefined state when SciTECO processes
- * escape sequences.
- */
-#ifdef NCURSES_UNIX
- keypad(teco_interface.cmdline_window, teco_ed & TECO_ED_FNKEYS);
-#endif
-
/* no special <CTRL/C> handling */
raw();
- nodelay(teco_interface.cmdline_window, FALSE);
+ nodelay(teco_interface.input_pad, FALSE);
/*
* Memory limiting is stopped temporarily, since it might otherwise
* constantly place 100% load on the CPU.
*/
teco_memory_stop_limiting();
- gint key = wgetch(teco_interface.cmdline_window);
+ gint key = wgetch(teco_interface.input_pad);
teco_memory_start_limiting();
/* allow asynchronous interruptions on <CTRL/C> */
teco_interrupted = FALSE;
- nodelay(teco_interface.cmdline_window, TRUE);
+ nodelay(teco_interface.input_pad, TRUE);
#if defined(CURSES_TTY) || defined(PDCURSES_WINCON) || defined(NCURSES_WIN32)
noraw(); /* FIXME: necessary because of NCURSES_WIN32 bug */
cbreak();
@@ -1585,6 +1614,11 @@ teco_interface_blocking_getch(void)
void
teco_interface_event_loop_iter(void)
{
+ static gchar keybuf[4];
+ static gint keybuf_i = 0;
+
+ GError **error = &teco_interface.event_loop_error;
+
gint key = g_queue_is_empty(teco_interface.input_queue)
? teco_interface_blocking_getch()
: GPOINTER_TO_INT(g_queue_pop_head(teco_interface.input_queue));
@@ -1613,23 +1647,24 @@ teco_interface_event_loop_iter(void)
* backspace.
* In SciTECO backspace is normalized to ^H.
*/
- if (!teco_cmdline_keypress_c(TECO_CTL_KEY('H'),
- &teco_interface.event_loop_error))
+ if (!teco_cmdline_keymacro_c(TECO_CTL_KEY('H'), error))
return;
break;
case KEY_ENTER:
case '\r':
case '\n':
- if (!teco_cmdline_keypress_c('\n', &teco_interface.event_loop_error))
+ if (!teco_cmdline_keymacro_c('\n', error))
return;
break;
/*
* Function key macros
+ *
+ * FIXME: Perhaps support everything returned by keyname()?
*/
#define FN(KEY) \
case KEY_##KEY: \
- if (!teco_cmdline_fnmacro(#KEY, &teco_interface.event_loop_error)) \
+ if (!teco_cmdline_keymacro(#KEY, -1, error)) \
return; \
break
#define FNS(KEY) FN(KEY); FN(S##KEY)
@@ -1639,9 +1674,8 @@ teco_interface_event_loop_iter(void)
gchar macro_name[3+1];
g_snprintf(macro_name, sizeof(macro_name),
- "F%d", key - KEY_F0);
- if (!teco_cmdline_fnmacro(macro_name,
- &teco_interface.event_loop_error))
+ "F%d", key - KEY_F0);
+ if (!teco_cmdline_keymacro(macro_name, -1, error))
return;
break;
}
@@ -1660,9 +1694,31 @@ teco_interface_event_loop_iter(void)
* Control keys and keys with printable representation
*/
default:
- if (key < 0x80 &&
- !teco_cmdline_keypress_c(key, &teco_interface.event_loop_error))
+ if (key > 0xFF)
+ /* unhandled function key */
return;
+
+ /*
+ * NOTE: There's also wget_wch(), but it requires
+ * a widechar version of Curses.
+ */
+ keybuf[keybuf_i++] = key;
+ gsize len = keybuf_i;
+ gunichar cp = g_utf8_get_char_validated(keybuf, len);
+ if (keybuf_i >= sizeof(keybuf) || cp != (gunichar)-2)
+ keybuf_i = 0;
+ if ((gint32)cp < 0)
+ /* incomplete or invalid */
+ return;
+ switch (teco_cmdline_keymacro(keybuf, len, error)) {
+ case TECO_KEYMACRO_ERROR:
+ return;
+ case TECO_KEYMACRO_SUCCESS:
+ break;
+ case TECO_KEYMACRO_UNDEFINED:
+ if (!teco_cmdline_keypress(keybuf, len, error))
+ return;
+ }
}
teco_interface_refresh();
@@ -1733,6 +1789,8 @@ teco_interface_cleanup(void)
delwin(teco_interface.cmdline_pad);
if (teco_interface.msg_window)
delwin(teco_interface.msg_window);
+ if (teco_interface.input_pad)
+ delwin(teco_interface.input_pad);
/*
* PDCurses/WinCon crashes if initscr() wasn't called.