aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/interface-curses.cpp
diff options
context:
space:
mode:
authorRobin Haberkorn <robin.haberkorn@googlemail.com>2014-11-17 04:41:18 +0100
committerRobin Haberkorn <robin.haberkorn@googlemail.com>2014-11-17 04:41:18 +0100
commitdceabff6cfc6bd6572a98b5c8f7775b42dc732a7 (patch)
treeef8f6ec6c5ea03af65b656df0f47a20e26cabba1 /src/interface-curses.cpp
parent2542eb02648294256a01ae4ecb6ac81bc8ab5094 (diff)
downloadsciteco-dceabff6cfc6bd6572a98b5c8f7775b42dc732a7.tar.gz
renamed the "NCurses" UI to "Curses" internally
* does not change ./configure parameters You still have to specifiy --with-interface=ncurses for the Curses interface with default settings * the "NCurses" UI was used for many different Curses variants, so plain "Curses" is a better name.
Diffstat (limited to 'src/interface-curses.cpp')
-rw-r--r--src/interface-curses.cpp525
1 files changed, 525 insertions, 0 deletions
diff --git a/src/interface-curses.cpp b/src/interface-curses.cpp
new file mode 100644
index 0000000..ad7296f
--- /dev/null
+++ b/src/interface-curses.cpp
@@ -0,0 +1,525 @@
+/*
+ * Copyright (C) 2012-2014 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 <stdio.h>
+#include <stdarg.h>
+#include <locale.h>
+
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <glib/gstdio.h>
+
+#include <curses.h>
+
+#include <Scintilla.h>
+#include <ScintillaTerm.h>
+
+#ifdef EMSCRIPTEN
+#include <emscripten.h>
+#endif
+
+#include "sciteco.h"
+#include "cmdline.h"
+#include "qregisters.h"
+#include "ring.h"
+#include "interface.h"
+#include "interface-curses.h"
+
+namespace SciTECO {
+
+extern "C" {
+static void scintilla_notify(Scintilla *sci, int idFrom,
+ void *notify, void *user_data);
+}
+
+#define UNNAMED_FILE "(Unnamed)"
+
+#define SCI_COLOR_ATTR(f, b) \
+ ((chtype)COLOR_PAIR(SCI_COLOR_PAIR(f, b)))
+
+void
+ViewCurses::initialize_impl(void)
+{
+ WINDOW *window;
+
+ /* NOTE: Scintilla initializes color pairs */
+ sci = scintilla_new(scintilla_notify);
+ window = get_window();
+
+ /*
+ * Window must have dimension before it can be
+ * positioned.
+ * Perhaps it's better to leave the window
+ * unitialized and set the position in
+ * InterfaceCurses::show_view().
+ */
+ wresize(window, 1, 1);
+ /* Set up window position: never changes */
+ mvwin(window, 1, 0);
+
+ setup();
+}
+
+void
+InterfaceCurses::main_impl(int &argc, char **&argv)
+{
+ init_screen();
+ cbreak();
+ noecho();
+ curs_set(0); /* Scintilla draws its own cursor */
+
+ setlocale(LC_CTYPE, ""); /* for displaying UTF-8 characters properly */
+
+ info_window = newwin(1, 0, 0, 0);
+ info_current = g_strdup(PACKAGE_NAME);
+
+ msg_window = newwin(1, 0, LINES - 2, 0);
+
+ cmdline_window = newwin(0, 0, LINES - 1, 0);
+ cmdline_current = NULL;
+
+ draw_info();
+ /* scintilla will be refreshed in event loop */
+ msg_clear();
+ cmdline_update("");
+
+#ifdef EMSCRIPTEN
+ nodelay(cmdline_window, TRUE);
+#else
+#ifndef PDCURSES_WIN32A
+ /* workaround: endwin() is somewhat broken in the win32a port */
+ endwin();
+#endif
+#endif
+}
+
+#ifdef __PDCURSES__
+
+void
+InterfaceCurses::init_screen(void)
+{
+#ifdef PDCURSES_WIN32A
+ /* enables window resizing on Win32a port */
+ PDC_set_resize_limits(25, 0xFFFF, 80, 0xFFFF);
+#endif
+
+ initscr();
+
+ screen_tty = NULL;
+ screen = NULL;
+}
+
+#else
+
+void
+InterfaceCurses::init_screen(void)
+{
+ /*
+ * Prevent the initial redraw and any escape sequences that may
+ * interfere with stdout, so we may use the terminal in
+ * cooked mode, for commandline help and batch processing.
+ * Scintilla must be initialized for batch processing to work.
+ * (Frankly I have no idea why this works!)
+ */
+ screen_tty = g_fopen("/dev/tty", "r+b");
+ screen = newterm(NULL, screen_tty, screen_tty);
+ set_term(screen);
+}
+
+#endif /* !__PDCURSES__ */
+
+void
+InterfaceCurses::resize_all_windows(void)
+{
+ int lines, cols; /* screen dimensions */
+
+ getmaxyx(stdscr, lines, cols);
+
+ wresize(info_window, 1, cols);
+ wresize(current_view->get_window(),
+ 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();
+ /* scintilla will be refreshed in event loop */
+ msg_clear(); /* FIXME: use saved message */
+ cmdline_update();
+}
+
+void
+InterfaceCurses::vmsg_impl(MessageType type, const gchar *fmt, va_list ap)
+{
+ static const chtype type2attr[] = {
+ SCI_COLOR_ATTR(COLOR_BLACK, COLOR_WHITE), /* MSG_USER */
+ SCI_COLOR_ATTR(COLOR_BLACK, COLOR_GREEN), /* MSG_INFO */
+ SCI_COLOR_ATTR(COLOR_BLACK, COLOR_YELLOW), /* MSG_WARNING */
+ SCI_COLOR_ATTR(COLOR_BLACK, COLOR_RED) /* MSG_ERROR */
+ };
+
+#ifdef PDCURSES_WIN32A
+ stdio_vmsg(type, fmt, ap);
+ if (isendwin()) /* batch mode */
+ return;
+#else
+ if (isendwin()) { /* batch mode */
+ stdio_vmsg(type, fmt, ap);
+ return;
+ }
+#endif
+
+ wmove(msg_window, 0, 0);
+ wbkgdset(msg_window, ' ' | type2attr[type]);
+ vw_printw(msg_window, fmt, ap);
+ wclrtoeol(msg_window);
+
+ wrefresh(msg_window);
+}
+
+void
+InterfaceCurses::msg_clear(void)
+{
+ if (isendwin()) /* batch mode */
+ return;
+
+ wmove(msg_window, 0, 0);
+ wbkgdset(msg_window, ' ' | SCI_COLOR_ATTR(COLOR_BLACK, COLOR_WHITE));
+ wclrtoeol(msg_window);
+
+ wrefresh(msg_window);
+}
+
+void
+InterfaceCurses::show_view_impl(ViewCurses *view)
+{
+ int lines, cols; /* screen dimensions */
+
+ current_view = view;
+
+ /*
+ * screen size might have changed since
+ * this view's WINDOW was last active
+ */
+ getmaxyx(stdscr, lines, cols);
+ wresize(current_view->get_window(),
+ lines - 3, cols);
+}
+
+void
+InterfaceCurses::draw_info(void)
+{
+ if (isendwin()) /* batch mode */
+ return;
+
+ wmove(info_window, 0, 0);
+ wbkgdset(info_window, ' ' | SCI_COLOR_ATTR(COLOR_BLACK, COLOR_WHITE));
+ waddstr(info_window, info_current);
+ wclrtoeol(info_window);
+
+ wrefresh(info_window);
+}
+
+void
+InterfaceCurses::info_update_impl(QRegister *reg)
+{
+ g_free(info_current);
+ info_current = g_strdup_printf("%s - <QRegister> %s", PACKAGE_NAME,
+ reg->name);
+
+ draw_info();
+}
+
+void
+InterfaceCurses::info_update_impl(Buffer *buffer)
+{
+ g_free(info_current);
+ info_current = g_strdup_printf("%s - <Buffer> %s%s", PACKAGE_NAME,
+ buffer->filename ? : UNNAMED_FILE,
+ buffer->dirty ? "*" : "");
+
+ draw_info();
+}
+
+void
+InterfaceCurses::cmdline_update_impl(const gchar *cmdline)
+{
+ size_t len;
+ int half_line = (getmaxx(stdscr) - 2) / 2;
+ const gchar *line;
+
+ if (cmdline) {
+ g_free(cmdline_current);
+ cmdline_current = g_strdup(cmdline);
+ } else {
+ cmdline = cmdline_current;
+ }
+ len = strlen(cmdline);
+
+ /* FIXME: optimize */
+ line = cmdline + len - MIN(len, half_line + len % half_line);
+
+ mvwaddch(cmdline_window, 0, 0, '*');
+ waddstr(cmdline_window, line);
+ waddch(cmdline_window, ' ' | A_REVERSE);
+ wclrtoeol(cmdline_window);
+
+ wrefresh(cmdline_window);
+}
+
+void
+InterfaceCurses::popup_add_impl(PopupEntryType type,
+ const gchar *name, bool highlight)
+{
+ gchar *entry;
+
+ if (isendwin()) /* batch mode */
+ return;
+
+ entry = g_strconcat(highlight ? "*" : " ", name, NIL);
+
+ popup.longest = MAX(popup.longest, (gint)strlen(name));
+ popup.length++;
+
+ popup.list = g_slist_prepend(popup.list, entry);
+}
+
+void
+InterfaceCurses::popup_show_impl(void)
+{
+ int lines, cols; /* screen dimensions */
+ int popup_lines;
+ gint popup_cols;
+ gint cur_file, cur_line;
+
+ if (isendwin()) /* batch mode */
+ goto cleanup;
+
+ getmaxyx(stdscr, lines, cols);
+
+ popup.longest += 3;
+ popup.list = g_slist_reverse(popup.list);
+
+ /* popup_cols = floor(cols / popup.longest) */
+ popup_cols = MAX(cols / popup.longest, 1);
+ /* popup_lines = ceil(popup.length / popup_cols) */
+ popup_lines = popup.length / popup_cols;
+ if ((popup.length % popup_cols))
+ popup_lines++;
+ popup_lines = MIN(popup_lines, lines - 1);
+
+ /* window covers message, scintilla and info windows */
+ popup.window = newwin(popup_lines, 0, lines - 1 - popup_lines, 0);
+ wbkgdset(popup.window, ' ' | SCI_COLOR_ATTR(COLOR_BLACK, COLOR_BLUE));
+
+ cur_file = 0;
+ cur_line = 1;
+ for (GSList *cur = popup.list; cur; cur = g_slist_next(cur)) {
+ gchar *entry = (gchar *)cur->data;
+
+ if (cur_file && !(cur_file % popup_cols)) {
+ wclrtoeol(popup.window);
+ waddch(popup.window, '\n');
+ cur_line++;
+ }
+
+ cur_file++;
+
+ if (cur_line == popup_lines && !(cur_file % popup_cols) &&
+ cur_file < popup.length) {
+ (void)wattrset(popup.window, A_BOLD);
+ waddstr(popup.window, "...");
+ break;
+ }
+
+ (void)wattrset(popup.window, *entry == '*' ? A_BOLD : A_NORMAL);
+ waddstr(popup.window, entry + 1);
+ for (int i = popup.longest - strlen(entry) + 1; i; i--)
+ waddch(popup.window, ' ');
+
+ g_free(cur->data);
+ }
+ wclrtoeol(popup.window);
+
+cleanup:
+ g_slist_free(popup.list);
+ popup.list = NULL;
+ popup.longest = popup.length = 0;
+}
+
+void
+InterfaceCurses::popup_clear_impl(void)
+{
+ if (!popup.window)
+ return;
+
+ redrawwin(info_window);
+ wrefresh(info_window);
+ redrawwin(current_view->get_window());
+ current_view->refresh();
+ redrawwin(msg_window);
+ wrefresh(msg_window);
+
+ delwin(popup.window);
+ popup.window = NULL;
+}
+
+/**
+ * One iteration of the event loop.
+ *
+ * This is a global function, so it may
+ * be used as an Emscripten callback.
+ *
+ * @bug
+ * Can probably be defined as a static method,
+ * so we can avoid declaring it a fried function of
+ * InterfaceCurses.
+ */
+void
+event_loop_iter()
+{
+ int key;
+
+ keypad(interface.cmdline_window, Flags::ed & Flags::ED_FNKEYS);
+
+ /* no special <CTRL/C> handling */
+ raw();
+ key = wgetch(interface.cmdline_window);
+ /* allow asynchronous interruptions on <CTRL/C> */
+ cbreak();
+ if (key == ERR)
+ return;
+
+ switch (key) {
+#ifdef KEY_RESIZE
+ case KEY_RESIZE:
+#ifdef PDCURSES
+ resize_term(0, 0);
+#endif
+ interface.resize_all_windows();
+ break;
+#endif
+ case 0x7F: /* DEL */
+ case KEY_BACKSPACE:
+ cmdline_keypress('\b');
+ break;
+ case KEY_ENTER:
+ case '\r':
+ case '\n':
+ cmdline_keypress(get_eol());
+ break;
+
+ /*
+ * Function key macros
+ */
+#define FN(KEY) case KEY_##KEY: cmdline_fnmacro(#KEY); break
+#define FNS(KEY) FN(KEY); FN(S##KEY)
+ FN(DOWN); FN(UP); FNS(LEFT); FNS(RIGHT);
+ FNS(HOME);
+ case KEY_F(0)...KEY_F(63): {
+ gchar macro_name[3+1];
+
+ g_snprintf(macro_name, sizeof(macro_name),
+ "F%d", key - KEY_F0);
+ cmdline_fnmacro(macro_name);
+ break;
+ }
+ FNS(DC);
+ FNS(IC);
+ FN(NPAGE); FN(PPAGE);
+ FNS(PRINT);
+ FN(A1); FN(A3); FN(B2); FN(C1); FN(C3);
+ FNS(END);
+ FNS(HELP);
+#undef FNS
+#undef FN
+
+ /*
+ * Control keys and keys with printable representation
+ */
+ default:
+ if (key <= 0xFF)
+ cmdline_keypress((gchar)key);
+ }
+
+ sigint_occurred = FALSE;
+
+ interface.current_view->refresh();
+ if (interface.popup.window)
+ wrefresh(interface.popup.window);
+}
+
+void
+InterfaceCurses::event_loop_impl(void)
+{
+ /* initial refresh: window might have been changed in batch mode */
+ current_view->refresh();
+ draw_info();
+
+#ifdef EMSCRIPTEN
+ PDC_emscripten_set_handler(event_loop_iter, TRUE);
+#else
+ for (;;)
+ event_loop_iter();
+#endif
+}
+
+InterfaceCurses::Popup::~Popup()
+{
+ if (window)
+ delwin(window);
+ if (list)
+ g_slist_free(list);
+}
+
+InterfaceCurses::~InterfaceCurses()
+{
+ if (info_window)
+ delwin(info_window);
+ g_free(info_current);
+ if (cmdline_window)
+ delwin(cmdline_window);
+ g_free(cmdline_current);
+ if (msg_window)
+ delwin(msg_window);
+
+ if (!isendwin())
+ endwin();
+
+ if (screen)
+ delscreen(screen);
+ if (screen_tty)
+ fclose(screen_tty);
+}
+
+/*
+ * Callbacks
+ */
+
+static void
+scintilla_notify(Scintilla *sci, int idFrom, void *notify, void *user_data)
+{
+ interface.process_notify((SCNotification *)notify);
+}
+
+} /* namespace SciTECO */