#include "config.h"
#include "sl-feat.h"
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include "slang.h"
#include "_slang.h"
#include "slcurses.h"
/* This file is meant to implement a primitive curses implementation in
* terms of SLsmg calls. The fact is that the interfaces are sufficiently
* different that a 100% emulation is not possible.
*/
SLcurses_Window_Type *SLcurses_Stdscr;
int SLcurses_Esc_Delay = 150; /* 0.15 seconds */
SLtt_Char_Type SLcurses_Acs_Map [128];
int SLcurses_Is_Endwin = 1;
static void blank_line (unsigned short *b, unsigned int len, unsigned short color)
{
unsigned short *bmax;
bmax = b + len;
color = (color << 8) | 32;
while (b < bmax) *b++ = color;
}
int SLcurses_mvprintw (int r, int c, char *fmt, ...)
{
char p[1000];
va_list ap;
SLcurses_wmove (SLcurses_Stdscr, r, c);
va_start(ap, fmt);
(void) vsprintf(p, fmt, ap);
va_end(ap);
SLcurses_waddnstr (SLcurses_Stdscr, p, -1);
return 0;
}
int SLcurses_mvwprintw (SLcurses_Window_Type *w, int r, int c, char *fmt, ...)
{
char p[1000];
va_list ap;
SLcurses_wmove (w, r, c);
va_start(ap, fmt);
(void) vsprintf(p, fmt, ap);
va_end(ap);
SLcurses_waddnstr (w, p, -1);
return 0;
}
int SLcurses_nil (void)
{
return 0;
}
int SLcurses_has_colors(void)
{
return SLtt_Use_Ansi_Colors;
}
int SLcurses_nodelay (SLcurses_Window_Type *w, int onoff)
{
w->delay_off = (onoff ? 0 : -1);
return 0;
}
int SLcurses_wgetch (SLcurses_Window_Type *w)
{
if (w == NULL)
return ERR;
SLcurses_wrefresh (w);
if ((w->delay_off == -1) ||
SLang_input_pending (w->delay_off))
{
if (w->use_keypad)
{
int ch = SLang_getkey ();
if (ch == '\033')
{
if (0 == SLang_input_pending (ESCDELAY / 100))
return ch;
}
else if (ch == 0xFFFF) return ERR;
SLang_ungetkey (ch);
return SLkp_getkey ();
}
return SLang_getkey ();
}
return ERR;
}
int SLcurses_getch (void)
{
return SLcurses_wgetch (SLcurses_Stdscr);
}
/* This is a super hack. That fact is that SLsmg and curses
* are incompatible.
*/
static unsigned char Color_Objects[256];
static unsigned int map_attr_to_object (SLtt_Char_Type attr)
{
unsigned int obj;
SLtt_Char_Type at;
obj = (attr >> 8) & 0xFF;
if (SLtt_Use_Ansi_Colors)
{
if (Color_Objects[obj] != 0) return obj;
at = SLtt_get_color_object (obj & 0xF);
if (attr & A_BOLD) at |= SLTT_BOLD_MASK;
if (attr & A_UNDERLINE) at |= SLTT_ULINE_MASK;
if (attr & A_REVERSE) at |= SLTT_REV_MASK;
SLtt_set_color_object (obj, at);
Color_Objects[obj] = 1;
}
else obj = obj & 0xF0;
return obj;
}
int SLcurses_start_color (void)
{
int f, b;
int obj;
if (SLtt_Use_Ansi_Colors == 0) return -1;
obj = 0;
for (f = 0; f < 8; f++)
{
for (b = 0; b < 8; b++)
{
obj++;
SLtt_set_color_fgbg (obj, f, b);
}
}
return 0;
}
#ifdef SIGINT
static void sigint_handler (int sig)
{
SLang_reset_tty ();
SLsmg_reset_smg ();
exit (sig);
}
#endif
/* Values are assumed to be 0, 1, 2. This fact is exploited */
static int TTY_State;
static int init_tty (int suspend_ok)
{
if (-1 == SLang_init_tty (-1, 1, 0))
return -1;
#ifdef REAL_UNIX_SYSTEM
if (suspend_ok) SLtty_set_suspend_state (1);
#endif
return 0;
}
int SLcurses_raw (void)
{
TTY_State = 1;
return init_tty (0);
}
int SLcurses_cbreak (void)
{
TTY_State = 2;
return init_tty (1);
}
#if defined(SIGTSTP) && defined(SIGSTOP)
static void sigtstp_handler (int sig)
{
sig = errno;
SLsmg_suspend_smg ();
if (TTY_State)
SLang_reset_tty ();
kill(getpid(),SIGSTOP);
SLsmg_resume_smg ();
if (TTY_State) init_tty (TTY_State - 1);
signal (SIGTSTP, sigtstp_handler);
errno = sig;
}
#endif
SLcurses_Window_Type *SLcurses_initscr (void)
{
SLcurses_Is_Endwin = 0;
SLsmg_Newline_Moves = -1;
SLtt_get_terminfo ();
if ((-1 == SLkp_init ())
|| (-1 == SLcurses_cbreak ())
|| (NULL == (SLcurses_Stdscr = SLcurses_newwin (0, 0, 0, 0))))
{
SLang_doerror (NULL);
SLang_exit_error ("SLcurses_initscr: init failed\n");
return NULL;
}
SLsmg_init_smg ();
#ifdef SIGINT
signal (SIGINT, sigint_handler);
#endif
#if defined(SIGTSTP) && defined(SIGSTOP)
signal (SIGTSTP, sigtstp_handler);
#endif
SLtt_set_mono (A_BOLD >> 8, NULL, SLTT_BOLD_MASK);
SLtt_set_mono (A_UNDERLINE >> 8, NULL, SLTT_ULINE_MASK);
SLtt_set_mono (A_REVERSE >> 8, NULL, SLTT_REV_MASK);
/* SLtt_set_mono (A_BLINK >> 8, NULL, SLTT_BLINK_MASK); */
SLtt_set_mono ((A_BOLD|A_UNDERLINE) >> 8, NULL, SLTT_ULINE_MASK|SLTT_BOLD_MASK);
SLtt_set_mono ((A_REVERSE|A_UNDERLINE) >> 8, NULL, SLTT_ULINE_MASK|SLTT_REV_MASK);
if (SLtt_Has_Alt_Charset)
{
SLcurses_Acs_Map[SLSMG_ULCORN_CHAR] = SLSMG_ULCORN_CHAR | A_ALTCHARSET;
SLcurses_Acs_Map[SLSMG_URCORN_CHAR] = SLSMG_URCORN_CHAR | A_ALTCHARSET;
SLcurses_Acs_Map[SLSMG_LLCORN_CHAR] = SLSMG_LLCORN_CHAR | A_ALTCHARSET;
SLcurses_Acs_Map[SLSMG_LRCORN_CHAR] = SLSMG_LRCORN_CHAR | A_ALTCHARSET;
SLcurses_Acs_Map[SLSMG_UTEE_CHAR] = SLSMG_UTEE_CHAR | A_ALTCHARSET;
SLcurses_Acs_Map[SLSMG_DTEE_CHAR] = SLSMG_DTEE_CHAR | A_ALTCHARSET;
SLcurses_Acs_Map[SLSMG_LTEE_CHAR] = SLSMG_LTEE_CHAR | A_ALTCHARSET;
SLcurses_Acs_Map[SLSMG_RTEE_CHAR] = SLSMG_RTEE_CHAR | A_ALTCHARSET;
SLcurses_Acs_Map[SLSMG_VLINE_CHAR] = SLSMG_VLINE_CHAR | A_ALTCHARSET;
SLcurses_Acs_Map[SLSMG_HLINE_CHAR] = SLSMG_HLINE_CHAR | A_ALTCHARSET;
SLcurses_Acs_Map[SLSMG_PLUS_CHAR] = SLSMG_PLUS_CHAR | A_ALTCHARSET;
SLcurses_Acs_Map[SLSMG_CKBRD_CHAR] = SLSMG_CKBRD_CHAR | A_ALTCHARSET;
}
else
{
/* ugly defaults to use on terminals which don't support graphics */
SLcurses_Acs_Map[SLSMG_ULCORN_CHAR] = '+';
SLcurses_Acs_Map[SLSMG_URCORN_CHAR] = '+';
SLcurses_Acs_Map[SLSMG_LLCORN_CHAR] = '+';
SLcurses_Acs_Map[SLSMG_LRCORN_CHAR] = '+';
SLcurses_Acs_Map[SLSMG_UTEE_CHAR] = '+';
SLcurses_Acs_Map[SLSMG_DTEE_CHAR] = '+';
SLcurses_Acs_Map[SLSMG_LTEE_CHAR] = '+';
SLcurses_Acs_Map[SLSMG_RTEE_CHAR] = '+';
SLcurses_Acs_Map[SLSMG_VLINE_CHAR] = '|';
SLcurses_Acs_Map[SLSMG_HLINE_CHAR] = '-';
SLcurses_Acs_Map[SLSMG_PLUS_CHAR] = '+';
SLcurses_Acs_Map[SLSMG_CKBRD_CHAR] = '#';
}
return SLcurses_Stdscr;
}
int SLcurses_wattrset (SLcurses_Window_Type *w, SLtt_Char_Type ch)
{
unsigned int obj;
obj = map_attr_to_object (ch);
w->color = obj;
w->attr = ch;
return 0;
}
int SLcurses_wattroff (SLcurses_Window_Type *w, SLtt_Char_Type ch)
{
if (SLtt_Use_Ansi_Colors)
return SLcurses_wattrset (w, 0);
w->attr &= ~ch;
return SLcurses_wattrset (w, w->attr);
}
int SLcurses_wattron (SLcurses_Window_Type *w, SLtt_Char_Type ch)
{
if (SLtt_Use_Ansi_Colors)
return SLcurses_wattrset (w, ch);
w->attr |= ch;
return SLcurses_wattrset (w, w->attr);
}
int SLcurses_delwin (SLcurses_Window_Type *w)
{
if (w == NULL) return 0;
if (w->lines != NULL)
{
unsigned short **lines = w->lines;
if (w->is_subwin == 0)
{
unsigned int r, rmax;
rmax = w->nrows;
for (r = 0; r < rmax; r++)
{
SLfree ((char *)lines[r]);
}
}
SLfree ((char *)lines);
}
SLfree ((char *)w);
if (w == SLcurses_Stdscr)
SLcurses_Stdscr = NULL;
return 0;
}
SLcurses_Window_Type *SLcurses_newwin (unsigned int nrows, unsigned int ncols,
unsigned int r, unsigned int c)
{
SLcurses_Window_Type *win;
unsigned short **lines;
if (r >= (unsigned int) SLtt_Screen_Rows)
return NULL;
if (c >= (unsigned int) SLtt_Screen_Cols)
return NULL;
if (NULL == (win = (SLcurses_Window_Type *) SLmalloc (sizeof (SLcurses_Window_Type))))
return NULL;
SLMEMSET ((char *) win, 0, sizeof (SLcurses_Window_Type));
if (nrows == 0)
nrows = (unsigned int) SLtt_Screen_Rows - r;
if (ncols == 0)
ncols = (unsigned int) SLtt_Screen_Cols - c;
lines = (unsigned short **) SLmalloc (nrows * sizeof (unsigned short *));
if (lines == NULL)
{
SLcurses_delwin (win);
return NULL;
}
SLMEMSET ((char *) lines, 0, nrows * sizeof (unsigned short *));
win->lines = lines;
win->scroll_max = win->nrows = nrows;
win->ncols = ncols;
win->_begy = r;
win->_begx = c;
win->_maxx = (c + ncols) - 1;
win->_maxy = (r + nrows) - 1;
win->modified = 1;
win->delay_off = -1;
for (r = 0; r < nrows; r++)
{
unsigned short *b;
b = (unsigned short *) SLmalloc (ncols * sizeof (short));
if (b == NULL)
{
SLcurses_delwin (win);
return NULL;
}
lines [r] = b;
blank_line (b, ncols, 0);
}
return win;
}
int SLcurses_wmove (SLcurses_Window_Type *win, unsigned int r, unsigned int c)
{
if (win == NULL) return -1;
win->_cury = r;
win->_curx = c;
win->modified = 1;
return 0;
}
static int do_newline (SLcurses_Window_Type *w)
{
w->_curx = 0;
w->_cury += 1;
if (w->_cury >= w->scroll_max)
{
w->_cury = w->scroll_max - 1;
if (w->scroll_ok)
SLcurses_wscrl (w, 1);
}
return 0;
}
int SLcurses_waddch (SLcurses_Window_Type *win, SLtt_Char_Type attr)
{
unsigned short *b, ch;
unsigned short color;
if (win == NULL) return -1;
if (win->_cury >= win->nrows)
{
/* Curses seems to move current postion to top of window. */
win->_cury = win->_curx = 0;
return -1;
}
win->modified = 1;
ch = attr & 0xFF;
if (attr == ch)
color = win->color;
else
{
/* hack to pick up the default color for graphics chars */
if (((attr & A_COLOR) == 0) && ((attr & A_ALTCHARSET) != 0))
{
attr |= win->color << 8;
}
color = map_attr_to_object (attr);
}
if (ch < ' ')
{
if (ch == '\n')
{
SLcurses_wclrtoeol (win);
return do_newline (win);
}
if (ch == '\r')
{
win->_curx = 0;
return 0;
}
if (ch == '\b')
{
if (win->_curx > 0)
win->_curx--;
return 0;
}
/* HACK HACK!!!! */
if (ch == '\t') ch = ' ';
}
if (win->_curx >= win->ncols)
do_newline (win);
b = win->lines[win->_cury] + win->_curx;
*b = ch | (color << 8);
win->_curx++;
return 0;
}
int SLcurses_wnoutrefresh (SLcurses_Window_Type *w)
{
unsigned int len;
unsigned int r, c;
unsigned int i, imax;
if (SLcurses_Is_Endwin)
{
if (TTY_State) init_tty (TTY_State - 1);
SLsmg_resume_smg ();
SLcurses_Is_Endwin = 0;
}
if (w == NULL)
{
SLsmg_refresh ();
return -1;
}
if (w->modified == 0)
return 0;
r = w->_begy;
c = w->_begx;
len = w->ncols;
imax = w->nrows;
for (i = 0; i < imax; i++)
{
SLsmg_gotorc (r, c);
SLsmg_write_color_chars (w->lines[i], len);
r++;
}
if (w->has_box)
SLsmg_draw_box(w->_begy, w->_begx, w->nrows, w->ncols);
SLsmg_gotorc (w->_begy + w->_cury, w->_begx + w->_curx);
w->modified = 0;
return 0;
}
int SLcurses_wrefresh (SLcurses_Window_Type *w)
{
if (w == NULL)
return -1;
if (w->modified == 0)
return 0;
SLcurses_wnoutrefresh (w);
SLsmg_refresh ();
return 0;
}
int SLcurses_wclrtoeol (SLcurses_Window_Type *w)
{
unsigned short *b, *bmax;
unsigned short blank;
if (w == NULL) return -1;
if (w->_cury >= w->nrows)
return 0;
w->modified = 1;
blank = (w->color << 8) | 0x20;
b = w->lines[w->_cury];
bmax = b + w->ncols;
b += w->_curx;
while (b < bmax) *b++ = blank;
return 0;
}
int SLcurses_wclrtobot (SLcurses_Window_Type *w)
{
unsigned short *b, *bmax;
unsigned short blank;
unsigned int r;
if (w == NULL) return -1;
w->modified = 1;
blank = (w->color << 8) | 0x20;
SLcurses_wclrtoeol (w);
for (r = w->_cury + 1; r < w->nrows; r++)
{
b = w->lines [r];
bmax = b + w->ncols;
while (b < bmax) *b++ = blank;
}
return 0;
}
int SLcurses_wscrl (SLcurses_Window_Type *w, int n)
{
unsigned short **lines;
unsigned int r, rmax, rmin, ncols;
unsigned short color;
if ((w == NULL) || (w->scroll_ok == 0))
return -1;
w->modified = 1;
#if 0
if (w->is_subwin)
{
SLang_reset_tty ();
SLsmg_reset_smg ();
fprintf (stderr, "\rAttempt to scroll a subwindow\n");
exit (1);
}
#endif
color = w->color;
ncols = w->ncols;
lines = w->lines;
rmax = w->scroll_max;
rmin = w->scroll_min;
if (rmax > w->nrows)
rmax = w->nrows;
if (rmin >= rmax)
return 0;
while (n > 0)
{
for (r = rmin + 1; r < rmax; r++)
{
/* lines[r - 1] = lines[r]; */
memcpy ((char *)lines[r - 1], (char *)lines[r],
sizeof (short) * ncols);
}
blank_line (lines[rmax - 1], ncols, color);
n--;
}
rmax--;
while (n < 0)
{
for (r = rmax; r > rmin; r--)
{
memcpy ((char *)lines[r], (char *)lines[r - 1],
sizeof (short) * ncols);
}
blank_line (lines[rmin], ncols, color);
n++;
}
/* wmove (w, w->nrows - 1, 0); */
/* wclrtobot (w); */
return 0;
}
/* Note: if len is < 0, entire string will be used.
*/
int SLcurses_waddnstr (SLcurses_Window_Type *w, char *str, int len)
{
unsigned short *b;
unsigned short color;
unsigned char ch;
unsigned int nrows, ncols, crow, ccol;
if ((w == NULL)
|| (str == NULL))
return -1;
w->modified = 1;
nrows = w->nrows;
ncols = w->ncols;
crow = w->_cury;
ccol = w->_curx;
color = (w->color << 8);
if (w->scroll_max <= nrows)
nrows = w->scroll_max;
if (crow >= nrows)
crow = 0; /* wrap back to top */
b = w->lines [crow] + ccol;
while (len && ((ch = (unsigned char) *str++) != 0))
{
len--;
if (ch == '\n')
{
w->_cury = crow;
w->_curx = ccol;
SLcurses_wclrtoeol (w);
do_newline (w);
crow = w->_cury;
ccol = w->_curx;
b = w->lines[crow];
continue;
}
if (ccol >= ncols)
{
ccol = 0;
crow++;
if (crow >= nrows)
{
w->_curx = 0;
w->_cury = crow;
do_newline (w);
crow = w->_cury;
ccol = w->_curx;
}
b = w->lines [crow];
}
if (ch == '\t')
{
unsigned int n = ccol;
n += SLsmg_Tab_Width;
n = SLsmg_Tab_Width - (n % SLsmg_Tab_Width);
if (ccol + n > ncols) n = ncols - len;
ccol += n;
while (n--)
*b++ = color | ' ';
continue;
}
*b++ = color | ch;
ccol++;
}
w->_curx = ccol;
w->_cury = crow;
return 0;
}
int SLcurses_wprintw (SLcurses_Window_Type *w, char *fmt, ...)
{
char p[1000];
va_list ap;
va_start(ap, fmt);
(void) vsprintf(p, fmt, ap);
va_end(ap);
SLcurses_waddnstr (w, p, -1);
return 0;
}
/* This routine IS NOT CORRECT. It needs to compute the proper overlap
* and copy accordingly. Here, I just assume windows are same size.
*/
#if 0
int SLcurses_overlay (SLcurses_Window_Type *swin, SLcurses_Window_Type *dwin)
{
unsigned short *s, *smax, *d, *dmax;
if ((swin == NULL) || (dwin == NULL))
return -1;
s = swin->buf;
smax = swin->bufmax;
d = dwin->buf;
dmax = dwin->bufmax;
while ((s < smax) && (d < dmax))
{
unsigned short ch = *s++;
if ((ch & 0xFF) != ' ')
*d = ch;
d++;
}
return -1; /* not implemented */
}
#endif
SLcurses_Window_Type *SLcurses_subwin (SLcurses_Window_Type *orig,
unsigned int nlines, unsigned int ncols,
unsigned int begin_y, unsigned int begin_x)
{
SLcurses_Window_Type *sw;
int r, c;
unsigned int i;
if (orig == NULL)
return NULL;
sw = (SLcurses_Window_Type *) SLmalloc (sizeof (SLcurses_Window_Type));
if (sw == NULL)
return NULL;
SLMEMSET ((char *)sw, 0, sizeof (SLcurses_Window_Type));
#if 1
r = begin_y - orig->_begy;
#else
r = 1 + ((int)orig->nrows - (int)nlines) / 2;
#endif
if (r < 0) r = 0;
if (r + nlines > orig->nrows) nlines = orig->nrows - r;
c = ((int)orig->ncols - (int)ncols) / 2;
if (c < 0) c = 0;
if (c + ncols > orig->ncols) ncols = orig->ncols - c;
sw->scroll_min = 0;
sw->scroll_max = sw->nrows = nlines;
sw->ncols = ncols;
sw->_begy = begin_y;
sw->_begx = begin_x;
sw->_maxx = (begin_x + ncols) - 1;
sw->_maxy = (begin_y + nlines) - 1;
sw->lines = (unsigned short **) SLmalloc (nlines * sizeof (unsigned short *));
if (sw->lines == NULL)
{
SLcurses_delwin (sw);
return NULL;
}
for (i = 0; i < nlines; i++)
{
sw->lines [i] = orig->lines [r + i] + c;
}
sw->is_subwin = 1;
return sw;
}
int SLcurses_wclear (SLcurses_Window_Type *w)
{
unsigned int i;
if (w != NULL) w->modified = 1;
for (i=0; i < w->nrows; i++)
blank_line (w->lines[i], w->ncols, w->color);
return 0;
}
int SLcurses_printw (char *fmt, ...)
{
char p[1000];
va_list ap;
va_start(ap, fmt);
(void) vsprintf(p, fmt, ap);
va_end(ap);
SLcurses_waddnstr (SLcurses_Stdscr, p, -1);
return 0;
}
int SLcurses_wdelch (SLcurses_Window_Type *w)
{
unsigned short *p, *p1, *pmax;
p = w->lines[w->_cury];
pmax = p + w->ncols;
p += w->_curx;
p1 = p + 1;
while (p1 < pmax)
{
*p = *p1;
p = p1;
p1++;
}
if (p < pmax)
*p = ' ' | (w->color << 8);
w->modified = 1;
return 0;
}
int SLcurses_winsch (SLcurses_Window_Type *w, int ch)
{
unsigned short *p, *p1, *pmax;
p = w->lines[w->_cury];
pmax = p + w->ncols;
p += w->_curx;
p1 = pmax - 1;
while (pmax > p)
{
*pmax = *p1;
pmax = p1;
p1--;
}
if (p < pmax)
*p = (unsigned short) ch | (w->color << 8);
w->modified = 1;
return 0;
}
int SLcurses_endwin (void)
{
SLcurses_Is_Endwin = 1;
SLsmg_suspend_smg ();
SLang_reset_tty ();
return 0;
}
#if 0
int SLcurses_mvwscanw (SLcurses_Window_Type *w, unsigned int r, unsigned int c,
char *fmt, ...)
{
#if HAVE_VFSCANF
int ret;
va_list ap;
SLcurses_wmove (w, r, c);
SLcurses_wrefresh (w);
va_start(ap, fmt);
ret = vfscanf (stdin, fmt, ap);
va_end(ap);
return ret;
#else
return 0;
#endif
}
int SLcurses_wscanw (SLcurses_Window_Type *w, char *fmt, ...)
{
#if HAVE_VFSCANF
va_list ap;
int ret;
SLcurses_wrefresh (w);
va_start(ap, fmt);
ret = vfscanf (stdin, fmt, ap);
va_end(ap);
return ret;
#else
return 0;
#endif
}
int SLcurses_scanw (char *fmt, ...)
{
#ifdef HAVE_VFSCANF
va_list ap;
int ret;
SLcurses_wrefresh (SLcurses_Stdscr);
va_start(ap, fmt);
ret = vfscanf (stdin, fmt, ap);
va_end(ap);
return ret;
#else
return 0;
#endif
}
#endif
int SLcurses_clearok (SLcurses_Window_Type *w, int bf)
{
if (bf)
{
SLsmg_cls ();
w->modified = 1;
}
return 0;
}