mirror of
git://git.code.sf.net/p/zsh/code
synced 2024-11-15 13:34:18 +01:00
1789 lines
40 KiB
C
1789 lines
40 KiB
C
/*
|
|
* curses.c - curses windowing module for zsh
|
|
*
|
|
* This file is part of zsh, the Z shell.
|
|
*
|
|
* Copyright (c) 2007 Clint Adams
|
|
* All rights reserved.
|
|
*
|
|
* Permission is hereby granted, without written agreement and without
|
|
* license or royalty fees, to use, copy, modify, and distribute this
|
|
* software and to distribute modified versions of this software for any
|
|
* purpose, provided that the above copyright notice and the following
|
|
* two paragraphs appear in all copies of this software.
|
|
*
|
|
* In no event shall Clint Adams or the Zsh Development Group be liable
|
|
* to any party for direct, indirect, special, incidental, or consequential
|
|
* damages arising out of the use of this software and its documentation,
|
|
* even if Clint Adams and the Zsh Development Group have been advised of
|
|
* the possibility of such damage.
|
|
*
|
|
* Clint Adams and the Zsh Development Group specifically disclaim any
|
|
* warranties, including, but not limited to, the implied warranties of
|
|
* merchantability and fitness for a particular purpose. The software
|
|
* provided hereunder is on an "as is" basis, and Clint Adams and the
|
|
* Zsh Development Group have no obligation to provide maintenance,
|
|
* support, updates, enhancements, or modifications.
|
|
*
|
|
*/
|
|
|
|
#define ZSH_CURSES_SOURCE 1
|
|
|
|
#include "curses.mdh"
|
|
#include "curses.pro"
|
|
|
|
#ifndef MULTIBYTE_SUPPORT
|
|
# undef HAVE_GETCCHAR
|
|
# undef HAVE_SETCCHAR
|
|
# undef HAVE_WADDWSTR
|
|
# undef HAVE_WGET_WCH
|
|
# undef HAVE_WIN_WCH
|
|
# undef HAVE_NCURSESW_NCURSES_H
|
|
#endif
|
|
|
|
#ifdef ZSH_HAVE_CURSES_H
|
|
# include "../zshcurses.h"
|
|
#endif
|
|
|
|
#ifdef HAVE_SETCCHAR
|
|
# include <wchar.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
|
|
enum zc_win_flags {
|
|
/* Window is permanent (probably "stdscr") */
|
|
ZCWF_PERMANENT = 0x0001,
|
|
/* Scrolling enabled */
|
|
ZCWF_SCROLL = 0x0002
|
|
};
|
|
|
|
typedef struct zc_win *ZCWin;
|
|
|
|
struct zc_win {
|
|
WINDOW *win;
|
|
char *name;
|
|
int flags;
|
|
LinkList children;
|
|
ZCWin parent;
|
|
};
|
|
|
|
struct zcurses_namenumberpair {
|
|
char *name;
|
|
int number;
|
|
};
|
|
|
|
struct colorpairnode {
|
|
struct hashnode node;
|
|
short colorpair;
|
|
};
|
|
typedef struct colorpairnode *Colorpairnode;
|
|
|
|
typedef int (*zccmd_t)(const char *nam, char **args);
|
|
struct zcurses_subcommand {
|
|
const char *name;
|
|
zccmd_t cmd;
|
|
int minargs;
|
|
int maxargs;
|
|
};
|
|
|
|
static struct ttyinfo saved_tty_state;
|
|
static struct ttyinfo curses_tty_state;
|
|
static LinkList zcurses_windows;
|
|
static HashTable zcurses_colorpairs = NULL;
|
|
#ifdef NCURSES_MOUSE_VERSION
|
|
/*
|
|
* The following is in principle a general set of flags, but
|
|
* is currently only needed for mouse status.
|
|
*/
|
|
static int zcurses_flags;
|
|
#endif
|
|
|
|
#define ZCURSES_EINVALID 1
|
|
#define ZCURSES_EDEFINED 2
|
|
#define ZCURSES_EUNDEFINED 3
|
|
|
|
#define ZCURSES_UNUSED 1
|
|
#define ZCURSES_USED 2
|
|
|
|
#define ZCURSES_ATTRON 1
|
|
#define ZCURSES_ATTROFF 2
|
|
|
|
static int zc_errno, zc_color_phase=0;
|
|
static short next_cp=0;
|
|
|
|
enum {
|
|
ZCF_MOUSE_ACTIVE,
|
|
ZCF_MOUSE_MASK_CHANGED
|
|
};
|
|
|
|
static const struct zcurses_namenumberpair zcurses_attributes[] = {
|
|
{"blink", A_BLINK},
|
|
{"bold", A_BOLD},
|
|
{"dim", A_DIM},
|
|
{"reverse", A_REVERSE},
|
|
{"standout", A_STANDOUT},
|
|
{"underline", A_UNDERLINE},
|
|
{NULL, 0}
|
|
};
|
|
|
|
static const struct zcurses_namenumberpair zcurses_colors[] = {
|
|
{"black", COLOR_BLACK},
|
|
{"red", COLOR_RED},
|
|
{"green", COLOR_GREEN},
|
|
{"yellow", COLOR_YELLOW},
|
|
{"blue", COLOR_BLUE},
|
|
{"magenta", COLOR_MAGENTA},
|
|
{"cyan", COLOR_CYAN},
|
|
{"white", COLOR_WHITE},
|
|
#ifdef HAVE_USE_DEFAULT_COLORS
|
|
{"default", -1},
|
|
#endif
|
|
{NULL, 0}
|
|
};
|
|
|
|
#ifdef NCURSES_MOUSE_VERSION
|
|
enum zcurses_mouse_event_types {
|
|
ZCME_PRESSED,
|
|
ZCME_RELEASED,
|
|
ZCME_CLICKED,
|
|
ZCME_DOUBLE_CLICKED,
|
|
ZCME_TRIPLE_CLICKED
|
|
};
|
|
|
|
static const struct zcurses_namenumberpair zcurses_mouse_event_list[] = {
|
|
{"PRESSED", ZCME_PRESSED},
|
|
{"RELEASED", ZCME_RELEASED},
|
|
{"CLICKED", ZCME_CLICKED},
|
|
{"DOUBLE_CLICKED", ZCME_DOUBLE_CLICKED},
|
|
{"TRIPLE_CLICKED", ZCME_TRIPLE_CLICKED},
|
|
{NULL, 0}
|
|
};
|
|
|
|
struct zcurses_mouse_event {
|
|
int button;
|
|
int what;
|
|
mmask_t event;
|
|
};
|
|
|
|
static const struct zcurses_mouse_event zcurses_mouse_map[] = {
|
|
{ 1, ZCME_PRESSED, BUTTON1_PRESSED },
|
|
{ 1, ZCME_RELEASED, BUTTON1_RELEASED },
|
|
{ 1, ZCME_CLICKED, BUTTON1_CLICKED },
|
|
{ 1, ZCME_DOUBLE_CLICKED, BUTTON1_DOUBLE_CLICKED },
|
|
{ 1, ZCME_TRIPLE_CLICKED, BUTTON1_TRIPLE_CLICKED },
|
|
|
|
{ 2, ZCME_PRESSED, BUTTON2_PRESSED },
|
|
{ 2, ZCME_RELEASED, BUTTON2_RELEASED },
|
|
{ 2, ZCME_CLICKED, BUTTON2_CLICKED },
|
|
{ 2, ZCME_DOUBLE_CLICKED, BUTTON2_DOUBLE_CLICKED },
|
|
{ 2, ZCME_TRIPLE_CLICKED, BUTTON2_TRIPLE_CLICKED },
|
|
|
|
{ 3, ZCME_PRESSED, BUTTON3_PRESSED },
|
|
{ 3, ZCME_RELEASED, BUTTON3_RELEASED },
|
|
{ 3, ZCME_CLICKED, BUTTON3_CLICKED },
|
|
{ 3, ZCME_DOUBLE_CLICKED, BUTTON3_DOUBLE_CLICKED },
|
|
{ 3, ZCME_TRIPLE_CLICKED, BUTTON3_TRIPLE_CLICKED },
|
|
|
|
{ 4, ZCME_PRESSED, BUTTON4_PRESSED },
|
|
{ 4, ZCME_RELEASED, BUTTON4_RELEASED },
|
|
{ 4, ZCME_CLICKED, BUTTON4_CLICKED },
|
|
{ 4, ZCME_DOUBLE_CLICKED, BUTTON4_DOUBLE_CLICKED },
|
|
{ 4, ZCME_TRIPLE_CLICKED, BUTTON4_TRIPLE_CLICKED },
|
|
|
|
#ifdef BUTTON5_PRESSED
|
|
/* Not defined if only 32 bits available */
|
|
{ 5, ZCME_PRESSED, BUTTON5_PRESSED },
|
|
{ 5, ZCME_RELEASED, BUTTON5_RELEASED },
|
|
{ 5, ZCME_CLICKED, BUTTON5_CLICKED },
|
|
{ 5, ZCME_DOUBLE_CLICKED, BUTTON5_DOUBLE_CLICKED },
|
|
{ 5, ZCME_TRIPLE_CLICKED, BUTTON5_TRIPLE_CLICKED },
|
|
#endif
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
static mmask_t zcurses_mouse_mask = ALL_MOUSE_EVENTS;
|
|
|
|
#endif
|
|
|
|
/* Autogenerated keypad string/number mapping*/
|
|
#include "curses_keys.h"
|
|
|
|
static char **
|
|
zcurses_pairs_to_array(const struct zcurses_namenumberpair *nnps)
|
|
{
|
|
char **arr, **arrptr;
|
|
int count;
|
|
const struct zcurses_namenumberpair *nnptr;
|
|
|
|
for (nnptr = nnps; nnptr->name; nnptr++)
|
|
;
|
|
count = nnptr - nnps;
|
|
|
|
arrptr = arr = (char **)zhalloc((count+1) * sizeof(char *));
|
|
|
|
for (nnptr = nnps; nnptr->name; nnptr++)
|
|
*arrptr++ = dupstring(nnptr->name);
|
|
*arrptr = NULL;
|
|
|
|
return arr;
|
|
}
|
|
|
|
static const char *
|
|
zcurses_strerror(int err)
|
|
{
|
|
static const char *errs[] = {
|
|
"unknown error",
|
|
"window name invalid",
|
|
"window already defined",
|
|
"window undefined",
|
|
NULL };
|
|
|
|
return errs[(err < 1 || err > 3) ? 0 : err];
|
|
}
|
|
|
|
static LinkNode
|
|
zcurses_getwindowbyname(const char *name)
|
|
{
|
|
LinkNode node;
|
|
ZCWin w;
|
|
|
|
for (node = firstnode(zcurses_windows); node; incnode(node))
|
|
if (w = (ZCWin)getdata(node), !strcmp(w->name, name))
|
|
return node;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static LinkNode
|
|
zcurses_validate_window(char *win, int criteria)
|
|
{
|
|
LinkNode target;
|
|
|
|
if (win==NULL || strlen(win) < 1) {
|
|
zc_errno = ZCURSES_EINVALID;
|
|
return NULL;
|
|
}
|
|
|
|
target = zcurses_getwindowbyname(win);
|
|
|
|
if (target && (criteria & ZCURSES_UNUSED)) {
|
|
zc_errno = ZCURSES_EDEFINED;
|
|
return NULL;
|
|
}
|
|
|
|
if (!target && (criteria & ZCURSES_USED)) {
|
|
zc_errno = ZCURSES_EUNDEFINED;
|
|
return NULL;
|
|
}
|
|
|
|
zc_errno = 0;
|
|
return target;
|
|
}
|
|
|
|
static int
|
|
zcurses_free_window(ZCWin w)
|
|
{
|
|
if (!(w->flags & ZCWF_PERMANENT) && delwin(w->win)!=OK)
|
|
return 1;
|
|
|
|
if (w->name)
|
|
zsfree(w->name);
|
|
|
|
if (w->children)
|
|
freelinklist(w->children, (FreeFunc)NULL);
|
|
|
|
zfree(w, sizeof(struct zc_win));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct zcurses_namenumberpair *
|
|
zcurses_attrget(UNUSED(WINDOW *w), char *attr)
|
|
{
|
|
struct zcurses_namenumberpair *zca;
|
|
|
|
if (!attr)
|
|
return NULL;
|
|
|
|
for(zca=(struct zcurses_namenumberpair *)zcurses_attributes;zca->name;zca++)
|
|
if (!strcmp(attr, zca->name)) {
|
|
return zca;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static short
|
|
zcurses_color(const char *color)
|
|
{
|
|
struct zcurses_namenumberpair *zc;
|
|
|
|
for(zc=(struct zcurses_namenumberpair *)zcurses_colors;zc->name;zc++)
|
|
if (!strcmp(color, zc->name)) {
|
|
return (short)zc->number;
|
|
}
|
|
|
|
return (short)-2;
|
|
}
|
|
|
|
static Colorpairnode
|
|
zcurses_colorget(const char *nam, char *colorpair)
|
|
{
|
|
char *bg, *cp;
|
|
short f, b;
|
|
Colorpairnode cpn;
|
|
|
|
/* zcurses_colorpairs is only initialised if color is supported */
|
|
if (!zcurses_colorpairs)
|
|
return NULL;
|
|
|
|
if (zc_color_phase==1 ||
|
|
!(cpn = (Colorpairnode) gethashnode2(zcurses_colorpairs, colorpair))) {
|
|
zc_color_phase = 2;
|
|
cp = ztrdup(colorpair);
|
|
|
|
bg = strchr(cp, '/');
|
|
if (bg==NULL) {
|
|
zsfree(cp);
|
|
return NULL;
|
|
}
|
|
|
|
*bg = '\0';
|
|
|
|
// cp/bg can be {number}/{number} or {name}/{name}
|
|
|
|
if( cp[0] >= '0' && cp[0] <= '9' ) {
|
|
f = atoi(cp);
|
|
} else {
|
|
f = zcurses_color(cp);
|
|
}
|
|
|
|
if( (bg+1)[0] >= '0' && (bg+1)[0] <= '9' ) {
|
|
b = atoi(bg+1);
|
|
} else {
|
|
b = zcurses_color(bg+1);
|
|
}
|
|
|
|
if (f==-2 || b==-2) {
|
|
if (f == -2)
|
|
zwarnnam(nam, "foreground color `%s' not known", cp);
|
|
if (b == -2)
|
|
zwarnnam(nam, "background color `%s' not known", bg+1);
|
|
*bg = '/';
|
|
zsfree(cp);
|
|
return NULL;
|
|
}
|
|
*bg = '/';
|
|
|
|
++next_cp;
|
|
if (next_cp >= COLOR_PAIRS || init_pair(next_cp, f, b) == ERR) {
|
|
zsfree(cp);
|
|
return NULL;
|
|
}
|
|
|
|
cpn = (Colorpairnode)zshcalloc(sizeof(struct colorpairnode));
|
|
|
|
if (!cpn) {
|
|
zsfree(cp);
|
|
return NULL;
|
|
}
|
|
|
|
cpn->colorpair = next_cp;
|
|
addhashnode(zcurses_colorpairs, cp, (void *)cpn);
|
|
}
|
|
|
|
return cpn;
|
|
}
|
|
|
|
static Colorpairnode cpn_match;
|
|
|
|
static void
|
|
zcurses_colornode(HashNode hn, int cp)
|
|
{
|
|
Colorpairnode cpn = (Colorpairnode)hn;
|
|
if (cpn->colorpair == (short)cp)
|
|
cpn_match = cpn;
|
|
}
|
|
|
|
static Colorpairnode
|
|
zcurses_colorget_reverse(short cp)
|
|
{
|
|
if (!zcurses_colorpairs)
|
|
return NULL;
|
|
|
|
cpn_match = NULL;
|
|
scanhashtable(zcurses_colorpairs, 0, 0, 0,
|
|
zcurses_colornode, cp);
|
|
return cpn_match;
|
|
}
|
|
|
|
static void
|
|
freecolorpairnode(HashNode hn)
|
|
{
|
|
zsfree(hn->nam);
|
|
zfree(hn, sizeof(struct colorpairnode));
|
|
}
|
|
|
|
|
|
/*************
|
|
* Subcommands
|
|
*************/
|
|
|
|
static int
|
|
zccmd_init(UNUSED(const char *nam), UNUSED(char **args))
|
|
{
|
|
LinkNode stdscr_win = zcurses_getwindowbyname("stdscr");
|
|
|
|
if (!stdscr_win) {
|
|
ZCWin w = (ZCWin)zshcalloc(sizeof(struct zc_win));
|
|
if (!w)
|
|
return 1;
|
|
|
|
gettyinfo(&saved_tty_state);
|
|
w->name = ztrdup("stdscr");
|
|
w->win = initscr();
|
|
if (w->win == NULL) {
|
|
zsfree(w->name);
|
|
zfree(w, sizeof(struct zc_win));
|
|
return 1;
|
|
}
|
|
w->flags = ZCWF_PERMANENT;
|
|
zinsertlinknode(zcurses_windows, lastnode(zcurses_windows), (void *)w);
|
|
if (start_color() != ERR) {
|
|
Colorpairnode cpn;
|
|
|
|
if(!zc_color_phase)
|
|
zc_color_phase = 1;
|
|
zcurses_colorpairs = newhashtable(8, "zc_colorpairs", NULL);
|
|
|
|
zcurses_colorpairs->hash = hasher;
|
|
zcurses_colorpairs->emptytable = emptyhashtable;
|
|
zcurses_colorpairs->filltable = NULL;
|
|
zcurses_colorpairs->cmpnodes = strcmp;
|
|
zcurses_colorpairs->addnode = addhashnode;
|
|
zcurses_colorpairs->getnode = gethashnode2;
|
|
zcurses_colorpairs->getnode2 = gethashnode2;
|
|
zcurses_colorpairs->removenode = removehashnode;
|
|
zcurses_colorpairs->disablenode = NULL;
|
|
zcurses_colorpairs->enablenode = NULL;
|
|
zcurses_colorpairs->freenode = freecolorpairnode;
|
|
zcurses_colorpairs->printnode = NULL;
|
|
|
|
#ifdef HAVE_USE_DEFAULT_COLORS
|
|
use_default_colors();
|
|
#endif
|
|
/* Initialise the default color pair, always 0 */
|
|
cpn = (Colorpairnode)zshcalloc(sizeof(struct colorpairnode));
|
|
if (cpn) {
|
|
cpn->colorpair = 0;
|
|
addhashnode(zcurses_colorpairs,
|
|
ztrdup("default/default"), (void *)cpn);
|
|
}
|
|
}
|
|
/*
|
|
* We use cbreak mode because we don't want line buffering
|
|
* on input since we'd just need to loop over characters.
|
|
* We use noecho since the manual says that's the right
|
|
* thing to do with cbreak.
|
|
*
|
|
* Turn these on immediately to catch typeahead.
|
|
*/
|
|
cbreak();
|
|
noecho();
|
|
gettyinfo(&curses_tty_state);
|
|
} else {
|
|
settyinfo(&curses_tty_state);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
zccmd_addwin(const char *nam, char **args)
|
|
{
|
|
int nlines, ncols, begin_y, begin_x;
|
|
ZCWin w;
|
|
|
|
if (zcurses_validate_window(args[0], ZCURSES_UNUSED) == NULL &&
|
|
zc_errno) {
|
|
zerrnam(nam, "%s: %s", zcurses_strerror(zc_errno), args[0], 0);
|
|
return 1;
|
|
}
|
|
|
|
nlines = atoi(args[1]);
|
|
ncols = atoi(args[2]);
|
|
begin_y = atoi(args[3]);
|
|
begin_x = atoi(args[4]);
|
|
|
|
w = (ZCWin)zshcalloc(sizeof(struct zc_win));
|
|
if (!w)
|
|
return 1;
|
|
|
|
w->name = ztrdup(args[0]);
|
|
if (args[5]) {
|
|
LinkNode node;
|
|
ZCWin worig;
|
|
|
|
node = zcurses_validate_window(args[5], ZCURSES_USED);
|
|
if (node == NULL) {
|
|
zwarnnam(nam, "%s: %s", zcurses_strerror(zc_errno), args[0],
|
|
0);
|
|
zsfree(w->name);
|
|
zfree(w, sizeof(struct zc_win));
|
|
return 1;
|
|
}
|
|
|
|
worig = (ZCWin)getdata(node);
|
|
|
|
w->win = subwin(worig->win, nlines, ncols, begin_y, begin_x);
|
|
if (w->win) {
|
|
w->parent = worig;
|
|
if (!worig->children)
|
|
worig->children = znewlinklist();
|
|
zinsertlinknode(worig->children, lastnode(worig->children),
|
|
(void *)w);
|
|
}
|
|
} else {
|
|
w->win = newwin(nlines, ncols, begin_y, begin_x);
|
|
}
|
|
|
|
if (w->win == NULL) {
|
|
zwarnnam(nam, "failed to create window `%s'", w->name);
|
|
zsfree(w->name);
|
|
zfree(w, sizeof(struct zc_win));
|
|
return 1;
|
|
}
|
|
|
|
zinsertlinknode(zcurses_windows, lastnode(zcurses_windows), (void *)w);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
zccmd_delwin(const char *nam, char **args)
|
|
{
|
|
LinkNode node;
|
|
ZCWin w;
|
|
int ret = 0;
|
|
|
|
node = zcurses_validate_window(args[0], ZCURSES_USED);
|
|
if (node == NULL) {
|
|
zwarnnam(nam, "%s: %s", zcurses_strerror(zc_errno), args[0]);
|
|
return 1;
|
|
}
|
|
|
|
w = (ZCWin)getdata(node);
|
|
|
|
if (w == NULL) {
|
|
zwarnnam(nam, "record for window `%s' is corrupt", args[0]);
|
|
return 1;
|
|
}
|
|
if (w->flags & ZCWF_PERMANENT) {
|
|
zwarnnam(nam, "window `%s' can't be deleted", args[0]);
|
|
return 1;
|
|
}
|
|
|
|
if (w->children && firstnode(w->children)) {
|
|
zwarnnam(nam, "window `%s' has subwindows, delete those first",
|
|
w->name);
|
|
return 1;
|
|
}
|
|
|
|
if (delwin(w->win)!=OK) {
|
|
/*
|
|
* Not sure what to do here, but we are probably stuffed,
|
|
* so delete the window locally anyway.
|
|
*/
|
|
ret = 1;
|
|
}
|
|
|
|
if (w->parent) {
|
|
/* Remove from parent's list of children */
|
|
LinkList wpc = w->parent->children;
|
|
LinkNode pcnode;
|
|
for (pcnode = firstnode(wpc); pcnode; incnode(pcnode)) {
|
|
ZCWin child = (ZCWin)getdata(pcnode);
|
|
if (child == w) {
|
|
remnode(wpc, pcnode);
|
|
break;
|
|
}
|
|
}
|
|
DPUTS(pcnode == NULL, "BUG: child node not found in parent's children");
|
|
/*
|
|
* We need to touch the parent to get the parent to refresh
|
|
* properly.
|
|
*/
|
|
touchwin(w->parent->win);
|
|
}
|
|
else
|
|
touchwin(stdscr);
|
|
|
|
if (w->name)
|
|
zsfree(w->name);
|
|
|
|
zfree((ZCWin)remnode(zcurses_windows, node), sizeof(struct zc_win));
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
zccmd_refresh(const char *nam, char **args)
|
|
{
|
|
WINDOW *win;
|
|
int ret = 0;
|
|
|
|
if (args[0]) {
|
|
for (; *args; args++) {
|
|
LinkNode node;
|
|
ZCWin w;
|
|
|
|
node = zcurses_validate_window(args[0], ZCURSES_USED);
|
|
if (node == NULL) {
|
|
zwarnnam(nam, "%s: %s", zcurses_strerror(zc_errno), args[0],
|
|
0);
|
|
return 1;
|
|
}
|
|
|
|
w = (ZCWin)getdata(node);
|
|
|
|
if (w->parent) {
|
|
/* This is what the manual says you have to do. */
|
|
touchwin(w->parent->win);
|
|
}
|
|
win = w->win;
|
|
if (wnoutrefresh(win) != OK)
|
|
ret = 1;
|
|
}
|
|
return (doupdate() != OK || ret);
|
|
}
|
|
else
|
|
{
|
|
return (wrefresh(stdscr) != OK) ? 1 : 0;
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
zccmd_move(const char *nam, char **args)
|
|
{
|
|
int y, x;
|
|
LinkNode node;
|
|
ZCWin w;
|
|
|
|
node = zcurses_validate_window(args[0], ZCURSES_USED);
|
|
if (node == NULL) {
|
|
zwarnnam(nam, "%s: %s", zcurses_strerror(zc_errno), args[0]);
|
|
return 1;
|
|
}
|
|
|
|
y = atoi(args[1]);
|
|
x = atoi(args[2]);
|
|
|
|
w = (ZCWin)getdata(node);
|
|
|
|
if (wmove(w->win, y, x)!=OK)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
zccmd_clear(const char *nam, char **args)
|
|
{
|
|
LinkNode node;
|
|
ZCWin w;
|
|
|
|
node = zcurses_validate_window(args[0], ZCURSES_USED);
|
|
if (node == NULL) {
|
|
zwarnnam(nam, "%s: %s", zcurses_strerror(zc_errno), args[0]);
|
|
return 1;
|
|
}
|
|
|
|
w = (ZCWin)getdata(node);
|
|
|
|
if (!args[1]) {
|
|
return werase(w->win) != OK;
|
|
} else if (!strcmp(args[1], "redraw")) {
|
|
return wclear(w->win) != OK;
|
|
} else if (!strcmp(args[1], "eol")) {
|
|
return wclrtoeol(w->win) != OK;
|
|
} else if (!strcmp(args[1], "bot")) {
|
|
return wclrtobot(w->win) != OK;
|
|
} else {
|
|
zwarnnam(nam, "`clear' expects `redraw', `eol' or `bot'");
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
zccmd_char(const char *nam, char **args)
|
|
{
|
|
LinkNode node;
|
|
ZCWin w;
|
|
#ifdef HAVE_SETCCHAR
|
|
wchar_t c;
|
|
cchar_t cc;
|
|
#endif
|
|
|
|
node = zcurses_validate_window(args[0], ZCURSES_USED);
|
|
if (node == NULL) {
|
|
zwarnnam(nam, "%s: %s", zcurses_strerror(zc_errno), args[0]);
|
|
return 1;
|
|
}
|
|
|
|
w = (ZCWin)getdata(node);
|
|
|
|
#ifdef HAVE_SETCCHAR
|
|
if (mbrtowc(&c, args[1], MB_CUR_MAX, NULL) < 1)
|
|
return 1;
|
|
|
|
if (setcchar(&cc, &c, A_NORMAL, 0, NULL)==ERR)
|
|
return 1;
|
|
|
|
if (wadd_wch(w->win, &cc)!=OK)
|
|
return 1;
|
|
#else
|
|
if (waddch(w->win, (chtype)args[1][0])!=OK)
|
|
return 1;
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
zccmd_string(const char *nam, char **args)
|
|
{
|
|
LinkNode node;
|
|
ZCWin w;
|
|
|
|
#ifdef HAVE_WADDWSTR
|
|
int clen;
|
|
wint_t wc;
|
|
wchar_t *wstr, *wptr;
|
|
char *str = args[1];
|
|
#endif
|
|
|
|
node = zcurses_validate_window(args[0], ZCURSES_USED);
|
|
if (node == NULL) {
|
|
zwarnnam(nam, "%s: %s", zcurses_strerror(zc_errno), args[0]);
|
|
return 1;
|
|
}
|
|
|
|
w = (ZCWin)getdata(node);
|
|
|
|
#ifdef HAVE_WADDWSTR
|
|
mb_charinit();
|
|
wptr = wstr = zhalloc((strlen(str)+1) * sizeof(wchar_t));
|
|
|
|
while (*str && (clen = mb_metacharlenconv(str, &wc))) {
|
|
str += clen;
|
|
if (wc == WEOF) /* TODO: replace with space? nicen? */
|
|
continue;
|
|
*wptr++ = wc;
|
|
}
|
|
*wptr++ = L'\0';
|
|
if (waddwstr(w->win, wstr)!=OK) {
|
|
return 1;
|
|
}
|
|
#else
|
|
if (waddstr(w->win, args[1])!=OK)
|
|
return 1;
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
zccmd_border(const char *nam, char **args)
|
|
{
|
|
LinkNode node;
|
|
ZCWin w;
|
|
|
|
node = zcurses_validate_window(args[0], ZCURSES_USED);
|
|
if (node == NULL) {
|
|
zwarnnam(nam, "%s: %s", zcurses_strerror(zc_errno), args[0]);
|
|
return 1;
|
|
}
|
|
|
|
w = (ZCWin)getdata(node);
|
|
|
|
if (wborder(w->win, 0, 0, 0, 0, 0, 0, 0, 0)!=OK)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
zccmd_endwin(UNUSED(const char *nam), UNUSED(char **args))
|
|
{
|
|
LinkNode stdscr_win = zcurses_getwindowbyname("stdscr");
|
|
|
|
if (stdscr_win) {
|
|
endwin();
|
|
/* Restore TTY as it was before zcurses -i */
|
|
settyinfo(&saved_tty_state);
|
|
/*
|
|
* TODO: should I need the following? Without it
|
|
* the screen stays messed up. Presumably we are
|
|
* doing stuff with shttyinfo when we shouldn't really be.
|
|
*/
|
|
gettyinfo(&shttyinfo);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
zccmd_attr(const char *nam, char **args)
|
|
{
|
|
LinkNode node;
|
|
ZCWin w;
|
|
char **attrs;
|
|
int ret = 0;
|
|
|
|
if (!args[0])
|
|
return 1;
|
|
|
|
node = zcurses_validate_window(args[0], ZCURSES_USED);
|
|
if (node == NULL) {
|
|
zwarnnam(nam, "%s: %s", zcurses_strerror(zc_errno), args[0]);
|
|
return 1;
|
|
}
|
|
|
|
w = (ZCWin)getdata(node);
|
|
|
|
for(attrs = args+1; *attrs; attrs++) {
|
|
if (strchr(*attrs, '/')) {
|
|
Colorpairnode cpn;
|
|
if ((cpn = zcurses_colorget(nam, *attrs)) == NULL ||
|
|
wcolor_set(w->win, cpn->colorpair, NULL) == ERR)
|
|
ret = 1;
|
|
} else {
|
|
char *ptr;
|
|
int onoff;
|
|
struct zcurses_namenumberpair *zca;
|
|
|
|
switch(*attrs[0]) {
|
|
case '-':
|
|
onoff = ZCURSES_ATTROFF;
|
|
ptr = (*attrs) + 1;
|
|
break;
|
|
case '+':
|
|
onoff = ZCURSES_ATTRON;
|
|
ptr = (*attrs) + 1;
|
|
break;
|
|
default:
|
|
onoff = ZCURSES_ATTRON;
|
|
ptr = *attrs;
|
|
break;
|
|
}
|
|
if ((zca = zcurses_attrget(w->win, ptr)) == NULL) {
|
|
zwarnnam(nam, "attribute `%s' not known", ptr);
|
|
ret = 1;
|
|
} else {
|
|
switch(onoff) {
|
|
case ZCURSES_ATTRON:
|
|
if (wattron(w->win, zca->number) == ERR)
|
|
ret = 1;
|
|
break;
|
|
case ZCURSES_ATTROFF:
|
|
if (wattroff(w->win, zca->number) == ERR)
|
|
ret = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
zccmd_bg(const char *nam, char **args)
|
|
{
|
|
LinkNode node;
|
|
ZCWin w;
|
|
char **attrs;
|
|
int ret = 0;
|
|
chtype ch = 0;
|
|
|
|
if (!args[0])
|
|
return 1;
|
|
|
|
node = zcurses_validate_window(args[0], ZCURSES_USED);
|
|
if (node == NULL) {
|
|
zwarnnam(nam, "%s: %s", zcurses_strerror(zc_errno), args[0]);
|
|
return 1;
|
|
}
|
|
|
|
w = (ZCWin)getdata(node);
|
|
|
|
for(attrs = args+1; *attrs; attrs++) {
|
|
if (strchr(*attrs, '/')) {
|
|
Colorpairnode cpn;
|
|
if ((cpn = zcurses_colorget(nam, *attrs)) == NULL)
|
|
ret = 1;
|
|
else if (cpn->colorpair >= 256) {
|
|
/* pretty unlikely, but... */
|
|
zwarnnam(nam, "bg color pair %s has index (%d) too large (max 255)",
|
|
cpn->node.nam, cpn->colorpair);
|
|
ret = 1;
|
|
} else {
|
|
ch |= COLOR_PAIR(cpn->colorpair);
|
|
}
|
|
} else if (**attrs == '@') {
|
|
ch |= (*attrs)[1] == Meta ? (*attrs)[2] ^ 32 : (*attrs)[1];
|
|
} else {
|
|
char *ptr;
|
|
int onoff;
|
|
struct zcurses_namenumberpair *zca;
|
|
|
|
switch(*attrs[0]) {
|
|
case '-':
|
|
onoff = ZCURSES_ATTROFF;
|
|
ptr = (*attrs) + 1;
|
|
break;
|
|
case '+':
|
|
onoff = ZCURSES_ATTRON;
|
|
ptr = (*attrs) + 1;
|
|
break;
|
|
default:
|
|
onoff = ZCURSES_ATTRON;
|
|
ptr = *attrs;
|
|
break;
|
|
}
|
|
if ((zca = zcurses_attrget(w->win, ptr)) == NULL) {
|
|
zwarnnam(nam, "attribute `%s' not known", ptr);
|
|
ret = 1;
|
|
} else {
|
|
switch(onoff) {
|
|
case ZCURSES_ATTRON:
|
|
if (wattron(w->win, zca->number) == ERR)
|
|
ret = 1;
|
|
break;
|
|
case ZCURSES_ATTROFF:
|
|
if (wattroff(w->win, zca->number) == ERR)
|
|
ret = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ret == 0)
|
|
return wbkgd(w->win, ch) != OK;
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
zccmd_scroll(const char *nam, char **args)
|
|
{
|
|
LinkNode node;
|
|
ZCWin w;
|
|
int ret = 0;
|
|
|
|
node = zcurses_validate_window(args[0], ZCURSES_USED);
|
|
if (node == NULL) {
|
|
zwarnnam(nam, "%s: %s", zcurses_strerror(zc_errno), args[0]);
|
|
return 1;
|
|
}
|
|
|
|
w = (ZCWin)getdata(node);
|
|
|
|
if (!strcmp(args[1], "on")) {
|
|
if (scrollok(w->win, TRUE) == ERR)
|
|
return 1;
|
|
w->flags |= ZCWF_SCROLL;
|
|
} else if (!strcmp(args[1], "off")) {
|
|
if (scrollok(w->win, FALSE) == ERR)
|
|
return 1;
|
|
w->flags &= ~ZCWF_SCROLL;
|
|
} else {
|
|
char *endptr;
|
|
zlong sl = zstrtol(args[1], &endptr, 10);
|
|
if (*endptr) {
|
|
zwarnnam(nam, "scroll requires `on', `off' or integer: %s",
|
|
args[1]);
|
|
return 1;
|
|
}
|
|
if (!(w->flags & ZCWF_SCROLL))
|
|
scrollok(w->win, TRUE);
|
|
if (wscrl(w->win, (int)sl) == ERR)
|
|
ret = 1;
|
|
if (!(w->flags & ZCWF_SCROLL))
|
|
scrollok(w->win, FALSE);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
zccmd_input(const char *nam, char **args)
|
|
{
|
|
LinkNode node;
|
|
ZCWin w;
|
|
char *var;
|
|
int keypadnum = -1;
|
|
int nargs = arrlen(args);
|
|
#ifdef HAVE_WGET_WCH
|
|
int ret;
|
|
wint_t wi;
|
|
VARARR(char, instr, 2*MB_CUR_MAX+1);
|
|
#else
|
|
int ci;
|
|
char instr[3];
|
|
#endif
|
|
|
|
node = zcurses_validate_window(args[0], ZCURSES_USED);
|
|
if (node == NULL) {
|
|
zwarnnam(nam, "%s: %s", zcurses_strerror(zc_errno), args[0]);
|
|
return 1;
|
|
}
|
|
|
|
w = (ZCWin)getdata(node);
|
|
|
|
if (nargs >= 3) {
|
|
keypad(w->win, TRUE);
|
|
} else {
|
|
keypad(w->win, FALSE);
|
|
}
|
|
|
|
if (nargs >= 4) {
|
|
#ifdef NCURSES_MOUSE_VERSION
|
|
if (!(zcurses_flags & ZCF_MOUSE_ACTIVE) ||
|
|
(zcurses_flags & ZCF_MOUSE_MASK_CHANGED)) {
|
|
if (mousemask(zcurses_mouse_mask, NULL) == (mmask_t)ERR) {
|
|
zwarnnam(nam, "current mouse mode is not supported");
|
|
return 1;
|
|
}
|
|
zcurses_flags = (zcurses_flags & ~ZCF_MOUSE_MASK_CHANGED) |
|
|
ZCF_MOUSE_ACTIVE;
|
|
}
|
|
#else
|
|
zwarnnam(nam, "mouse events are not supported");
|
|
return 1;
|
|
#endif
|
|
}
|
|
#ifdef NCURSES_MOUSE_VERSION
|
|
else {
|
|
if (zcurses_flags & ZCF_MOUSE_ACTIVE) {
|
|
mousemask((mmask_t)0, NULL);
|
|
zcurses_flags &= ~ZCF_MOUSE_ACTIVE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Linux, OS X, FreeBSD documentation for wgetch() mentions:
|
|
|
|
Programmers concerned about portability should be prepared for either
|
|
of two cases: (a) signal receipt does not interrupt getch; (b) signal
|
|
receipt interrupts getch and causes it to return ERR with errno set to
|
|
EINTR. Under the ncurses implementation, handled signals never inter-
|
|
rupt getch.
|
|
|
|
* Some observed behavior: wgetch() returns ERR with EINTR when a signal is
|
|
* handled by the shell "trap" command mechanism. Observed that it returns
|
|
* ERR twice, the second time without even attempting to repeat the
|
|
* interrupted read. Third call will then begin reading again.
|
|
*
|
|
* Because of widespread of previous implementation that called wget*ch
|
|
* possibly indefinitely many times after ERR/EINTR, and because of the
|
|
* above observation, wget_wch call is repeated after each ERR/EINTR, but
|
|
* errno is being reset (it wasn't) and the loop to all means should break.
|
|
* Problem: the timeout may be waited twice.
|
|
*/
|
|
errno = 0;
|
|
|
|
#ifdef HAVE_WGET_WCH
|
|
while ((ret = wget_wch(w->win, &wi)) == ERR) {
|
|
if (errno != EINTR || errflag || retflag || breaks || exit_pending)
|
|
break;
|
|
errno = 0;
|
|
}
|
|
switch (ret) {
|
|
case OK:
|
|
ret = wctomb(instr, (wchar_t)wi);
|
|
if (ret == 0) {
|
|
instr[0] = Meta;
|
|
instr[1] = '\0' ^ 32;
|
|
instr[2] = '\0';
|
|
} else {
|
|
(void)metafy(instr, ret, META_NOALLOC);
|
|
}
|
|
break;
|
|
|
|
case KEY_CODE_YES:
|
|
*instr = '\0';
|
|
keypadnum = (int)wi;
|
|
break;
|
|
|
|
case ERR:
|
|
default:
|
|
return 1;
|
|
}
|
|
#else
|
|
while ((ci = wgetch(w->win)) == ERR) {
|
|
if (errno != EINTR || errflag || retflag || breaks || exit_pending)
|
|
return 1;
|
|
errno = 0;
|
|
}
|
|
if (ci >= 256) {
|
|
keypadnum = ci;
|
|
*instr = '\0';
|
|
} else {
|
|
if (imeta(ci)) {
|
|
instr[0] = Meta;
|
|
instr[1] = (char)ci ^ 32;
|
|
instr[2] = '\0';
|
|
} else {
|
|
instr[0] = (char)ci;
|
|
instr[1] = '\0';
|
|
}
|
|
}
|
|
#endif
|
|
if (args[1])
|
|
var = args[1];
|
|
else
|
|
var = "REPLY";
|
|
if (!setsparam(var, ztrdup(instr)))
|
|
return 1;
|
|
if (nargs >= 3) {
|
|
if (keypadnum > 0) {
|
|
#ifdef NCURSES_MOUSE_VERSION
|
|
if (nargs >= 4 && keypadnum == KEY_MOUSE) {
|
|
MEVENT mevent;
|
|
char digits[DIGBUFSIZE];
|
|
LinkList margs;
|
|
const struct zcurses_mouse_event *zcmmp = zcurses_mouse_map;
|
|
|
|
if (!setsparam(args[2], ztrdup("MOUSE")))
|
|
return 1;
|
|
if (getmouse(&mevent) == ERR) {
|
|
/*
|
|
* This may happen if the mouse wasn't in
|
|
* the window, so set the array to empty
|
|
* but return success unless the set itself
|
|
* failed.
|
|
*/
|
|
return !setaparam(args[3], mkarray(NULL));
|
|
}
|
|
margs = newlinklist();
|
|
sprintf(digits, "%d", (int)mevent.id);
|
|
addlinknode(margs, dupstring(digits));
|
|
sprintf(digits, "%d", mevent.x);
|
|
addlinknode(margs, dupstring(digits));
|
|
sprintf(digits, "%d", mevent.y);
|
|
addlinknode(margs, dupstring(digits));
|
|
sprintf(digits, "%d", mevent.z);
|
|
addlinknode(margs, dupstring(digits));
|
|
|
|
/*
|
|
* We only expect one event, but it doesn't hurt
|
|
* to keep testing.
|
|
*/
|
|
for (; zcmmp->button; zcmmp++) {
|
|
if (mevent.bstate & zcmmp->event) {
|
|
const struct zcurses_namenumberpair *zcmelp =
|
|
zcurses_mouse_event_list;
|
|
for (; zcmelp->name; zcmelp++) {
|
|
if (zcmelp->number == zcmmp->what) {
|
|
char *evstr = zhalloc(strlen(zcmelp->name)+2);
|
|
sprintf(evstr, "%s%d", zcmelp->name,
|
|
zcmmp->button);
|
|
addlinknode(margs, evstr);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (mevent.bstate & BUTTON_SHIFT)
|
|
addlinknode(margs, "SHIFT");
|
|
if (mevent.bstate & BUTTON_CTRL)
|
|
addlinknode(margs, "CTRL");
|
|
if (mevent.bstate & BUTTON_ALT)
|
|
addlinknode(margs, "ALT");
|
|
if (!setaparam(args[3], zlinklist2array(margs, 1)))
|
|
return 1;
|
|
} else {
|
|
#endif
|
|
const struct zcurses_namenumberpair *nnptr;
|
|
char fbuf[DIGBUFSIZE+1];
|
|
|
|
for (nnptr = keypad_names; nnptr->name; nnptr++) {
|
|
if (keypadnum == nnptr->number) {
|
|
if (!setsparam(args[2], ztrdup(nnptr->name)))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
}
|
|
if (keypadnum > KEY_F0) {
|
|
/* assume it's a function key */
|
|
sprintf(fbuf, "F%d", keypadnum - KEY_F0);
|
|
} else {
|
|
/* print raw number */
|
|
sprintf(fbuf, "%d", keypadnum);
|
|
}
|
|
if (!setsparam(args[2], ztrdup(fbuf)))
|
|
return 1;
|
|
#ifdef NCURSES_MOUSE_VERSION
|
|
}
|
|
#endif
|
|
} else {
|
|
if (!setsparam(args[2], ztrdup("")))
|
|
return 1;
|
|
}
|
|
}
|
|
#ifdef NCURSES_MOUSE_VERSION
|
|
if (keypadnum != KEY_MOUSE && nargs >= 4)
|
|
return !setaparam(args[3], mkarray(NULL));
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
zccmd_timeout(const char *nam, char **args)
|
|
{
|
|
LinkNode node;
|
|
ZCWin w;
|
|
int to;
|
|
char *eptr;
|
|
|
|
node = zcurses_validate_window(args[0], ZCURSES_USED);
|
|
if (node == NULL) {
|
|
zwarnnam(nam, "%s: %s", zcurses_strerror(zc_errno), args[0]);
|
|
return 1;
|
|
}
|
|
|
|
w = (ZCWin)getdata(node);
|
|
|
|
to = (int)zstrtol(args[1], &eptr, 10);
|
|
if (*eptr) {
|
|
zwarnnam(nam, "timeout requires an integer: %s", args[1]);
|
|
return 1;
|
|
}
|
|
|
|
#if defined(__sun__) && defined(__SVR4) && !defined(HAVE_USE_DEFAULT_COLORS)
|
|
/*
|
|
* On Solaris turning a timeout off seems to be problematic.
|
|
* The following fixes it. We test for Solaris without ncurses
|
|
* (the last test) to be specific; this may turn up in other older
|
|
* versions of curses, but it's difficult to test for.
|
|
*/
|
|
if (to < 0) {
|
|
nocbreak();
|
|
cbreak();
|
|
}
|
|
#endif
|
|
wtimeout(w->win, to);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
zccmd_mouse(const char *nam, char **args)
|
|
{
|
|
#ifdef NCURSES_MOUSE_VERSION
|
|
int ret = 0;
|
|
|
|
for (; *args; args++) {
|
|
if (!strcmp(*args, "delay")) {
|
|
char *eptr;
|
|
zlong delay;
|
|
|
|
if (!*++args ||
|
|
((delay = zstrtol(*args, &eptr, 10)), *eptr != '\0')) {
|
|
zwarnnam(nam, "mouse delay requires an integer argument");
|
|
return 1;
|
|
}
|
|
if (mouseinterval((int)delay) != OK)
|
|
ret = 1;
|
|
} else {
|
|
char *arg = *args;
|
|
int onoff = 1;
|
|
if (*arg == '+')
|
|
arg++;
|
|
else if (*arg == '-') {
|
|
arg++;
|
|
onoff = 0;
|
|
}
|
|
if (!strcmp(arg, "motion")) {
|
|
mmask_t old_mask = zcurses_mouse_mask;
|
|
if (onoff)
|
|
zcurses_mouse_mask |= REPORT_MOUSE_POSITION;
|
|
else
|
|
zcurses_mouse_mask &= ~REPORT_MOUSE_POSITION;
|
|
if (old_mask != zcurses_mouse_mask)
|
|
zcurses_flags |= ZCF_MOUSE_MASK_CHANGED;
|
|
} else {
|
|
zwarnnam(nam, "unrecognised mouse command: %s", arg);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
#else
|
|
return 1;
|
|
#endif
|
|
}
|
|
|
|
|
|
static int
|
|
zccmd_position(const char *nam, char **args)
|
|
{
|
|
LinkNode node;
|
|
ZCWin w;
|
|
int i, intarr[6];
|
|
char **array, dbuf[DIGBUFSIZE];
|
|
|
|
node = zcurses_validate_window(args[0], ZCURSES_USED);
|
|
if (node == NULL) {
|
|
zwarnnam(nam, "%s: %s", zcurses_strerror(zc_errno), args[0]);
|
|
return 1;
|
|
}
|
|
|
|
w = (ZCWin)getdata(node);
|
|
|
|
/* Look no pointers: these are macros. */
|
|
getyx(w->win, intarr[0], intarr[1]);
|
|
if (intarr[0] == -1)
|
|
return 1;
|
|
getbegyx(w->win, intarr[2], intarr[3]);
|
|
if (intarr[2] == -1)
|
|
return 1;
|
|
getmaxyx(w->win, intarr[4], intarr[5]);
|
|
if (intarr[4] == -1)
|
|
return 1;
|
|
|
|
array = (char **)zalloc(7*sizeof(char *));
|
|
for (i = 0; i < 6; i++) {
|
|
sprintf(dbuf, "%d", intarr[i]);
|
|
array[i] = ztrdup(dbuf);
|
|
}
|
|
array[6] = NULL;
|
|
|
|
setaparam(args[1], array);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
zccmd_querychar(const char *nam, char **args)
|
|
{
|
|
LinkNode node;
|
|
ZCWin w;
|
|
short cp;
|
|
Colorpairnode cpn;
|
|
const struct zcurses_namenumberpair *zattrp;
|
|
LinkList clist;
|
|
#if defined(HAVE_WIN_WCH) && defined(HAVE_GETCCHAR)
|
|
attr_t attrs;
|
|
wchar_t c;
|
|
cchar_t cc;
|
|
int count;
|
|
VARARR(char, instr, 2*MB_CUR_MAX+1);
|
|
#else
|
|
chtype inc, attrs;
|
|
char instr[3];
|
|
#endif
|
|
|
|
node = zcurses_validate_window(args[0], ZCURSES_USED);
|
|
if (node == NULL) {
|
|
zwarnnam(nam, "%s: %s", zcurses_strerror(zc_errno), args[0]);
|
|
return 1;
|
|
}
|
|
|
|
w = (ZCWin)getdata(node);
|
|
|
|
#if defined(HAVE_WIN_WCH) && defined(HAVE_GETCCHAR)
|
|
if (win_wch(w->win, &cc) == ERR)
|
|
return 1;
|
|
|
|
if (getcchar(&cc, &c, &attrs, &cp, NULL) == ERR)
|
|
return 1;
|
|
/* Hmmm... I always get 0 for cp, whereas the following works... */
|
|
cp = PAIR_NUMBER(winch(w->win));
|
|
|
|
count = wctomb(instr, c);
|
|
if (count == -1)
|
|
return 1;
|
|
(void)metafy(instr, count, META_NOALLOC);
|
|
#else
|
|
inc = winch(w->win);
|
|
/* I think the following is correct, the manual is a little terse */
|
|
cp = PAIR_NUMBER(inc);
|
|
inc &= A_CHARTEXT;
|
|
if (imeta(inc)) {
|
|
instr[0] = Meta;
|
|
instr[1] = (unsigned char) (inc ^ 32);
|
|
instr[2] = '\0';
|
|
} else {
|
|
instr[0] = (unsigned char) inc;
|
|
instr[1] = '\0';
|
|
}
|
|
attrs = inc;
|
|
#endif
|
|
|
|
/*
|
|
* Attribute numbers vary, so make a linked list.
|
|
* This also saves us from doing the permanent allocation till
|
|
* the end.
|
|
*/
|
|
clist = newlinklist();
|
|
/* First the (possibly multibyte) character itself. */
|
|
addlinknode(clist, instr);
|
|
/*
|
|
* Next the colo[u]r.
|
|
* We should be able to match it in the colorpair list, but
|
|
* if some reason we can't, fail safe and output the number.
|
|
*/
|
|
cpn = zcurses_colorget_reverse(cp);
|
|
if (cpn) {
|
|
addlinknode(clist, cpn->node.nam);
|
|
} else {
|
|
/* report color pair number */
|
|
char digits[DIGBUFSIZE];
|
|
sprintf(digits, "%d", (int)cp);
|
|
addlinknode(clist, digits);
|
|
}
|
|
/* Now see what attributes are present. */
|
|
for (zattrp = zcurses_attributes; zattrp->name; zattrp++) {
|
|
if (attrs & zattrp->number)
|
|
addlinknode(clist, zattrp->name);
|
|
}
|
|
|
|
/* Turn this into an array and store it. */
|
|
return !setaparam(args[1] ? args[1] : "reply", zlinklist2array(clist, 1));
|
|
}
|
|
|
|
|
|
static int
|
|
zccmd_touch(const char *nam, char **args)
|
|
{
|
|
LinkNode node;
|
|
ZCWin w;
|
|
int ret = 0;
|
|
|
|
for (; *args; args++) {
|
|
node = zcurses_validate_window(args[0], ZCURSES_USED);
|
|
if (node == NULL) {
|
|
zwarnnam(nam, "%s: %s", zcurses_strerror(zc_errno), args[0]);
|
|
return 1;
|
|
}
|
|
|
|
w = (ZCWin)getdata(node);
|
|
if (touchwin(w->win) != OK)
|
|
ret = 1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
zccmd_resize(const char *nam, char **args)
|
|
{
|
|
#ifdef HAVE_RESIZE_TERM
|
|
int y, x, do_endwin=0, do_save=1;
|
|
LinkNode stdscr_win = zcurses_getwindowbyname("stdscr");
|
|
|
|
if (stdscr_win) {
|
|
y = atoi(args[0]);
|
|
x = atoi(args[1]);
|
|
if (args[2]) {
|
|
if (0 == strcmp(args[2], "endwin")) {
|
|
do_endwin=1;
|
|
} else if (0 == strcmp(args[2], "endwin_nosave")) {
|
|
do_endwin=1;
|
|
do_save=0;
|
|
} else if (0 == strcmp(args[2], "nosave")) {
|
|
do_save=0;
|
|
} else {
|
|
zwarnnam(nam, "`resize' expects `endwin', `nosave' or `endwin_nosave' for third argument, if given");
|
|
}
|
|
}
|
|
|
|
if (y == 0 && x == 0 && args[2] == NULL) {
|
|
// Special case to just test that curses has resize_term. #ifdef
|
|
// HAVE_RESIZE_TERM will result in return value 2 if resize_term
|
|
// is not available.
|
|
return 0;
|
|
} else {
|
|
// Without this call some window moves are inaccurate. Tested on
|
|
// OS X ncurses 5.4, Homebrew ncursesw 6.0-2, Arch Linux ncursesw
|
|
// 6.0, Ubuntu 14.04 ncurses 5.9, FreeBSD ncursesw.so.8
|
|
//
|
|
// On the other hand, the whole resize goal can be (from tests)
|
|
// accomplished by calling endwin and refresh. But to secure any
|
|
// future problems, resize_term is provided, and it is featured
|
|
// with endwin, so that users have multiple options.
|
|
if (do_endwin) {
|
|
endwin();
|
|
}
|
|
|
|
if( resize_term( y, x ) == OK ) {
|
|
// Things work without this, but we need to get out from
|
|
// endwin (i.e. call refresh), and in theory store new
|
|
// curses state (the resize might have changed it), which
|
|
// should be presented to terminal only after refresh.
|
|
if (do_endwin || do_save) {
|
|
ZCWin w;
|
|
w = (ZCWin)getdata(stdscr_win);
|
|
wnoutrefresh(w->win);
|
|
doupdate();
|
|
}
|
|
|
|
if (do_save) {
|
|
gettyinfo(&curses_tty_state);
|
|
}
|
|
return 0;
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
} else {
|
|
return 1;
|
|
}
|
|
#else
|
|
return 2;
|
|
#endif
|
|
}
|
|
|
|
/*********************
|
|
Main builtin handler
|
|
*********************/
|
|
|
|
/**/
|
|
static int
|
|
bin_zcurses(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
|
|
{
|
|
char **saargs;
|
|
struct zcurses_subcommand *zcsc;
|
|
int num_args;
|
|
|
|
struct zcurses_subcommand scs[] = {
|
|
{"init", zccmd_init, 0, 0},
|
|
{"addwin", zccmd_addwin, 5, 6},
|
|
{"delwin", zccmd_delwin, 1, 1},
|
|
{"refresh", zccmd_refresh, 0, -1},
|
|
{"move", zccmd_move, 3, 3},
|
|
{"clear", zccmd_clear, 1, 2},
|
|
{"position", zccmd_position, 2, 2},
|
|
{"char", zccmd_char, 2, 2},
|
|
{"string", zccmd_string, 2, 2},
|
|
{"border", zccmd_border, 1, 1},
|
|
{"end", zccmd_endwin, 0, 0},
|
|
{"attr", zccmd_attr, 2, -1},
|
|
{"bg", zccmd_bg, 2, -1},
|
|
{"scroll", zccmd_scroll, 2, 2},
|
|
{"input", zccmd_input, 1, 4},
|
|
{"timeout", zccmd_timeout, 2, 2},
|
|
{"mouse", zccmd_mouse, 0, -1},
|
|
{"querychar", zccmd_querychar, 1, 2},
|
|
{"touch", zccmd_touch, 1, -1},
|
|
{"resize", zccmd_resize, 2, 3},
|
|
{NULL, (zccmd_t)0, 0, 0}
|
|
};
|
|
|
|
for(zcsc = scs; zcsc->name; zcsc++) {
|
|
if(!strcmp(args[0], zcsc->name))
|
|
break;
|
|
}
|
|
|
|
if (zcsc->name == NULL) {
|
|
zwarnnam(nam, "unknown subcommand: %s", args[0]);
|
|
return 1;
|
|
}
|
|
|
|
saargs = args;
|
|
while (*saargs++);
|
|
num_args = saargs - (args + 2);
|
|
|
|
if (num_args < zcsc->minargs) {
|
|
zwarnnam(nam, "too few arguments for subcommand: %s", args[0]);
|
|
return 1;
|
|
} else if (zcsc->maxargs >= 0 && num_args > zcsc->maxargs) {
|
|
zwarnnam(nam, "too many arguments for subcommand: %s", args[0]);
|
|
return 1;
|
|
}
|
|
|
|
if (zcsc->cmd != zccmd_init && zcsc->cmd != zccmd_endwin &&
|
|
!zcurses_getwindowbyname("stdscr")) {
|
|
zwarnnam(nam, "command `%s' can't be used before `zcurses init'",
|
|
zcsc->name);
|
|
return 1;
|
|
}
|
|
|
|
return zcsc->cmd(nam, args+1);
|
|
}
|
|
|
|
|
|
static struct builtin bintab[] = {
|
|
BUILTIN("zcurses", 0, bin_zcurses, 1, -1, 0, "", NULL),
|
|
};
|
|
|
|
|
|
/*******************
|
|
* Special variables
|
|
*******************/
|
|
|
|
static char **
|
|
zcurses_colorsarrgetfn(UNUSED(Param pm))
|
|
{
|
|
return zcurses_pairs_to_array(zcurses_colors);
|
|
}
|
|
|
|
static const struct gsu_array zcurses_colorsarr_gsu =
|
|
{ zcurses_colorsarrgetfn, arrsetfn, stdunsetfn };
|
|
|
|
|
|
static char **
|
|
zcurses_attrgetfn(UNUSED(Param pm))
|
|
{
|
|
return zcurses_pairs_to_array(zcurses_attributes);
|
|
}
|
|
|
|
static const struct gsu_array zcurses_attrs_gsu =
|
|
{ zcurses_attrgetfn, arrsetfn, stdunsetfn };
|
|
|
|
|
|
static char **
|
|
zcurses_keycodesgetfn(UNUSED(Param pm))
|
|
{
|
|
return zcurses_pairs_to_array(keypad_names);
|
|
}
|
|
|
|
static const struct gsu_array zcurses_keycodes_gsu =
|
|
{ zcurses_keycodesgetfn, arrsetfn, stdunsetfn };
|
|
|
|
|
|
static char **
|
|
zcurses_windowsgetfn(UNUSED(Param pm))
|
|
{
|
|
LinkNode node;
|
|
char **arr, **arrptr;
|
|
int count = countlinknodes(zcurses_windows);
|
|
|
|
arrptr = arr = (char **)zhalloc((count+1) * sizeof(char *));
|
|
|
|
for (node = firstnode(zcurses_windows); node; incnode(node))
|
|
*arrptr++ = dupstring(((ZCWin)getdata(node))->name);
|
|
*arrptr = NULL;
|
|
|
|
return arr;
|
|
}
|
|
|
|
static const struct gsu_array zcurses_windows_gsu =
|
|
{ zcurses_windowsgetfn, arrsetfn, stdunsetfn };
|
|
|
|
|
|
static zlong
|
|
zcurses_colorsintgetfn(UNUSED(Param pm))
|
|
{
|
|
return COLORS;
|
|
}
|
|
|
|
static const struct gsu_integer zcurses_colorsint_gsu =
|
|
{ zcurses_colorsintgetfn, nullintsetfn, stdunsetfn };
|
|
|
|
|
|
static zlong
|
|
zcurses_colorpairsintgetfn(UNUSED(Param pm))
|
|
{
|
|
return COLOR_PAIRS;
|
|
}
|
|
|
|
static const struct gsu_integer zcurses_colorpairsint_gsu =
|
|
{ zcurses_colorpairsintgetfn, nullintsetfn, stdunsetfn };
|
|
|
|
|
|
static struct paramdef partab[] = {
|
|
SPECIALPMDEF("zcurses_colors", PM_ARRAY|PM_READONLY,
|
|
&zcurses_colorsarr_gsu, NULL, NULL),
|
|
SPECIALPMDEF("zcurses_attrs", PM_ARRAY|PM_READONLY,
|
|
&zcurses_attrs_gsu, NULL, NULL),
|
|
SPECIALPMDEF("zcurses_keycodes", PM_ARRAY|PM_READONLY,
|
|
&zcurses_keycodes_gsu, NULL, NULL),
|
|
SPECIALPMDEF("zcurses_windows", PM_ARRAY|PM_READONLY,
|
|
&zcurses_windows_gsu, NULL, NULL),
|
|
SPECIALPMDEF("ZCURSES_COLORS", PM_INTEGER|PM_READONLY,
|
|
&zcurses_colorsint_gsu, NULL, NULL),
|
|
SPECIALPMDEF("ZCURSES_COLOR_PAIRS", PM_INTEGER|PM_READONLY,
|
|
&zcurses_colorpairsint_gsu, NULL, NULL)
|
|
};
|
|
|
|
/***************************
|
|
* Standard module interface
|
|
***************************/
|
|
|
|
|
|
/*
|
|
* boot_ is executed when the module is loaded.
|
|
*/
|
|
|
|
static struct features module_features = {
|
|
bintab, sizeof(bintab)/sizeof(*bintab),
|
|
NULL, 0,
|
|
NULL, 0,
|
|
partab, sizeof(partab)/sizeof(*partab),
|
|
0
|
|
};
|
|
|
|
/**/
|
|
int
|
|
setup_(UNUSED(Module m))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/**/
|
|
int
|
|
features_(Module m, char ***features)
|
|
{
|
|
*features = featuresarray(m, &module_features);
|
|
return 0;
|
|
}
|
|
|
|
/**/
|
|
int
|
|
enables_(Module m, int **enables)
|
|
{
|
|
return handlefeatures(m, &module_features, enables);
|
|
}
|
|
|
|
/**/
|
|
int
|
|
boot_(UNUSED(Module m))
|
|
{
|
|
zcurses_windows = znewlinklist();
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**/
|
|
int
|
|
cleanup_(Module m)
|
|
{
|
|
freelinklist(zcurses_windows, (FreeFunc) zcurses_free_window);
|
|
if (zcurses_colorpairs)
|
|
deletehashtable(zcurses_colorpairs);
|
|
return setfeatureenables(m, &module_features, NULL);
|
|
}
|
|
|
|
/**/
|
|
int
|
|
finish_(UNUSED(Module m))
|
|
{
|
|
return 0;
|
|
}
|