aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/interface-curses
diff options
context:
space:
mode:
Diffstat (limited to 'src/interface-curses')
-rw-r--r--src/interface-curses/Makefile.am11
-rw-r--r--src/interface-curses/curses-info-popup.c211
-rw-r--r--src/interface-curses/curses-info-popup.cpp219
-rw-r--r--src/interface-curses/curses-info-popup.h96
-rw-r--r--src/interface-curses/curses-utils.c (renamed from src/interface-curses/curses-utils.cpp)38
-rw-r--r--src/interface-curses/curses-utils.h22
-rw-r--r--src/interface-curses/interface-curses.h199
-rw-r--r--src/interface-curses/interface.c (renamed from src/interface-curses/interface-curses.cpp)1044
8 files changed, 848 insertions, 992 deletions
diff --git a/src/interface-curses/Makefile.am b/src/interface-curses/Makefile.am
index 01e68ae..14fc920 100644
--- a/src/interface-curses/Makefile.am
+++ b/src/interface-curses/Makefile.am
@@ -1,8 +1,9 @@
-AM_CPPFLAGS += -I$(top_srcdir)/src
+AM_CPPFLAGS += -I$(top_srcdir)/contrib/rb3ptr \
+ -I$(top_srcdir)/src
-AM_CXXFLAGS = -Wall -Wno-char-subscripts
+AM_CFLAGS = -std=gnu11 -Wall -Wno-initializer-overrides -Wno-unused-value
noinst_LTLIBRARIES = libsciteco-interface.la
-libsciteco_interface_la_SOURCES = interface-curses.cpp interface-curses.h \
- curses-utils.cpp curses-utils.h \
- curses-info-popup.cpp curses-info-popup.h
+libsciteco_interface_la_SOURCES = interface.c \
+ curses-utils.c curses-utils.h \
+ curses-info-popup.c curses-info-popup.h
diff --git a/src/interface-curses/curses-info-popup.c b/src/interface-curses/curses-info-popup.c
new file mode 100644
index 0000000..7d661a2
--- /dev/null
+++ b/src/interface-curses/curses-info-popup.c
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2012-2021 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 <glib.h>
+
+#include <curses.h>
+
+#include "list.h"
+#include "string-utils.h"
+#include "interface.h"
+#include "curses-utils.h"
+#include "curses-info-popup.h"
+
+/*
+ * FIXME: This is redundant with teco-gtk-info-popup.gob.
+ */
+typedef struct {
+ teco_stailq_entry_t entry;
+
+ teco_popup_entry_type_t type;
+ teco_string_t name;
+ gboolean highlight;
+} teco_popup_entry_t;
+
+void
+teco_curses_info_popup_add(teco_curses_info_popup_t *ctx, teco_popup_entry_type_t type,
+ const gchar *name, gsize name_len, gboolean highlight)
+{
+ if (G_UNLIKELY(!ctx->chunk))
+ ctx->chunk = g_string_chunk_new(32);
+
+ /*
+ * FIXME: Test with g_slice_new()...
+ * It could however cause problems upon command-line termination
+ * and may not be measurably faster.
+ */
+ teco_popup_entry_t *entry = g_new(teco_popup_entry_t, 1);
+ entry->type = type;
+ /*
+ * Popup entries aren't removed individually, so we can
+ * more efficiently store them via GStringChunk.
+ */
+ teco_string_init_chunk(&entry->name, name, name_len, ctx->chunk);
+ entry->highlight = highlight;
+
+ teco_stailq_insert_tail(&ctx->list, &entry->entry);
+
+ ctx->longest = MAX(ctx->longest, (gint)name_len);
+ ctx->length++;
+}
+
+static void
+teco_curses_info_popup_init_pad(teco_curses_info_popup_t *ctx, attr_t attr)
+{
+ int cols = getmaxx(stdscr); /**! screen width */
+ int pad_lines; /**! pad height */
+ 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);
+
+ /* pad_cols = floor((cols - 2) / pad_colwidth) */
+ pad_cols = (cols - 2) / pad_colwidth;
+ /* pad_lines = ceil(length / pad_cols) */
+ pad_lines = (ctx->length+pad_cols-1) / pad_cols;
+
+ /*
+ * Render the entire autocompletion list into a pad
+ * which can be higher than the physical screen.
+ * The pad uses two columns less than the screen since
+ * it will be drawn into the popup window which has left
+ * and right borders.
+ */
+ ctx->pad = newpad(pad_lines, cols - 2);
+
+ wbkgd(ctx->pad, ' ' | attr);
+
+ /*
+ * cur_col is the row currently written.
+ * It does not wrap but grows indefinitely.
+ * Therefore the real current row is (cur_col % popup_cols)
+ */
+ gint cur_col = 0;
+ for (teco_stailq_entry_t *cur = ctx->list.first; cur != NULL; cur = cur->next) {
+ teco_popup_entry_t *entry = (teco_popup_entry_t *)cur;
+ gint cur_line = cur_col/pad_cols + 1;
+
+ wmove(ctx->pad, cur_line-1,
+ (cur_col % pad_cols)*pad_colwidth);
+
+ wattrset(ctx->pad, entry->highlight ? A_BOLD : A_NORMAL);
+
+ switch (entry->type) {
+ case TECO_POPUP_FILE:
+ case TECO_POPUP_DIRECTORY:
+ g_assert(!teco_string_contains(&entry->name, '\0'));
+ teco_curses_format_filename(ctx->pad, entry->name.data, -1);
+ break;
+ default:
+ teco_curses_format_str(ctx->pad, entry->name.data, entry->name.len, -1);
+ break;
+ }
+
+ cur_col++;
+ }
+}
+
+void
+teco_curses_info_popup_show(teco_curses_info_popup_t *ctx, attr_t attr)
+{
+ if (!ctx->length)
+ /* nothing to display */
+ return;
+
+ int lines, cols; /* screen dimensions */
+ getmaxyx(stdscr, lines, cols);
+
+ if (ctx->window)
+ delwin(ctx->window);
+
+ if (!ctx->pad)
+ teco_curses_info_popup_init_pad(ctx, attr);
+ gint pad_lines = getmaxy(ctx->pad);
+
+ /*
+ * Popup window can cover all but one screen row.
+ * Another row is reserved for the top border.
+ */
+ gint popup_lines = MIN(pad_lines + 1, lines - 1);
+
+ /* window covers message, scintilla and info windows */
+ ctx->window = newwin(popup_lines, 0, lines - 1 - popup_lines, 0);
+
+ wbkgdset(ctx->window, ' ' | attr);
+
+ wborder(ctx->window,
+ ACS_VLINE,
+ ACS_VLINE, /* may be overwritten with scrollbar */
+ ACS_HLINE,
+ ' ', /* no bottom line */
+ ACS_ULCORNER, ACS_URCORNER,
+ ACS_VLINE, ACS_VLINE);
+
+ copywin(ctx->pad, ctx->window,
+ ctx->pad_first_line, 0,
+ 1, 1, popup_lines - 1, cols - 2, FALSE);
+
+ if (pad_lines <= popup_lines - 1)
+ /* no need for scrollbar */
+ return;
+
+ /* bar_height = ceil((popup_lines-1)/pad_lines * (popup_lines-2)) */
+ gint bar_height = ((popup_lines-1)*(popup_lines-2) + pad_lines-1) /
+ pad_lines;
+ /* bar_y = floor(pad_first_line/pad_lines * (popup_lines-2)) + 1 */
+ gint bar_y = ctx->pad_first_line*(popup_lines-2) / pad_lines + 1;
+
+ mvwvline(ctx->window, 1, cols-1, ACS_CKBOARD, popup_lines-2);
+ /*
+ * We do not use ACS_BLOCK here since it will not
+ * always be drawn as a solid block (e.g. xterm).
+ * Instead, simply draw reverse blanks.
+ */
+ wmove(ctx->window, bar_y, cols-1);
+ wattron(ctx->window, A_REVERSE);
+ wvline(ctx->window, ' ', bar_height);
+
+ /* progress scroll position */
+ ctx->pad_first_line += popup_lines - 1;
+ /* wrap on last shown page */
+ ctx->pad_first_line %= pad_lines;
+ if (pad_lines - ctx->pad_first_line < popup_lines - 1)
+ /* show last page */
+ ctx->pad_first_line = pad_lines - (popup_lines - 1);
+}
+
+void
+teco_curses_info_popup_clear(teco_curses_info_popup_t *ctx)
+{
+ if (ctx->window)
+ delwin(ctx->window);
+ if (ctx->pad)
+ delwin(ctx->pad);
+ if (ctx->chunk)
+ g_string_chunk_free(ctx->chunk);
+
+ teco_stailq_entry_t *entry;
+ while ((entry = teco_stailq_remove_head(&ctx->list)))
+ g_free(entry);
+
+ teco_curses_info_popup_init(ctx);
+}
diff --git a/src/interface-curses/curses-info-popup.cpp b/src/interface-curses/curses-info-popup.cpp
deleted file mode 100644
index 487f1b7..0000000
--- a/src/interface-curses/curses-info-popup.cpp
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * Copyright (C) 2012-2017 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 <string.h>
-
-#include <glib.h>
-
-#include <curses.h>
-
-#include "curses-utils.h"
-#include "curses-info-popup.h"
-
-namespace SciTECO {
-
-void
-CursesInfoPopup::add(PopupEntryType type,
- const gchar *name, bool highlight)
-{
- size_t name_len = strlen(name);
- Entry *entry = (Entry *)g_malloc(sizeof(Entry) + name_len + 1);
-
- entry->type = type;
- entry->highlight = highlight;
- strcpy(entry->name, name);
-
- longest = MAX(longest, (gint)name_len);
- length++;
-
- /*
- * Entries are added in reverse (constant time for GSList),
- * so they will later have to be reversed.
- */
- list = g_slist_prepend(list, entry);
-}
-
-void
-CursesInfoPopup::init_pad(attr_t attr)
-{
- int cols = getmaxx(stdscr); /* screen width */
- int pad_lines; /* pad height */
- gint pad_cols; /* entry columns */
- gint pad_colwidth; /* width per entry column */
-
- gint cur_col;
-
- /* reserve 2 spaces between columns */
- pad_colwidth = MIN(longest + 2, cols - 2);
-
- /* pad_cols = floor((cols - 2) / pad_colwidth) */
- pad_cols = (cols - 2) / pad_colwidth;
- /* pad_lines = ceil(length / pad_cols) */
- pad_lines = (length+pad_cols-1) / pad_cols;
-
- /*
- * Render the entire autocompletion list into a pad
- * which can be higher than the physical screen.
- * The pad uses two columns less than the screen since
- * it will be drawn into the popup window which has left
- * and right borders.
- */
- pad = newpad(pad_lines, cols - 2);
-
- wbkgd(pad, ' ' | attr);
-
- /*
- * cur_col is the row currently written.
- * It does not wrap but grows indefinitely.
- * Therefore the real current row is (cur_col % popup_cols)
- */
- cur_col = 0;
- for (GSList *cur = list; cur != NULL; cur = g_slist_next(cur)) {
- Entry *entry = (Entry *)cur->data;
- gint cur_line = cur_col/pad_cols + 1;
-
- wmove(pad, cur_line-1,
- (cur_col % pad_cols)*pad_colwidth);
-
- wattrset(pad, entry->highlight ? A_BOLD : A_NORMAL);
-
- switch (entry->type) {
- case POPUP_FILE:
- case POPUP_DIRECTORY:
- Curses::format_filename(pad, entry->name);
- break;
- default:
- Curses::format_str(pad, entry->name);
- break;
- }
-
- cur_col++;
- }
-}
-
-void
-CursesInfoPopup::show(attr_t attr)
-{
- int lines, cols; /* screen dimensions */
- gint pad_lines;
- gint popup_lines;
- gint bar_height, bar_y;
-
- if (!length)
- /* nothing to display */
- return;
-
- getmaxyx(stdscr, lines, cols);
-
- if (window)
- delwin(window);
- else
- /* reverse list only once */
- list = g_slist_reverse(list);
-
- if (!pad)
- init_pad(attr);
- pad_lines = getmaxy(pad);
-
- /*
- * Popup window can cover all but one screen row.
- * Another row is reserved for the top border.
- */
- popup_lines = MIN(pad_lines + 1, lines - 1);
-
- /* window covers message, scintilla and info windows */
- window = newwin(popup_lines, 0, lines - 1 - popup_lines, 0);
-
- wbkgdset(window, ' ' | attr);
-
- wborder(window,
- ACS_VLINE,
- ACS_VLINE, /* may be overwritten with scrollbar */
- ACS_HLINE,
- ' ', /* no bottom line */
- ACS_ULCORNER, ACS_URCORNER,
- ACS_VLINE, ACS_VLINE);
-
- copywin(pad, window,
- pad_first_line, 0,
- 1, 1, popup_lines - 1, cols - 2, FALSE);
-
- if (pad_lines <= popup_lines - 1)
- /* no need for scrollbar */
- return;
-
- /* bar_height = ceil((popup_lines-1)/pad_lines * (popup_lines-2)) */
- bar_height = ((popup_lines-1)*(popup_lines-2) + pad_lines-1) /
- pad_lines;
- /* bar_y = floor(pad_first_line/pad_lines * (popup_lines-2)) + 1 */
- bar_y = pad_first_line*(popup_lines-2) / pad_lines + 1;
-
- mvwvline(window, 1, cols-1, ACS_CKBOARD, popup_lines-2);
- /*
- * We do not use ACS_BLOCK here since it will not
- * always be drawn as a solid block (e.g. xterm).
- * Instead, simply draw reverse blanks.
- */
- wmove(window, bar_y, cols-1);
- wattron(window, A_REVERSE);
- wvline(window, ' ', bar_height);
-
- /* progress scroll position */
- pad_first_line += popup_lines - 1;
- /* wrap on last shown page */
- pad_first_line %= pad_lines;
- if (pad_lines - pad_first_line < popup_lines - 1)
- /* show last page */
- pad_first_line = pad_lines - (popup_lines - 1);
-}
-
-void
-CursesInfoPopup::clear(void)
-{
- g_slist_free_full(list, g_free);
- list = NULL;
- length = 0;
- longest = 0;
-
- pad_first_line = 0;
-
- if (window) {
- delwin(window);
- window = NULL;
- }
-
- if (pad) {
- delwin(pad);
- pad = NULL;
- }
-}
-
-CursesInfoPopup::~CursesInfoPopup()
-{
- if (window)
- delwin(window);
- if (pad)
- delwin(pad);
- if (list)
- g_slist_free_full(list, g_free);
-}
-
-} /* namespace SciTECO */
diff --git a/src/interface-curses/curses-info-popup.h b/src/interface-curses/curses-info-popup.h
index af09cb4..d911182 100644
--- a/src/interface-curses/curses-info-popup.h
+++ b/src/interface-curses/curses-info-popup.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2017 Robin Haberkorn
+ * Copyright (C) 2012-2021 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
@@ -14,78 +14,52 @@
* 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
-#ifndef __CURSES_INFO_POPUP_H
-#define __CURSES_INFO_POPUP_H
+#include <string.h>
#include <glib.h>
#include <curses.h>
-#include "memory.h"
+#include "list.h"
+#include "interface.h"
-namespace SciTECO {
+typedef struct {
+ WINDOW *window; /**! window showing part of pad */
+ WINDOW *pad; /**! full-height entry list */
-class CursesInfoPopup : public Object {
-public:
- /**
- * @bug This is identical to the type defined in
- * interface.h. But for the sake of abstraction
- * we cannot access it here (or in gtk-info-popup
- * for that matter).
- */
- enum PopupEntryType {
- POPUP_PLAIN,
- POPUP_FILE,
- POPUP_DIRECTORY
- };
+ teco_stailq_head_t list; /**! list of popup entries */
+ gint longest; /**! size of longest entry */
+ gint length; /**! total number of popup entries */
-private:
- WINDOW *window; /**! window showing part of pad */
- WINDOW *pad; /**! full-height entry list */
+ gint pad_first_line; /**! first line in pad to show */
- struct Entry {
- PopupEntryType type;
- bool highlight;
- gchar name[];
- };
+ GStringChunk *chunk; /**! string chunk for all popup entry names */
+} teco_curses_info_popup_t;
- GSList *list; /**! list of popup entries */
- gint longest; /**! size of longest entry */
- gint length; /**! total number of popup entries */
+static inline void
+teco_curses_info_popup_init(teco_curses_info_popup_t *ctx)
+{
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->list = TECO_STAILQ_HEAD_INITIALIZER(&ctx->list);
+}
- gint pad_first_line; /**! first line in pad to show */
+void teco_curses_info_popup_add(teco_curses_info_popup_t *ctx, teco_popup_entry_type_t type,
+ const gchar *name, gsize name_len, gboolean highlight);
-public:
- CursesInfoPopup() : window(NULL), pad(NULL),
- list(NULL), longest(0), length(0),
- pad_first_line(0) {}
+void teco_curses_info_popup_show(teco_curses_info_popup_t *ctx, attr_t attr);
+static inline bool
+teco_curses_info_popup_is_shown(teco_curses_info_popup_t *ctx)
+{
+ return ctx->window != NULL;
+}
- void add(PopupEntryType type,
- const gchar *name, bool highlight = false);
+static inline void
+teco_curses_info_popup_noutrefresh(teco_curses_info_popup_t *ctx)
+{
+ if (ctx->window)
+ wnoutrefresh(ctx->window);
+}
- void show(attr_t attr);
- inline bool
- is_shown(void)
- {
- return window != NULL;
- }
-
- void clear(void);
-
- inline void
- noutrefresh(void)
- {
- if (window)
- wnoutrefresh(window);
- }
-
- ~CursesInfoPopup();
-
-private:
- void init_pad(attr_t attr);
-};
-
-} /* namespace SciTECO */
-
-#endif
+void teco_curses_info_popup_clear(teco_curses_info_popup_t *ctx);
diff --git a/src/interface-curses/curses-utils.cpp b/src/interface-curses/curses-utils.c
index f5d5c8c..ace5795 100644
--- a/src/interface-curses/curses-utils.cpp
+++ b/src/interface-curses/curses-utils.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2017 Robin Haberkorn
+ * Copyright (C) 2012-2021 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,19 +29,14 @@
#include "string-utils.h"
#include "curses-utils.h"
-namespace SciTECO {
-
gsize
-Curses::format_str(WINDOW *win, const gchar *str,
- gssize len, gint max_width)
+teco_curses_format_str(WINDOW *win, const gchar *str, gsize len, gint max_width)
{
int old_x, old_y;
gint chars_added = 0;
getyx(win, old_y, old_x);
- if (len < 0)
- len = strlen(str);
if (max_width < 0)
max_width = getmaxx(win) - old_x;
@@ -51,7 +46,7 @@ Curses::format_str(WINDOW *win, const gchar *str,
* View::set_representations()
*/
switch (*str) {
- case CTL_KEY_ESC:
+ case '\e':
chars_added++;
if (chars_added > max_width)
goto truncate;
@@ -80,12 +75,12 @@ Curses::format_str(WINDOW *win, const gchar *str,
waddch(win, 'B' | A_REVERSE);
break;
default:
- if (IS_CTL(*str)) {
+ if (TECO_IS_CTL(*str)) {
chars_added += 2;
if (chars_added > max_width)
goto truncate;
waddch(win, '^' | A_REVERSE);
- waddch(win, CTL_ECHO(*str) | A_REVERSE);
+ waddch(win, TECO_CTL_ECHO(*str) | A_REVERSE);
} else {
chars_added++;
if (chars_added > max_width)
@@ -114,29 +109,29 @@ truncate:
}
gsize
-Curses::format_filename(WINDOW *win, const gchar *filename,
- gint max_width)
+teco_curses_format_filename(WINDOW *win, const gchar *filename,
+ gint max_width)
{
int old_x = getcurx(win);
- gchar *filename_canon = String::canonicalize_ctl(filename);
- size_t filename_len = strlen(filename_canon);
+ g_autofree gchar *filename_printable = teco_string_echo(filename, strlen(filename));
+ size_t filename_len = strlen(filename_printable);
if (max_width < 0)
max_width = getmaxx(win) - old_x;
if (filename_len <= (size_t)max_width) {
- waddstr(win, filename_canon);
+ waddstr(win, filename_printable);
} else {
- const gchar *keep_post = filename_canon + filename_len -
+ const gchar *keep_post = filename_printable + filename_len -
max_width + 3;
#ifdef G_OS_WIN32
- const gchar *keep_pre = g_path_skip_root(filename_canon);
+ const gchar *keep_pre = g_path_skip_root(filename_printable);
if (keep_pre) {
- waddnstr(win, filename_canon,
- keep_pre - filename_canon);
- keep_post += keep_pre - filename_canon;
+ waddnstr(win, filename_printable,
+ keep_pre - filename_printable);
+ keep_post += keep_pre - filename_printable;
}
#endif
wattron(win, A_UNDERLINE | A_BOLD);
@@ -145,8 +140,5 @@ Curses::format_filename(WINDOW *win, const gchar *filename,
waddstr(win, keep_post);
}
- g_free(filename_canon);
return getcurx(win) - old_x;
}
-
-} /* namespace SciTECO */
diff --git a/src/interface-curses/curses-utils.h b/src/interface-curses/curses-utils.h
index 778f39e..3a681a4 100644
--- a/src/interface-curses/curses-utils.h
+++ b/src/interface-curses/curses-utils.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2017 Robin Haberkorn
+ * Copyright (C) 2012-2021 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
@@ -14,26 +14,12 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
-#ifndef __CURSES_UTILS_H
-#define __CURSES_UTILS_H
+#pragma once
#include <glib.h>
#include <curses.h>
-namespace SciTECO {
-
-namespace Curses {
-
-gsize format_str(WINDOW *win, const gchar *str,
- gssize len = -1, gint max_width = -1);
-
-gsize format_filename(WINDOW *win, const gchar *filename,
- gint max_width = -1);
-
-} /* namespace Curses */
-
-} /* namespace SciTECO */
+gsize teco_curses_format_str(WINDOW *win, const gchar *str, gsize len, gint max_width);
-#endif
+gsize teco_curses_format_filename(WINDOW *win, const gchar *filename, gint max_width);
diff --git a/src/interface-curses/interface-curses.h b/src/interface-curses/interface-curses.h
deleted file mode 100644
index 32fff1d..0000000
--- a/src/interface-curses/interface-curses.h
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Copyright (C) 2012-2017 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/>.
- */
-
-#ifndef __INTERFACE_CURSES_H
-#define __INTERFACE_CURSES_H
-
-#include <stdarg.h>
-
-#include <glib.h>
-
-#include <curses.h>
-
-#include <Scintilla.h>
-#include <ScintillaTerm.h>
-
-#include "interface.h"
-#include "curses-info-popup.h"
-
-namespace SciTECO {
-
-typedef class ViewCurses : public View<ViewCurses> {
- Scintilla *sci;
-
-public:
- ViewCurses() : sci(NULL) {}
-
- /* implementation of View::initialize() */
- void initialize_impl(void);
-
- inline ~ViewCurses()
- {
- /*
- * NOTE: This deletes/frees the view's
- * curses WINDOW, despite of what old versions
- * of the Scinterm documentation claim.
- */
- if (sci)
- scintilla_delete(sci);
- }
-
- inline void
- noutrefresh(void)
- {
- scintilla_noutrefresh(sci);
- }
-
- inline void
- refresh(void)
- {
- scintilla_refresh(sci);
- }
-
- inline WINDOW *
- get_window(void)
- {
- return scintilla_get_window(sci);
- }
-
- /* implementation of View::ssm() */
- inline sptr_t
- ssm_impl(unsigned int iMessage, uptr_t wParam = 0, sptr_t lParam = 0)
- {
- return scintilla_send_message(sci, iMessage, wParam, lParam);
- }
-} ViewCurrent;
-
-typedef class InterfaceCurses : public Interface<InterfaceCurses, ViewCurses> {
- /**
- * Mapping of the first 16 curses color codes (that may or may not
- * correspond with the standard terminal color codes) to
- * Scintilla-compatible RGB values (red is LSB) to initialize after
- * Curses startup.
- * Negative values mean no color redefinition (keep the original
- * palette entry).
- */
- gint32 color_table[16];
-
- /**
- * Mapping of the first 16 curses color codes to their
- * original values for restoring them on shutdown.
- * Unfortunately, this may not be supported on all
- * curses ports, so this array may be unused.
- */
- struct {
- short r, g, b;
- } orig_color_table[16];
-
- int stdout_orig, stderr_orig;
- SCREEN *screen;
- FILE *screen_tty;
-
- WINDOW *info_window;
- enum {
- INFO_TYPE_BUFFER = 0,
- INFO_TYPE_QREGISTER
- } info_type;
- gchar *info_current;
-
- WINDOW *msg_window;
-
- WINDOW *cmdline_window, *cmdline_pad;
- gsize cmdline_len, cmdline_rubout_len;
-
- CursesInfoPopup popup;
-
-public:
- InterfaceCurses();
- ~InterfaceCurses();
-
- /* override of Interface::init() */
- void init(void);
-
- /* override of Interface::init_color() */
- void init_color(guint color, guint32 rgb);
-
- /* implementation of Interface::vmsg() */
- void vmsg_impl(MessageType type, const gchar *fmt, va_list ap);
- /* override of Interface::msg_clear() */
- void msg_clear(void);
-
- /* implementation of Interface::show_view() */
- void show_view_impl(ViewCurses *view);
-
- /* implementation of Interface::info_update() */
- void info_update_impl(const QRegister *reg);
- void info_update_impl(const Buffer *buffer);
-
- /* implementation of Interface::cmdline_update() */
- void cmdline_update_impl(const Cmdline *cmdline);
-
- /* override of Interface::set_clipboard() */
- void set_clipboard(const gchar *name,
- const gchar *str = NULL, gssize str_len = -1);
- /* override of Interface::get_clipboard() */
- gchar *get_clipboard(const gchar *name, gsize *str_len = NULL);
-
- /* implementation of Interface::popup_add() */
- inline void
- popup_add_impl(PopupEntryType type,
- const gchar *name, bool highlight = false)
- {
- /* FIXME: The enum casting is dangerous */
- if (cmdline_window)
- /* interactive mode */
- popup.add((CursesInfoPopup::PopupEntryType)type,
- name, highlight);
- }
-
- /* implementation of Interface::popup_show() */
- void popup_show_impl(void);
- /* implementation of Interface::popup_is_shown() */
- inline bool
- popup_is_shown_impl(void)
- {
- return popup.is_shown();
- }
-
- /* implementation of Interface::popup_clear() */
- void popup_clear_impl(void);
-
- /* main entry point (implementation) */
- void event_loop_impl(void);
-
-private:
- void init_color_safe(guint color, guint32 rgb);
- void restore_colors(void);
-
- void init_screen(void);
- void init_interactive(void);
- void restore_batch(void);
-
- void init_clipboard(void);
-
- void resize_all_windows(void);
-
- void set_window_title(const gchar *title);
- void draw_info(void);
- void draw_cmdline(void);
-
- friend void event_loop_iter();
-} InterfaceCurrent;
-
-} /* namespace SciTECO */
-
-#endif
diff --git a/src/interface-curses/interface-curses.cpp b/src/interface-curses/interface.c
index a06fe30..821581b 100644
--- a/src/interface-curses/interface-curses.cpp
+++ b/src/interface-curses/interface.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2017 Robin Haberkorn
+ * Copyright (C) 2012-2021 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
@@ -27,6 +27,15 @@
#include <locale.h>
#include <errno.h>
+#ifdef HAVE_WINDOWS_H
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif
+
+#ifdef EMSCRIPTEN
+#include <emscripten.h>
+#endif
+
#include <glib.h>
#include <glib/gprintf.h>
#include <glib/gstdio.h>
@@ -45,26 +54,17 @@
#include <Scintilla.h>
#include <ScintillaTerm.h>
-#ifdef EMSCRIPTEN
-#include <emscripten.h>
-#endif
-
#include "sciteco.h"
#include "string-utils.h"
#include "cmdline.h"
-#include "qregisters.h"
+#include "qreg.h"
#include "ring.h"
#include "error.h"
-#include "interface.h"
-#include "interface-curses.h"
#include "curses-utils.h"
#include "curses-info-popup.h"
-
-#ifdef HAVE_WINDOWS_H
-/* here it shouldn't cause conflicts with other headers */
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#endif
+#include "view.h"
+#include "memory.h"
+#include "interface.h"
/**
* Whether we have PDCurses-only routines:
@@ -99,18 +99,11 @@
#endif
#ifdef NCURSES_VERSION
-#if defined(G_OS_UNIX) || defined(G_OS_HAIKU)
-/**
- * Whether we're on ncurses/UNIX.
- * Haiku has a UNIX-like terminal and is largely
- * POSIX compliant, so we can handle it like a
- * UNIX ncurses.
- */
+#ifdef G_OS_UNIX
+/** Whether we're on ncurses/UNIX. */
#define NCURSES_UNIX
#elif defined(G_OS_WIN32)
-/**
- * Whether we're on ncurses/win32 console
- */
+/** Whether we're on ncurses/win32 console */
#define NCURSES_WIN32
#endif
#endif
@@ -123,10 +116,6 @@
#define CURSES_TTY
#endif
-namespace SciTECO {
-
-extern "C" {
-
/*
* PDCurses/win32a by default assigns functions to certain
* keys like CTRL+V, CTRL++, CTRL+- and CTRL+=.
@@ -147,9 +136,6 @@ int PDC_set_function_key(const unsigned function, const int new_key);
#define FUNCTION_KEY_CHOOSE_FONT 4
#endif
-static void scintilla_notify(Scintilla *sci, int idFrom,
- void *notify, void *user_data);
-
#if defined(PDCURSES_WIN32) || defined(NCURSES_WIN32)
/**
@@ -161,11 +147,11 @@ static void scintilla_notify(Scintilla *sci, int idFrom,
* separate thread.
*/
static BOOL WINAPI
-console_ctrl_handler(DWORD type)
+teco_console_ctrl_handler(DWORD type)
{
switch (type) {
case CTRL_C_EVENT:
- sigint_occurred = TRUE;
+ teco_sigint_occurred = TRUE;
return TRUE;
}
@@ -174,9 +160,7 @@ console_ctrl_handler(DWORD type)
#endif
-} /* extern "C" */
-
-static gint xterm_version(void) G_GNUC_UNUSED;
+static gint teco_xterm_version(void) G_GNUC_UNUSED;
#define UNNAMED_FILE "(Unnamed)"
@@ -223,12 +207,12 @@ static gint xterm_version(void) G_GNUC_UNUSED;
* for each component).
*/
static inline void
-rgb2curses(guint32 rgb, short &r, short &g, short &b)
+teco_rgb2curses_triple(guint32 rgb, gshort *r, gshort *g, gshort *b)
{
/* NOTE: We could also use 200/51 */
- r = ((rgb & 0x0000FF) >> 0)*1000/0xFF;
- g = ((rgb & 0x00FF00) >> 8)*1000/0xFF;
- b = ((rgb & 0xFF0000) >> 16)*1000/0xFF;
+ *r = ((rgb & 0x0000FF) >> 0)*1000/0xFF;
+ *g = ((rgb & 0x00FF00) >> 8)*1000/0xFF;
+ *b = ((rgb & 0xFF0000) >> 16)*1000/0xFF;
}
/**
@@ -240,8 +224,8 @@ rgb2curses(guint32 rgb, short &r, short &g, short &b)
* It is equivalent to Scinterm's internal `term_color`
* function.
*/
-static short
-rgb2curses(guint32 rgb)
+static gshort
+teco_rgb2curses(guint32 rgb)
{
switch (rgb) {
case 0x000000: return COLOR_BLACK;
@@ -266,20 +250,19 @@ rgb2curses(guint32 rgb)
}
static gint
-xterm_version(void)
+teco_xterm_version(void)
{
static gint xterm_patch = -2;
- const gchar *term = g_getenv("TERM");
- const gchar *xterm_version;
-
/*
* The XTerm patch level (version) is cached.
*/
- if (xterm_patch != -2)
+ if (G_LIKELY(xterm_patch != -2))
return xterm_patch;
xterm_patch = -1;
+ const gchar *term = g_getenv("TERM");
+
if (!term || !g_str_has_prefix(term, "xterm"))
/* no XTerm */
return -1;
@@ -290,7 +273,7 @@ xterm_version(void)
* XTERM_VERSION however should be sufficient to tell
* whether we are running under a real XTerm.
*/
- xterm_version = g_getenv("XTERM_VERSION");
+ const gchar *xterm_version = g_getenv("XTERM_VERSION");
if (!xterm_version)
/* no XTerm */
return -1;
@@ -305,32 +288,121 @@ xterm_version(void)
return xterm_patch;
}
-void
-ViewCurses::initialize_impl(void)
+/*
+ * NOTE: The teco_view_t pointer is reused to directly
+ * point to the Scintilla object.
+ * This saves one heap object per view.
+ */
+
+static void
+teco_view_scintilla_notify(Scintilla *sci, int idFrom, void *notify, void *user_data)
+{
+ teco_interface_process_notify(notify);
+}
+
+teco_view_t *
+teco_view_new(void)
+{
+ return (teco_view_t *)scintilla_new(teco_view_scintilla_notify);
+}
+
+static inline void
+teco_view_noutrefresh(teco_view_t *ctx)
+{
+ scintilla_noutrefresh((Scintilla *)ctx);
+}
+
+static inline WINDOW *
+teco_view_get_window(teco_view_t *ctx)
+{
+ return scintilla_get_window((Scintilla *)ctx);
+}
+
+sptr_t
+teco_view_ssm(teco_view_t *ctx, unsigned int iMessage, uptr_t wParam, sptr_t lParam)
{
- sci = scintilla_new(scintilla_notify);
- setup();
+ return scintilla_send_message((Scintilla *)ctx, iMessage, wParam, lParam);
}
-InterfaceCurses::InterfaceCurses() : stdout_orig(-1), stderr_orig(-1),
- screen(NULL),
- screen_tty(NULL),
- info_window(NULL),
- info_type(INFO_TYPE_BUFFER),
- info_current(NULL),
- msg_window(NULL),
- cmdline_window(NULL), cmdline_pad(NULL),
- cmdline_len(0), cmdline_rubout_len(0)
+void
+teco_view_free(teco_view_t *ctx)
{
- for (guint i = 0; i < G_N_ELEMENTS(color_table); i++)
- color_table[i] = -1;
- for (guint i = 0; i < G_N_ELEMENTS(orig_color_table); i++)
- orig_color_table[i].r = -1;
+ scintilla_delete((Scintilla *)ctx);
}
+static struct {
+ /**
+ * Mapping of the first 16 curses color codes (that may or may not
+ * correspond with the standard terminal color codes) to
+ * Scintilla-compatible RGB values (red is LSB) to initialize after
+ * Curses startup.
+ * Negative values mean no color redefinition (keep the original
+ * palette entry).
+ */
+ gint32 color_table[16];
+
+ /**
+ * Mapping of the first 16 curses color codes to their
+ * original values for restoring them on shutdown.
+ * Unfortunately, this may not be supported on all
+ * curses ports, so this array may be unused.
+ */
+ struct {
+ gshort r, g, b;
+ } orig_color_table[16];
+
+ int stdout_orig, stderr_orig;
+ SCREEN *screen;
+ FILE *screen_tty;
+
+ WINDOW *info_window;
+ enum {
+ TECO_INFO_TYPE_BUFFER = 0,
+ TECO_INFO_TYPE_QREG
+ } info_type;
+ teco_string_t info_current;
+
+ WINDOW *msg_window;
+
+ WINDOW *cmdline_window, *cmdline_pad;
+ gsize cmdline_len, cmdline_rubout_len;
+
+ teco_curses_info_popup_t popup;
+
+ /**
+ * GError "thrown" by teco_interface_event_loop_iter().
+ * Having this in a variable avoids problems with EMScripten.
+ */
+ GError *event_loop_error;
+} teco_interface;
+
+static void teco_interface_init_color_safe(guint color, guint32 rgb);
+static void teco_interface_restore_colors(void);
+
+static void teco_interface_init_screen(void);
+static gboolean teco_interface_init_interactive(GError **error);
+static void teco_interface_restore_batch(void);
+
+static void teco_interface_init_clipboard(void);
+
+static void teco_interface_resize_all_windows(void);
+
+static void teco_interface_set_window_title(const gchar *title);
+static void teco_interface_draw_info(void);
+static void teco_interface_draw_cmdline(void);
+
void
-InterfaceCurses::init(void)
+teco_interface_init(void)
{
+ for (guint i = 0; i < G_N_ELEMENTS(teco_interface.color_table); i++)
+ teco_interface.color_table[i] = -1;
+ for (guint i = 0; i < G_N_ELEMENTS(teco_interface.orig_color_table); i++)
+ teco_interface.orig_color_table[i].r = -1;
+
+ teco_interface.stdout_orig = teco_interface.stderr_orig = -1;
+
+ teco_curses_info_popup_init(&teco_interface.popup);
+
/*
* We must register this handler to handle
* asynchronous interruptions via CTRL+C
@@ -338,40 +410,45 @@ InterfaceCurses::init(void)
* have won't do.
*/
#if defined(PDCURSES_WIN32) || defined(NCURSES_WIN32)
- SetConsoleCtrlHandler(console_ctrl_handler, TRUE);
+ SetConsoleCtrlHandler(teco_console_ctrl_handler, TRUE);
#endif
/*
* Make sure we have a string for the info line
- * even if info_update() is never called.
+ * even if teco_interface_info_update() is never called.
*/
- info_current = g_strdup(PACKAGE_NAME);
+ teco_string_init(&teco_interface.info_current, PACKAGE_NAME, strlen(PACKAGE_NAME));
/*
* On all platforms except Curses/XTerm, it's
* safe to initialize the clipboards now.
*/
#ifndef CURSES_TTY
- init_clipboard();
+ teco_interface_init_clipboard();
#endif
}
-void
-InterfaceCurses::init_color_safe(guint color, guint32 rgb)
+GOptionGroup *
+teco_interface_get_options(void)
{
- short r, g, b;
+ return NULL;
+}
+static void
+teco_interface_init_color_safe(guint color, guint32 rgb)
+{
#ifdef PDCURSES_WIN32
- if (orig_color_table[color].r < 0) {
+ if (teco_interface.orig_color_table[color].r < 0) {
color_content((short)color,
- &orig_color_table[color].r,
- &orig_color_table[color].g,
- &orig_color_table[color].b);
+ &teco_interface.orig_color_table[color].r,
+ &teco_interface.orig_color_table[color].g,
+ &teco_interface.orig_color_table[color].b);
}
#endif
- rgb2curses(rgb, r, g, b);
- ::init_color((short)color, r, g, b);
+ gshort r, g, b;
+ teco_rgb2curses_triple(rgb, &r, &g, &b);
+ init_color((short)color, r, g, b);
}
#ifdef PDCURSES_WIN32
@@ -381,29 +458,29 @@ InterfaceCurses::init_color_safe(guint color, guint32 rgb)
* the real console color palette - or at least the default
* palette when the console started.
*/
-void
-InterfaceCurses::restore_colors(void)
+static void
+teco_interface_restore_colors(void)
{
if (!can_change_color())
return;
- for (guint i = 0; i < G_N_ELEMENTS(orig_color_table); i++) {
- if (orig_color_table[i].r < 0)
+ for (guint i = 0; i < G_N_ELEMENTS(teco_interface.orig_color_table); i++) {
+ if (teco_interface.orig_color_table[i].r < 0)
continue;
- ::init_color((short)i,
- orig_color_table[i].r,
- orig_color_table[i].g,
- orig_color_table[i].b);
+ init_color((short)i,
+ teco_interface.orig_color_table[i].r,
+ teco_interface.orig_color_table[i].g,
+ teco_interface.orig_color_table[i].b);
}
}
#elif defined(CURSES_TTY)
/*
- * FIXME: On UNIX/ncurses init_color_safe() __may__ change the
- * terminal's palette permanently and there does not appear to be
- * any portable way of restoring the original one.
+ * FIXME: On UNIX/ncurses teco_interface_init_color_safe() __may__
+ * change the terminal's palette permanently and there does not
+ * appear to be any portable way of restoring the original one.
* Curses has color_content(), but there is actually no terminal
* that allows querying the current palette and so color_content()
* will return bogus "default" values and only for the first 8 colors.
@@ -424,23 +501,23 @@ InterfaceCurses::restore_colors(void)
* already properly restored on endwin().
* Welcome in Curses hell.
*/
-void
-InterfaceCurses::restore_colors(void)
+static void
+teco_interface_restore_colors(void)
{
- if (xterm_version() < 0)
+ if (teco_xterm_version() < 0)
return;
/*
* Looks like a real XTerm
*/
- fputs(CTL_KEY_ESC_STR "]104\a", screen_tty);
- fflush(screen_tty);
+ fputs("\e]104\a", teco_interface.screen_tty);
+ fflush(teco_interface.screen_tty);
}
#else /* !PDCURSES_WIN32 && !CURSES_TTY */
-void
-InterfaceCurses::restore_colors(void)
+static void
+teco_interface_restore_colors(void)
{
/*
* No way to restore the palette, or it's
@@ -451,9 +528,9 @@ InterfaceCurses::restore_colors(void)
#endif
void
-InterfaceCurses::init_color(guint color, guint32 rgb)
+teco_interface_init_color(guint color, guint32 rgb)
{
- if (color >= G_N_ELEMENTS(color_table))
+ if (color >= G_N_ELEMENTS(teco_interface.color_table))
return;
#if defined(__PDCURSES__) && !defined(PDC_RGB)
@@ -469,12 +546,12 @@ InterfaceCurses::init_color(guint color, guint32 rgb)
((color & 0x1) << 2) | ((color & 0x4) >> 2);
#endif
- if (cmdline_window) {
+ if (teco_interface.cmdline_window) {
/* interactive mode */
if (!can_change_color())
return;
- init_color_safe(color, rgb);
+ teco_interface_init_color_safe(color, rgb);
} else {
/*
* batch mode: store colors,
@@ -482,21 +559,21 @@ InterfaceCurses::init_color(guint color, guint32 rgb)
* which is called by Scinterm when interactive
* mode is initialized
*/
- color_table[color] = (gint32)rgb;
+ teco_interface.color_table[color] = (gint32)rgb;
}
}
#ifdef CURSES_TTY
-void
-InterfaceCurses::init_screen(void)
+static void
+teco_interface_init_screen(void)
{
- screen_tty = g_fopen("/dev/tty", "r+");
+ teco_interface.screen_tty = g_fopen("/dev/tty", "r+");
/* should never fail */
- g_assert(screen_tty != NULL);
+ g_assert(teco_interface.screen_tty != NULL);
- screen = newterm(NULL, screen_tty, screen_tty);
- if (!screen) {
+ teco_interface.screen = newterm(NULL, teco_interface.screen_tty, teco_interface.screen_tty);
+ if (!teco_interface.screen) {
g_fprintf(stderr, "Error initializing interactive mode. "
"$TERM may be incorrect.\n");
exit(EXIT_FAILURE);
@@ -509,25 +586,23 @@ InterfaceCurses::init_screen(void)
* interrupt terminal interaction.
*/
if (isatty(1)) {
- FILE *stdout_new;
- stdout_orig = dup(1);
- g_assert(stdout_orig >= 0);
- stdout_new = g_freopen("/dev/null", "a+", stdout);
+ teco_interface.stdout_orig = dup(1);
+ g_assert(teco_interface.stdout_orig >= 0);
+ FILE *stdout_new = g_freopen("/dev/null", "a+", stdout);
g_assert(stdout_new != NULL);
}
if (isatty(2)) {
- FILE *stderr_new;
- stderr_orig = dup(2);
- g_assert(stderr_orig >= 0);
- stderr_new = g_freopen("/dev/null", "a+", stderr);
+ teco_interface.stderr_orig = dup(2);
+ g_assert(teco_interface.stderr_orig >= 0);
+ FILE *stderr_new = g_freopen("/dev/null", "a+", stderr);
g_assert(stderr_new != NULL);
}
}
#elif defined(XCURSES)
-void
-InterfaceCurses::init_screen(void)
+static void
+teco_interface_init_screen(void)
{
const char *argv[] = {PACKAGE_NAME, NULL};
@@ -551,16 +626,16 @@ InterfaceCurses::init_screen(void)
#else
-void
-InterfaceCurses::init_screen(void)
+static void
+teco_interface_init_screen(void)
{
initscr();
}
#endif
-void
-InterfaceCurses::init_interactive(void)
+static gboolean
+teco_interface_init_interactive(GError **error)
{
/*
* Curses accesses many environment variables
@@ -569,7 +644,8 @@ InterfaceCurses::init_interactive(void)
* environment before initscr()/newterm().
* This is safe to do here since there are no threads.
*/
- QRegisters::globals.update_environ();
+ if (!teco_qreg_table_set_environ(&teco_qreg_table_globals, error))
+ return FALSE;
/*
* On UNIX terminals, the escape key is usually
@@ -625,41 +701,41 @@ InterfaceCurses::init_interactive(void)
/* for displaying UTF-8 characters properly */
setlocale(LC_CTYPE, "");
- init_screen();
+ teco_interface_init_screen();
cbreak();
noecho();
/* Scintilla draws its own cursor */
curs_set(0);
- info_window = newwin(1, 0, 0, 0);
+ teco_interface.info_window = newwin(1, 0, 0, 0);
- msg_window = newwin(1, 0, LINES - 2, 0);
+ teco_interface.msg_window = newwin(1, 0, LINES - 2, 0);
- cmdline_window = newwin(0, 0, LINES - 1, 0);
- keypad(cmdline_window, TRUE);
+ teco_interface.cmdline_window = newwin(0, 0, LINES - 1, 0);
+ keypad(teco_interface.cmdline_window, TRUE);
#ifdef EMCURSES
- nodelay(cmdline_window, TRUE);
+ nodelay(teco_interface.cmdline_window, TRUE);
#endif
/*
* Will also initialize Scinterm, Curses color pairs
* and resizes the current view.
*/
- if (current_view)
- show_view(current_view);
+ if (teco_interface_current_view)
+ teco_interface_show_view(teco_interface_current_view);
/*
* Only now it's safe to redefine the 16 default colors.
*/
if (can_change_color()) {
- for (guint i = 0; i < G_N_ELEMENTS(color_table); i++) {
+ for (guint i = 0; i < G_N_ELEMENTS(teco_interface.color_table); i++) {
/*
* init_color() may still fail if COLORS < 16
*/
- if (color_table[i] >= 0)
- init_color_safe(i, (guint32)color_table[i]);
+ if (teco_interface.color_table[i] >= 0)
+ teco_interface_init_color_safe(i, (guint32)teco_interface.color_table[i]);
}
}
@@ -670,12 +746,14 @@ InterfaceCurses::init_interactive(void)
* with stdout.
*/
#ifdef CURSES_TTY
- init_clipboard();
+ teco_interface_init_clipboard();
#endif
+
+ return TRUE;
}
-void
-InterfaceCurses::restore_batch(void)
+static void
+teco_interface_restore_batch(void)
{
/*
* Set window title to a reasonable default,
@@ -685,7 +763,7 @@ InterfaceCurses::restore_batch(void)
* is necessary.
*/
#if defined(CURSES_TTY) && defined(HAVE_TIGETSTR)
- set_window_title(g_getenv("TERM") ? : "");
+ teco_interface_set_window_title(g_getenv("TERM") ? : "");
#endif
/*
@@ -693,61 +771,59 @@ InterfaceCurses::restore_batch(void)
* (i.e. return to batch mode)
*/
endwin();
- restore_colors();
+ teco_interface_restore_colors();
/*
* Restore stdout and stderr, so output goes to
* the terminal again in case we "muted" them.
*/
#ifdef CURSES_TTY
- if (stdout_orig >= 0) {
- int fd = dup2(stdout_orig, 1);
+ if (teco_interface.stdout_orig >= 0) {
+ int fd = dup2(teco_interface.stdout_orig, 1);
g_assert(fd == 1);
}
- if (stderr_orig >= 0) {
- int fd = dup2(stderr_orig, 2);
+ if (teco_interface.stderr_orig >= 0) {
+ int fd = dup2(teco_interface.stderr_orig, 2);
g_assert(fd == 2);
}
#endif
/*
- * See vmsg_impl(): It looks at msg_win to determine
+ * See teco_interface_vmsg(): It looks at msg_window to determine
* whether we're in batch mode.
*/
- if (msg_window) {
- delwin(msg_window);
- msg_window = NULL;
+ if (teco_interface.msg_window) {
+ delwin(teco_interface.msg_window);
+ teco_interface.msg_window = NULL;
}
}
-void
-InterfaceCurses::resize_all_windows(void)
+static void
+teco_interface_resize_all_windows(void)
{
int lines, cols; /* screen dimensions */
getmaxyx(stdscr, lines, cols);
- wresize(info_window, 1, cols);
- wresize(current_view->get_window(),
+ wresize(teco_interface.info_window, 1, cols);
+ wresize(teco_view_get_window(teco_interface_current_view),
lines - 3, cols);
- wresize(msg_window, 1, cols);
- mvwin(msg_window, lines - 2, 0);
- wresize(cmdline_window, 1, cols);
- mvwin(cmdline_window, lines - 1, 0);
-
- draw_info();
- msg_clear(); /* FIXME: use saved message */
- popup_clear();
- draw_cmdline();
+ wresize(teco_interface.msg_window, 1, cols);
+ mvwin(teco_interface.msg_window, lines - 2, 0);
+ wresize(teco_interface.cmdline_window, 1, cols);
+ mvwin(teco_interface.cmdline_window, lines - 1, 0);
+
+ teco_interface_draw_info();
+ teco_interface_msg_clear(); /* FIXME: use saved message */
+ teco_interface_popup_clear();
+ teco_interface_draw_cmdline();
}
void
-InterfaceCurses::vmsg_impl(MessageType type, const gchar *fmt, va_list ap)
+teco_interface_vmsg(teco_msg_t type, const gchar *fmt, va_list ap)
{
- short fg, bg;
-
- if (!msg_window) { /* batch mode */
- stdio_vmsg(type, fmt, ap);
+ if (!teco_interface.msg_window) { /* batch mode */
+ teco_interface_stdio_vmsg(type, fmt, ap);
return;
}
@@ -759,67 +835,65 @@ InterfaceCurses::vmsg_impl(MessageType type, const gchar *fmt, va_list ap)
defined(CURSES_TTY) || defined(NCURSES_WIN32)
va_list aq;
va_copy(aq, ap);
- stdio_vmsg(type, fmt, aq);
+ teco_interface_stdio_vmsg(type, fmt, aq);
va_end(aq);
#endif
- fg = rgb2curses(ssm(SCI_STYLEGETBACK, STYLE_DEFAULT));
+ short fg, bg;
+
+ fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0));
switch (type) {
default:
- case MSG_USER:
- bg = rgb2curses(ssm(SCI_STYLEGETFORE, STYLE_DEFAULT));
+ case TECO_MSG_USER:
+ bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0));
break;
- case MSG_INFO:
+ case TECO_MSG_INFO:
bg = COLOR_GREEN;
break;
- case MSG_WARNING:
+ case TECO_MSG_WARNING:
bg = COLOR_YELLOW;
break;
- case MSG_ERROR:
+ case TECO_MSG_ERROR:
bg = COLOR_RED;
beep();
break;
}
- wmove(msg_window, 0, 0);
- wbkgdset(msg_window, ' ' | SCI_COLOR_ATTR(fg, bg));
- vw_printw(msg_window, fmt, ap);
- wclrtoeol(msg_window);
+ wmove(teco_interface.msg_window, 0, 0);
+ wbkgdset(teco_interface.msg_window, ' ' | SCI_COLOR_ATTR(fg, bg));
+ vw_printw(teco_interface.msg_window, fmt, ap);
+ wclrtoeol(teco_interface.msg_window);
}
void
-InterfaceCurses::msg_clear(void)
+teco_interface_msg_clear(void)
{
- short fg, bg;
-
- if (!msg_window) /* batch mode */
+ if (!teco_interface.msg_window) /* batch mode */
return;
- fg = rgb2curses(ssm(SCI_STYLEGETBACK, STYLE_DEFAULT));
- bg = rgb2curses(ssm(SCI_STYLEGETFORE, STYLE_DEFAULT));
+ short fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0));
+ short bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0));
- wbkgdset(msg_window, ' ' | SCI_COLOR_ATTR(fg, bg));
- werase(msg_window);
+ wbkgdset(teco_interface.msg_window, ' ' | SCI_COLOR_ATTR(fg, bg));
+ werase(teco_interface.msg_window);
}
void
-InterfaceCurses::show_view_impl(ViewCurses *view)
+teco_interface_show_view(teco_view_t *view)
{
- int lines, cols; /* screen dimensions */
- WINDOW *current_view_win;
-
- current_view = view;
+ teco_interface_current_view = view;
- if (!cmdline_window) /* batch mode */
+ if (!teco_interface.cmdline_window) /* batch mode */
return;
- current_view_win = current_view->get_window();
+ WINDOW *current_view_win = teco_view_get_window(teco_interface_current_view);
/*
* screen size might have changed since
* this view's WINDOW was last active
*/
+ int lines, cols; /* screen dimensions */
getmaxyx(stdscr, lines, cols);
wresize(current_view_win, lines - 3, cols);
/* Set up window position: never changes */
@@ -828,8 +902,8 @@ InterfaceCurses::show_view_impl(ViewCurses *view)
#if PDCURSES
-void
-InterfaceCurses::set_window_title(const gchar *title)
+static void
+teco_interface_set_window_title(const gchar *title)
{
static gchar *last_title = NULL;
@@ -851,8 +925,8 @@ InterfaceCurses::set_window_title(const gchar *title)
#elif defined(CURSES_TTY) && defined(HAVE_TIGETSTR)
-void
-InterfaceCurses::set_window_title(const gchar *title)
+static void
+teco_interface_set_window_title(const gchar *title)
{
if (!has_status_line || !to_status_line || !from_status_line)
return;
@@ -882,30 +956,26 @@ InterfaceCurses::set_window_title(const gchar *title)
* we do not let curses write to stdout.
* NOTE: This leaves the title set after we quit.
*/
- fputs(to_status_line, screen_tty);
- fputs(title, screen_tty);
- fputs(from_status_line, screen_tty);
- fflush(screen_tty);
+ fputs(to_status_line, teco_interface.screen_tty);
+ fputs(title, teco_interface.screen_tty);
+ fputs(from_status_line, teco_interface.screen_tty);
+ fflush(teco_interface.screen_tty);
}
#else
-void
-InterfaceCurses::set_window_title(const gchar *title)
+static void
+teco_interface_set_window_title(const gchar *title)
{
/* no way to set window title */
}
#endif
-void
-InterfaceCurses::draw_info(void)
+static void
+teco_interface_draw_info(void)
{
- short fg, bg;
- const gchar *info_type_str;
- gchar *info_current_canon, *title;
-
- if (!info_window) /* batch mode */
+ if (!teco_interface.info_window) /* batch mode */
return;
/*
@@ -913,69 +983,75 @@ InterfaceCurses::draw_info(void)
* the current buffer's STYLE_DEFAULT.
* The same style is used for MSG_USER messages.
*/
- fg = rgb2curses(ssm(SCI_STYLEGETBACK, STYLE_DEFAULT));
- bg = rgb2curses(ssm(SCI_STYLEGETFORE, STYLE_DEFAULT));
+ short fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0));
+ short bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0));
+
+ wmove(teco_interface.info_window, 0, 0);
+ wbkgdset(teco_interface.info_window, ' ' | SCI_COLOR_ATTR(fg, bg));
- wmove(info_window, 0, 0);
- wbkgdset(info_window, ' ' | SCI_COLOR_ATTR(fg, bg));
+ const gchar *info_type_str;
- switch (info_type) {
- case INFO_TYPE_QREGISTER:
+ switch (teco_interface.info_type) {
+ case TECO_INFO_TYPE_QREG:
info_type_str = PACKAGE_NAME " - <QRegister> ";
- waddstr(info_window, info_type_str);
+ waddstr(teco_interface.info_window, info_type_str);
/* same formatting as in command lines */
- Curses::format_str(info_window, info_current);
+ teco_curses_format_str(teco_interface.info_window,
+ teco_interface.info_current.data,
+ teco_interface.info_current.len, -1);
break;
- case INFO_TYPE_BUFFER:
+ case TECO_INFO_TYPE_BUFFER:
info_type_str = PACKAGE_NAME " - <Buffer> ";
- waddstr(info_window, info_type_str);
- Curses::format_filename(info_window, info_current);
+ waddstr(teco_interface.info_window, info_type_str);
+ g_assert(!teco_string_contains(&teco_interface.info_current, '\0'));
+ teco_curses_format_filename(teco_interface.info_window,
+ teco_interface.info_current.data, -1);
break;
default:
g_assert_not_reached();
}
- wclrtoeol(info_window);
+ wclrtoeol(teco_interface.info_window);
/*
* Make sure the title will consist only of printable
* characters
*/
- info_current_canon = String::canonicalize_ctl(info_current);
- title = g_strconcat(info_type_str, info_current_canon, NIL);
- g_free(info_current_canon);
- set_window_title(title);
- g_free(title);
+ 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);
+ teco_interface_set_window_title(title);
}
void
-InterfaceCurses::info_update_impl(const QRegister *reg)
+teco_interface_info_update_qreg(const teco_qreg_t *reg)
{
- g_free(info_current);
- /* NOTE: will contain control characters */
- info_type = INFO_TYPE_QREGISTER;
- info_current = g_strdup(reg->name);
- /* NOTE: drawn in event_loop_iter() */
+ 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_type = TECO_INFO_TYPE_QREG;
+ /* NOTE: drawn in teco_interface_event_loop_iter() */
}
void
-InterfaceCurses::info_update_impl(const Buffer *buffer)
+teco_interface_info_update_buffer(const teco_buffer_t *buffer)
{
- g_free(info_current);
- info_type = INFO_TYPE_BUFFER;
- info_current = g_strconcat(buffer->filename ? : UNNAMED_FILE,
- buffer->dirty ? "*" : " ", NIL);
- /* NOTE: drawn in event_loop_iter() */
+ const gchar *filename = buffer->filename ? : UNNAMED_FILE;
+
+ 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_type = TECO_INFO_TYPE_BUFFER;
+ /* NOTE: drawn in teco_interface_event_loop_iter() */
}
void
-InterfaceCurses::cmdline_update_impl(const Cmdline *cmdline)
+teco_interface_cmdline_update(const teco_cmdline_t *cmdline)
{
- short fg, bg;
- int max_cols = 1;
-
/*
* Replace entire pre-formatted command-line.
* We don't know if it is similar to the last one,
@@ -983,18 +1059,22 @@ InterfaceCurses::cmdline_update_impl(const Cmdline *cmdline)
* We approximate the size of the new formatted command-line,
* wasting a few bytes for control characters.
*/
- if (cmdline_pad)
- delwin(cmdline_pad);
- for (guint i = 0; i < cmdline->len+cmdline->rubout_len; i++)
- max_cols += IS_CTL((*cmdline)[i]) ? 3 : 1;
- cmdline_pad = newpad(1, max_cols);
+ if (teco_interface.cmdline_pad)
+ delwin(teco_interface.cmdline_pad);
- fg = rgb2curses(ssm(SCI_STYLEGETFORE, STYLE_DEFAULT));
- bg = rgb2curses(ssm(SCI_STYLEGETBACK, STYLE_DEFAULT));
- wcolor_set(cmdline_pad, SCI_COLOR_PAIR(fg, bg), NULL);
+ int max_cols = 1;
+ for (guint i = 0; i < cmdline->str.len; i++)
+ max_cols += TECO_IS_CTL(cmdline->str.data[i]) ? 3 : 1;
+ teco_interface.cmdline_pad = newpad(1, max_cols);
+
+ short fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0));
+ short bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0));
+ wcolor_set(teco_interface.cmdline_pad, SCI_COLOR_PAIR(fg, bg), NULL);
/* format effective command line */
- cmdline_len = Curses::format_str(cmdline_pad, cmdline->str, cmdline->len);
+ teco_interface.cmdline_len =
+ teco_curses_format_str(teco_interface.cmdline_pad,
+ cmdline->str.data, cmdline->effective_len, -1);
/*
* A_BOLD should result in either a bold font or a brighter
@@ -1006,61 +1086,61 @@ InterfaceCurses::cmdline_update_impl(const Cmdline *cmdline)
* for rubbed out parts of the command line which will
* be user-configurable.
*/
- wattron(cmdline_pad, A_UNDERLINE | A_BOLD);
+ wattron(teco_interface.cmdline_pad, A_UNDERLINE | A_BOLD);
/*
* Format rubbed-out command line.
* NOTE: This formatting will never be truncated since we're
* writing into the pad which is large enough.
*/
- cmdline_rubout_len = Curses::format_str(cmdline_pad, cmdline->str + cmdline->len,
- cmdline->rubout_len);
+ teco_interface.cmdline_rubout_len =
+ teco_curses_format_str(teco_interface.cmdline_pad, cmdline->str.data + cmdline->effective_len,
+ cmdline->str.len - cmdline->effective_len, -1);
/* highlight cursor after effective command line */
- if (cmdline_rubout_len) {
- attr_t attr;
- short pair;
+ if (teco_interface.cmdline_rubout_len) {
+ attr_t attr = 0;
+ short pair = 0;
- wmove(cmdline_pad, 0, cmdline_len);
- wattr_get(cmdline_pad, &attr, &pair, NULL);
- wchgat(cmdline_pad, 1,
+ wmove(teco_interface.cmdline_pad, 0, teco_interface.cmdline_len);
+ wattr_get(teco_interface.cmdline_pad, &attr, &pair, NULL);
+ wchgat(teco_interface.cmdline_pad, 1,
(attr & A_UNDERLINE) | A_REVERSE, pair, NULL);
} else {
- cmdline_len++;
- wattroff(cmdline_pad, A_UNDERLINE | A_BOLD);
- waddch(cmdline_pad, ' ' | A_REVERSE);
+ teco_interface.cmdline_len++;
+ wattroff(teco_interface.cmdline_pad, A_UNDERLINE | A_BOLD);
+ waddch(teco_interface.cmdline_pad, ' ' | A_REVERSE);
}
- draw_cmdline();
+ teco_interface_draw_cmdline();
}
-void
-InterfaceCurses::draw_cmdline(void)
+static void
+teco_interface_draw_cmdline(void)
{
- short fg, bg;
/* total width available for command line */
- guint total_width = getmaxx(cmdline_window) - 1;
- /* beginning of command line to show */
- guint disp_offset;
- /* length of command line to show */
- guint disp_len;
+ guint total_width = getmaxx(teco_interface.cmdline_window) - 1;
- disp_offset = cmdline_len -
- MIN(cmdline_len,
- total_width/2 + cmdline_len % MAX(total_width/2, 1));
+ /* beginning of command line to show */
+ guint disp_offset = teco_interface.cmdline_len -
+ MIN(teco_interface.cmdline_len,
+ total_width/2 + teco_interface.cmdline_len % MAX(total_width/2, 1));
/*
+ * length of command line to show
+ *
* NOTE: we do not use getmaxx(cmdline_pad) here since it may be
* larger than the text the pad contains.
*/
- disp_len = MIN(total_width, cmdline_len+cmdline_rubout_len - disp_offset);
+ guint disp_len = MIN(total_width, teco_interface.cmdline_len +
+ teco_interface.cmdline_rubout_len - disp_offset);
- fg = rgb2curses(ssm(SCI_STYLEGETFORE, STYLE_DEFAULT));
- bg = rgb2curses(ssm(SCI_STYLEGETBACK, STYLE_DEFAULT));
+ short fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0));
+ short bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0));
- wbkgdset(cmdline_window, ' ' | SCI_COLOR_ATTR(fg, bg));
- werase(cmdline_window);
- mvwaddch(cmdline_window, 0, 0, '*' | A_BOLD);
- copywin(cmdline_pad, cmdline_window,
+ wbkgdset(teco_interface.cmdline_window, ' ' | SCI_COLOR_ATTR(fg, bg));
+ werase(teco_interface.cmdline_window);
+ mvwaddch(teco_interface.cmdline_window, 0, 0, '*' | A_BOLD);
+ copywin(teco_interface.cmdline_pad, teco_interface.cmdline_window,
0, disp_offset, 0, 1, 0, disp_len, FALSE);
}
@@ -1073,12 +1153,11 @@ InterfaceCurses::draw_cmdline(void)
* it corresponds to the X11 PRIMARY, SECONDARY or
* CLIPBOARD selections.
*/
-void
-InterfaceCurses::init_clipboard(void)
+static void
+teco_interface_init_clipboard(void)
{
char *contents;
long length;
- int rc;
/*
* Even on PDCurses, while the clipboard functions are
@@ -1089,71 +1168,68 @@ InterfaceCurses::init_clipboard(void)
* This could be done at compile time, but this way is more
* generic (albeit inefficient).
*/
- rc = PDC_getclipboard(&contents, &length);
+ int rc = PDC_getclipboard(&contents, &length);
if (rc == PDC_CLIP_ACCESS_ERROR)
return;
if (rc == PDC_CLIP_SUCCESS)
PDC_freeclipboard(contents);
- QRegisters::globals.insert(new QRegisterClipboard());
+ teco_qreg_table_insert(&teco_qreg_table_globals, teco_qreg_clipboard_new(""));
}
-void
-InterfaceCurses::set_clipboard(const gchar *name, const gchar *str, gssize str_len)
+gboolean
+teco_interface_set_clipboard(const gchar *name, const gchar *str, gsize str_len, GError **error)
{
- int rc;
-
- if (str) {
- if (str_len < 0)
- str_len = strlen(str);
-
- rc = PDC_setclipboard(str, str_len);
- } else {
- rc = PDC_clearclipboard();
+ int rc = str ? PDC_setclipboard(str, str_len) : PDC_clearclipboard();
+ if (rc != PDC_CLIP_SUCCESS) {
+ g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED,
+ "Error %d copying to clipboard", rc);
+ return FALSE;
}
- if (rc != PDC_CLIP_SUCCESS)
- throw Error("Error %d copying to clipboard", rc);
+ return TRUE;
}
-gchar *
-InterfaceCurses::get_clipboard(const gchar *name, gsize *str_len)
+gboolean
+teco_interface_get_clipboard(const gchar *name, gchar **str, gsize *len, GError **error)
{
char *contents;
long length = 0;
- int rc;
- gchar *str;
/*
* NOTE: It is undefined whether we can pass in NULL for length.
*/
- rc = PDC_getclipboard(&contents, &length);
- if (str_len)
- *str_len = length;
+ int rc = PDC_getclipboard(&contents, &length);
+ *len = length;
if (rc == PDC_CLIP_EMPTY)
- return NULL;
- if (rc != PDC_CLIP_SUCCESS)
- throw Error("Error %d retrieving clipboard", rc);
+ return TRUE;
+ if (rc != PDC_CLIP_SUCCESS) {
+ g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED,
+ "Error %d retrieving clipboard", rc);
+ return FALSE;
+ }
/*
* PDCurses defines its own free function and there is no
* way to find out which allocator was used.
* We must therefore copy the memory to be on the safe side.
- * At least we can null-terminate the return string in the
- * process (PDCurses does not guarantee that either).
+ * At least, the result is guaranteed to be null-terminated
+ * and thus teco_string_t-compatible
+ * (PDCurses does not guarantee that either).
*/
- str = (gchar *)g_malloc(length + 1);
- memcpy(str, contents, length);
- str[length] = '\0';
+ if (str) {
+ *str = memcpy(g_malloc(length + 1), contents, length);
+ (*str)[length] = '\0';
+ }
PDC_freeclipboard(contents);
- return str;
+ return TRUE;
}
#elif defined(CURSES_TTY)
-void
-InterfaceCurses::init_clipboard(void)
+static void
+teco_interface_init_clipboard(void)
{
/*
* At least on XTerm, there are escape sequences
@@ -1167,13 +1243,13 @@ InterfaceCurses::init_clipboard(void)
* not register the clipboard registers if they aren't.
* Therefore, a special XTerm clipboard ED flag an be set by the user.
*/
- if (!(Flags::ed & Flags::ED_XTERM_CLIPBOARD) || xterm_version() < 203)
+ if (!(teco_ed & TECO_ED_XTERM_CLIPBOARD) || teco_xterm_version() < 203)
return;
- QRegisters::globals.insert(new QRegisterClipboard());
- QRegisters::globals.insert(new QRegisterClipboard("P"));
- QRegisters::globals.insert(new QRegisterClipboard("S"));
- QRegisters::globals.insert(new QRegisterClipboard("C"));
+ teco_qreg_table_insert(&teco_qreg_table_globals, teco_qreg_clipboard_new(""));
+ teco_qreg_table_insert(&teco_qreg_table_globals, teco_qreg_clipboard_new("P"));
+ teco_qreg_table_insert(&teco_qreg_table_globals, teco_qreg_clipboard_new("S"));
+ teco_qreg_table_insert(&teco_qreg_table_globals, teco_qreg_clipboard_new("C"));
}
static inline gchar
@@ -1189,27 +1265,21 @@ get_selection_by_name(const gchar *name)
return g_ascii_tolower(*name) ? : 'c';
}
-void
-InterfaceCurses::set_clipboard(const gchar *name, const gchar *str, gssize str_len)
+gboolean
+teco_interface_set_clipboard(const gchar *name, const gchar *str, gsize str_len,
+ GError **error)
{
+ fputs("\e]52;", teco_interface.screen_tty);
+ fputc(get_selection_by_name(name), teco_interface.screen_tty);
+ fputc(';', teco_interface.screen_tty);
+
/*
* Enough space for 1024 Base64-encoded bytes.
*/
gchar buffer[(1024 / 3) * 4 + 4];
gsize out_len;
-
/* g_base64_encode_step() state: */
- gint state = 0;
- gint save = 0;
-
- fputs(CTL_KEY_ESC_STR "]52;", screen_tty);
- fputc(get_selection_by_name(name), screen_tty);
- fputc(';', screen_tty);
-
- if (!str)
- str_len = 0;
- else if (str_len < 0)
- str_len = strlen(str);
+ gint state = 0, save = 0;
while (str_len > 0) {
gsize step_len = MIN(1024, str_len);
@@ -1221,41 +1291,32 @@ InterfaceCurses::set_clipboard(const gchar *name, const gchar *str, gssize str_l
out_len = g_base64_encode_step((const guchar *)str,
step_len, FALSE,
buffer, &state, &save);
- fwrite(buffer, 1, out_len, screen_tty);
+ fwrite(buffer, 1, out_len, teco_interface.screen_tty);
str_len -= step_len;
str += step_len;
}
out_len = g_base64_encode_close(FALSE, buffer, &state, &save);
- fwrite(buffer, 1, out_len, screen_tty);
+ fwrite(buffer, 1, out_len, teco_interface.screen_tty);
- fputc('\a', screen_tty);
- fflush(screen_tty);
+ fputc('\a', teco_interface.screen_tty);
+ fflush(teco_interface.screen_tty);
+
+ return TRUE;
}
-gchar *
-InterfaceCurses::get_clipboard(const gchar *name, gsize *str_len)
+gboolean
+teco_interface_get_clipboard(const gchar *name, gchar **str, gsize *len, GError **error)
{
/*
- * Space for storing one group of decoded Base64 characters
- * and the OSC-52 response.
- */
- gchar buffer[MAX(3, 7)];
- GString *str_base64;
-
- /* g_base64_decode_step() state: */
- gint state = 0;
- guint save = 0;
-
- /*
* Query the clipboard -- XTerm will reply with the
* OSC-52 command that would set the current selection.
*/
- fputs(CTL_KEY_ESC_STR "]52;", screen_tty);
- fputc(get_selection_by_name(name), screen_tty);
- fputs(";?\a", screen_tty);
- fflush(screen_tty);
+ fputs("\e]52;", teco_interface.screen_tty);
+ fputc(get_selection_by_name(name), teco_interface.screen_tty);
+ fputs(";?\a", teco_interface.screen_tty);
+ fflush(teco_interface.screen_tty);
/*
* It is very well possible that the XTerm clipboard
@@ -1277,22 +1338,32 @@ InterfaceCurses::get_clipboard(const gchar *name, gsize *str_len)
if (getch() == ERR) {
/* timeout */
cbreak();
- throw Error("Timed out reading XTerm clipboard");
+ g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED,
+ "Timed out reading XTerm clipboard");
+ return FALSE;
}
}
- str_base64 = g_string_new("");
+ GString *str_base64 = g_string_new("");
+ /* g_base64_decode_step() state: */
+ gint state = 0;
+ guint save = 0;
for (;;) {
- gchar c;
- gsize out_len;
+ /*
+ * Space for storing one group of decoded Base64 characters
+ * and the OSC-52 response.
+ */
+ gchar buffer[MAX(3, 7)];
- c = (gchar)getch();
+ gchar c = (gchar)getch();
if (c == ERR) {
/* timeout */
cbreak();
g_string_free(str_base64, TRUE);
- throw Error("Timed out reading XTerm clipboard");
+ g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED,
+ "Timed out reading XTerm clipboard");
+ return FALSE;
}
if (c == '\a')
break;
@@ -1301,30 +1372,29 @@ InterfaceCurses::get_clipboard(const gchar *name, gsize *str_len)
* This could be simplified using sscanf() and
* g_base64_decode(), but we avoid one allocation
* to get the entire Base64 string.
- * (Also to allow for timeouts, we must should
+ * (Also to allow for timeouts, we should
* read character-wise using getch() anyway.)
*/
- out_len = g_base64_decode_step(&c, sizeof(c),
- (guchar *)buffer,
- &state, &save);
+ gsize out_len = g_base64_decode_step(&c, sizeof(c),
+ (guchar *)buffer,
+ &state, &save);
g_string_append_len(str_base64, buffer, out_len);
}
cbreak();
- if (str_len)
- *str_len = str_base64->len;
+ if (str)
+ *str = str_base64->str;
+ *len = str_base64->len;
- /*
- * If the clipboard answer is empty, return NULL.
- */
- return g_string_free(str_base64, str_base64->len == 0);
+ g_string_free(str_base64, !str);
+ return TRUE;
}
#else
-void
-InterfaceCurses::init_clipboard(void)
+static void
+teco_interface_init_clipboard(void)
{
/*
* No native clipboard support, so no clipboard Q-Regs are
@@ -1332,37 +1402,55 @@ InterfaceCurses::init_clipboard(void)
*/
}
-void
-InterfaceCurses::set_clipboard(const gchar *name, const gchar *str, gssize str_len)
+gboolean
+teco_interface_set_clipboard(const gchar *name, const gchar *str, gsize str_len,
+ GError **error)
{
- throw Error("Setting clipboard unsupported");
+ g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED,
+ "Setting clipboard unsupported");
+ return FALSE;
}
-gchar *
-InterfaceCurses::get_clipboard(const gchar *name, gsize *str_len)
+gboolean
+teco_interface_get_clipboard(const gchar *name, gchar **str, gsize *len, GError **error)
{
- throw Error("Getting clipboard unsupported");
+ g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED,
+ "Getting clipboard unsupported");
+ return FALSE;
}
#endif /* !__PDCURSES__ && !CURSES_TTY */
void
-InterfaceCurses::popup_show_impl(void)
+teco_interface_popup_add(teco_popup_entry_type_t type, const gchar *name, gsize name_len,
+ gboolean highlight)
{
- short fg, bg;
+ if (teco_interface.cmdline_window)
+ /* interactive mode */
+ teco_curses_info_popup_add(&teco_interface.popup, type, name, name_len, highlight);
+}
- if (!cmdline_window)
+void
+teco_interface_popup_show(void)
+{
+ if (!teco_interface.cmdline_window)
/* batch mode */
return;
- fg = rgb2curses(ssm(SCI_STYLEGETFORE, STYLE_CALLTIP));
- bg = rgb2curses(ssm(SCI_STYLEGETBACK, STYLE_CALLTIP));
+ short fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_CALLTIP, 0));
+ short bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_CALLTIP, 0));
- popup.show(SCI_COLOR_ATTR(fg, bg));
+ teco_curses_info_popup_show(&teco_interface.popup, SCI_COLOR_ATTR(fg, bg));
+}
+
+gboolean
+teco_interface_popup_is_shown(void)
+{
+ return teco_curses_info_popup_is_shown(&teco_interface.popup);
}
void
-InterfaceCurses::popup_clear_impl(void)
+teco_interface_popup_clear(void)
{
#ifdef __PDCURSES__
/*
@@ -1374,31 +1462,35 @@ InterfaceCurses::popup_clear_impl(void)
* Actually we would expect this to be necessary on any curses,
* but ncurses doesn't require this.
*/
- if (popup.is_shown()) {
- touchwin(info_window);
- touchwin(msg_window);
+ if (teco_curses_info_popup_is_shown(&teco_interface.popup)) {
+ touchwin(teco_interface.info_window);
+ touchwin(teco_interface.msg_window);
}
#endif
- popup.clear();
+ teco_curses_info_popup_clear(&teco_interface.popup);
+ teco_curses_info_popup_init(&teco_interface.popup);
+}
+
+gboolean
+teco_interface_is_interrupted(void)
+{
+ return teco_sigint_occurred != FALSE;
}
/**
* One iteration of the event loop.
*
- * This is a global function, so it may
- * be used as an Emscripten callback.
+ * This is a global function, so it may be used as an asynchronous Emscripten callback.
+ * While this function cannot directly throw GErrors,
+ * it can set teco_interface.event_loop_error.
*
- * @bug
- * Can probably be defined as a static method,
- * so we can avoid declaring it a fried function of
- * InterfaceCurses.
+ * @fixme Thrown errors should be somehow caught when building for EMScripten as well.
+ * Perhaps in a goto-block.
*/
void
-event_loop_iter()
+teco_interface_event_loop_iter(void)
{
- int key;
-
/*
* On PDCurses/win32, raw() and cbreak() does
* not disable and enable CTRL+C handling properly.
@@ -1426,7 +1518,7 @@ event_loop_iter()
* escape sequences.
*/
#ifdef NCURSES_UNIX
- keypad(interface.cmdline_window, Flags::ed & Flags::ED_FNKEYS);
+ keypad(teco_interface.cmdline_window, teco_ed & TECO_ED_FNKEYS);
#endif
/* no special <CTRL/C> handling */
@@ -1434,9 +1526,15 @@ event_loop_iter()
#ifdef PDCURSES_WIN32
SetConsoleMode(console_hnd, console_mode & ~ENABLE_PROCESSED_INPUT);
#endif
- key = wgetch(interface.cmdline_window);
+ /*
+ * Memory limiting is stopped temporarily, since it might otherwise
+ * constantly place 100% load on the CPU.
+ */
+ teco_memory_stop_limiting();
+ int key = wgetch(teco_interface.cmdline_window);
+ teco_memory_start_limiting();
/* allow asynchronous interruptions on <CTRL/C> */
- sigint_occurred = FALSE;
+ teco_sigint_occurred = FALSE;
noraw(); /* FIXME: necessary because of NCURSES_WIN32 bug */
cbreak();
#ifdef PDCURSES_WIN32
@@ -1451,10 +1549,10 @@ event_loop_iter()
#if PDCURSES
resize_term(0, 0);
#endif
- interface.resize_all_windows();
+ teco_interface_resize_all_windows();
break;
#endif
- case CTL_KEY('H'):
+ case TECO_CTL_KEY('H'):
case 0x7F: /* ^? */
case KEY_BACKSPACE:
/*
@@ -1465,18 +1563,25 @@ event_loop_iter()
* backspace.
* In SciTECO backspace is normalized to ^H.
*/
- cmdline.keypress(CTL_KEY('H'));
+ if (!teco_cmdline_keypress_c(TECO_CTL_KEY('H'),
+ &teco_interface.event_loop_error))
+ return;
break;
case KEY_ENTER:
case '\r':
case '\n':
- cmdline.keypress('\n');
+ if (!teco_cmdline_keypress_c('\n', &teco_interface.event_loop_error))
+ return;
break;
/*
* Function key macros
*/
-#define FN(KEY) case KEY_##KEY: cmdline.fnmacro(#KEY); break
+#define FN(KEY) \
+ case KEY_##KEY: \
+ if (!teco_cmdline_fnmacro(#KEY, &teco_interface.event_loop_error)) \
+ return; \
+ break
#define FNS(KEY) FN(KEY); FN(S##KEY)
FN(DOWN); FN(UP); FNS(LEFT); FNS(RIGHT);
FNS(HOME);
@@ -1485,7 +1590,9 @@ event_loop_iter()
g_snprintf(macro_name, sizeof(macro_name),
"F%d", key - KEY_F0);
- cmdline.fnmacro(macro_name);
+ if (!teco_cmdline_fnmacro(macro_name,
+ &teco_interface.event_loop_error))
+ return;
break;
}
FNS(DC);
@@ -1503,8 +1610,9 @@ event_loop_iter()
* Control keys and keys with printable representation
*/
default:
- if (key <= 0xFF)
- cmdline.keypress((gchar)key);
+ if (key <= 0xFF &&
+ !teco_cmdline_keypress_c(key, &teco_interface.event_loop_error))
+ return;
}
/*
@@ -1513,37 +1621,38 @@ event_loop_iter()
* so we redraw it here, where the overhead does
* not matter much.
*/
- interface.draw_info();
- wnoutrefresh(interface.info_window);
- interface.current_view->noutrefresh();
- wnoutrefresh(interface.msg_window);
- wnoutrefresh(interface.cmdline_window);
- interface.popup.noutrefresh();
+ teco_interface_draw_info();
+ wnoutrefresh(teco_interface.info_window);
+ teco_view_noutrefresh(teco_interface_current_view);
+ wnoutrefresh(teco_interface.msg_window);
+ wnoutrefresh(teco_interface.cmdline_window);
+ teco_curses_info_popup_noutrefresh(&teco_interface.popup);
doupdate();
}
-void
-InterfaceCurses::event_loop_impl(void)
+gboolean
+teco_interface_event_loop(GError **error)
{
- static const Cmdline empty_cmdline;
+ static const teco_cmdline_t empty_cmdline; // FIXME
/*
* Initialize Curses for interactive mode
*/
- init_interactive();
+ if (!teco_interface_init_interactive(error))
+ return FALSE;
/* initial refresh */
- draw_info();
- wnoutrefresh(info_window);
- current_view->noutrefresh();
- msg_clear();
- wnoutrefresh(msg_window);
- cmdline_update(&empty_cmdline);
- wnoutrefresh(cmdline_window);
+ teco_interface_draw_info();
+ wnoutrefresh(teco_interface.info_window);
+ teco_view_noutrefresh(teco_interface_current_view);
+ teco_interface_msg_clear();
+ wnoutrefresh(teco_interface.msg_window);
+ teco_interface_cmdline_update(&empty_cmdline);
+ wnoutrefresh(teco_interface.cmdline_window);
doupdate();
#ifdef EMCURSES
- PDC_emscripten_set_handler(event_loop_iter, TRUE);
+ PDC_emscripten_set_handler(teco_interface_event_loop_iter, TRUE);
/*
* We must not block emscripten's main loop,
* instead event_loop_iter() is called asynchronously.
@@ -1556,28 +1665,41 @@ InterfaceCurses::event_loop_impl(void)
*/
emscripten_exit_with_live_runtime();
#else
- try {
- for (;;)
- event_loop_iter();
- } catch (Quit) {
- /* SciTECO termination (e.g. EX$$) */
+ while (!teco_interface.event_loop_error)
+ teco_interface_event_loop_iter();
+
+ /*
+ * The error needs to be propagated only if this is
+ * NOT a SciTECO termination (e.g. EX$$)
+ */
+ if (!g_error_matches(teco_interface.event_loop_error,
+ TECO_ERROR, TECO_ERROR_QUIT)) {
+ g_propagate_error(error, g_steal_pointer(&teco_interface.event_loop_error));
+ return FALSE;
}
+ g_clear_error(&teco_interface.event_loop_error);
- restore_batch();
+ teco_interface_restore_batch();
#endif
+
+ return TRUE;
}
-InterfaceCurses::~InterfaceCurses()
+void
+teco_interface_cleanup(void)
{
- if (info_window)
- delwin(info_window);
- g_free(info_current);
- if (cmdline_window)
- delwin(cmdline_window);
- if (cmdline_pad)
- delwin(cmdline_pad);
- if (msg_window)
- delwin(msg_window);
+ if (teco_interface.event_loop_error)
+ g_error_free(teco_interface.event_loop_error);
+
+ if (teco_interface.info_window)
+ delwin(teco_interface.info_window);
+ teco_string_clear(&teco_interface.info_current);
+ if (teco_interface.cmdline_window)
+ delwin(teco_interface.cmdline_window);
+ if (teco_interface.cmdline_pad)
+ delwin(teco_interface.cmdline_pad);
+ if (teco_interface.msg_window)
+ delwin(teco_interface.msg_window);
/*
* PDCurses (win32) crashes if initscr() wasn't called.
@@ -1586,28 +1708,16 @@ InterfaceCurses::~InterfaceCurses()
* instead.
*/
#ifndef XCURSES
- if (info_window && !isendwin())
+ if (teco_interface.info_window && !isendwin())
endwin();
#endif
- if (screen)
- delscreen(screen);
- if (screen_tty)
- fclose(screen_tty);
- if (stderr_orig >= 0)
- close(stderr_orig);
- if (stdout_orig >= 0)
- close(stdout_orig);
-}
-
-/*
- * Callbacks
- */
-
-static void
-scintilla_notify(Scintilla *sci, int idFrom, void *notify, void *user_data)
-{
- interface.process_notify((SCNotification *)notify);
+ if (teco_interface.screen)
+ delscreen(teco_interface.screen);
+ if (teco_interface.screen_tty)
+ fclose(teco_interface.screen_tty);
+ if (teco_interface.stderr_orig >= 0)
+ close(teco_interface.stderr_orig);
+ if (teco_interface.stdout_orig >= 0)
+ close(teco_interface.stdout_orig);
}
-
-} /* namespace SciTECO */