[GB.NCURSES]

* NEW: Add ReadLine() function to Window that does the same as ncurses
  getstr(3X) but usable in all input modes
* NEW: Encapsulate Input, Cursor and Border modes into static objects
* NEW: Tidy up Color class in various places
* NEW: Specifiying color values is now done via floats instead of ints
* NEW: Remove ContainerW and ContainerH (and corresponding long forms)
  properties from Window class. It is always .Width/.Height + 2
* NEW: Rename Input.Repeater to Input.RepeatDelay
* OPT: Remove unnecessary ncurses input mode changes
* OPT: Tidy up Input and Screen code
* OPT: Reduce useless calls to input queue read callback in Screen class
* BUG: Add constant for already implemented "very visible" cursor mode
* BUG: Use opaque input module in Window.Ask()
* BUG: Note that NoDelay and Window/Screen events still not work



git-svn-id: svn://localhost/gambas/trunk@4827 867c0c6c-44f3-4631-809d-bfa615b0a4ec
This commit is contained in:
Tobias Boege 2012-06-13 15:47:00 +00:00
parent 35b390e5e1
commit 0a4dd6f9d2
11 changed files with 822 additions and 598 deletions

View file

@ -13,5 +13,5 @@ gb_ncurses_la_SOURCES = \
c_key.h c_key.c \ c_key.h c_key.c \
c_color.h c_color.c \ c_color.h c_color.c \
c_screen.h c_screen.c \ c_screen.h c_screen.c \
input.h input.c c_input.h c_input.c

View file

@ -29,8 +29,8 @@
#include "c_color.h" #include "c_color.h"
#include "c_screen.h" #include "c_screen.h"
static int _index;
static int _color; static int _color;
static int _pair;
/** /**
* Colour initialisation * Colour initialisation
@ -41,6 +41,50 @@ void COLOR_init()
use_default_colors(); use_default_colors();
} }
/**
* Initialise a colour
* @index: the color number
* @r: red
* @g: green
* @b: blue
* Note that one gives colour amounts in floats 0.0 - 1.0
*/
int COLOR_setcolor(short index, float r, float g, float b)
{
return init_color(index, r * 1000, g * 1000, b * 1000);
}
/**
* Initialise one field of a colour
* @index: color number
* @val: value as float 0.0 - 1.0
* @what: field number
*/
int COLOR_setcolor_one(short index, float val, int what)
{
short r, g, b;
float rf, gf, bf;
color_content(index, &r, &g, &b);
rf = ((float) r) / 1000;
gf = ((float) g) / 1000;
bf = ((float) b) / 1000;
switch (what) {
case 0:
rf = val;
break;
case 1:
gf = val;
break;
case 2:
bf = val;
break;
default:
return -1;
}
return COLOR_setcolor(index, rf, gf, bf);
}
/** /**
* Initialise a colour pair * Initialise a colour pair
* @index: colour pair index * @index: colour pair index
@ -105,81 +149,120 @@ static int COLOR_content(short color, short *r, short *g, short *b)
* Color static class * Color static class
*/ */
BEGIN_PROPERTY(Color_Capabilities) BEGIN_PROPERTY(Color_Available)
RETURN_SELF(); GB.ReturnBoolean(has_colors());
END_PROPERTY END_PROPERTY
BEGIN_PROPERTY(Color_Colors) BEGIN_PROPERTY(Color_CanChange)
GB.ReturnBoolean(can_change_color());
END_PROPERTY
BEGIN_PROPERTY(Color_Count)
GB.ReturnInteger(COLORS); GB.ReturnInteger(COLORS);
END_PROPERTY END_PROPERTY
BEGIN_PROPERTY(Color_Pairs)
GB.ReturnInteger(COLOR_PAIRS);
END_PROPERTY
BEGIN_METHOD(Color_get, GB_INTEGER index) BEGIN_METHOD(Color_get, GB_INTEGER index)
if (!PAIR_VALID(VARG(index))) { if (!COLOR_VALID(VARG(index))) {
GB.Error(GB_ERR_BOUND); GB.Error(GB_ERR_BOUND);
return; return;
} }
_index = VARG(index); _color = VARG(index);
RETURN_SELF(); RETURN_SELF();
END_METHOD END_METHOD
BEGIN_METHOD(Color_Define, GB_INTEGER color; GB_INTEGER r; GB_INTEGER g; GB_INTEGER b) BEGIN_METHOD(Color_Set, GB_INTEGER color; GB_INTEGER r; GB_INTEGER g; GB_INTEGER b)
if (!COLOR_VALID(VARG(color))) { if (!COLOR_VALID(VARG(color))) {
GB.Error(GB_ERR_BOUND); GB.Error(GB_ERR_BOUND);
return; return;
} }
init_color(VARG(color), VARG(r), VARG(g), VARG(b)); init_color(VARG(color), VARG(r), VARG(g), VARG(b));
REFRESH();
END_METHOD END_METHOD
BEGIN_METHOD(Color_Content, GB_INTEGER color) /*
* .ColorInfo virtual class
*/
if (!COLOR_VALID(VARG(color))) { BEGIN_PROPERTY(ColorInfo_Red)
short red;
if (READ_PROPERTY) {
COLOR_content(_color, &red, NULL, NULL);
GB.ReturnInteger(red);
return;
}
COLOR_setcolor_one(_color, VPROP(GB_FLOAT), 0);
REAL_REFRESH();
END_PROPERTY
BEGIN_PROPERTY(ColorInfo_Green)
short green;
if (READ_PROPERTY) {
COLOR_content(_color, NULL, &green, NULL);
GB.ReturnInteger(green);
return;
}
COLOR_setcolor_one(_color, VPROP(GB_FLOAT), 1);
END_PROPERTY
BEGIN_PROPERTY(ColorInfo_Blue)
short blue;
if (READ_PROPERTY) {
COLOR_content(_color, NULL, NULL, &blue);
GB.ReturnInteger(blue);
return;
}
COLOR_setcolor_one(_color, VPROP(GB_FLOAT), 2);
END_PROPERTY
/*
* Pair static class
*/
BEGIN_PROPERTY(Pair_Count)
GB.ReturnInteger(COLOR_PAIRS);
END_PROPERTY
BEGIN_METHOD(Pair_get, GB_INTEGER index)
if (!PAIR_VALID(VARG(index))) {
GB.Error(GB_ERR_BOUND); GB.Error(GB_ERR_BOUND);
return; return;
} }
_color = VARG(color); _pair = VARG(index);
RETURN_SELF(); RETURN_SELF();
END_METHOD END_METHOD
/* /*
* .Color.Capabilities virtual class * .PairInfo virtual class
*/ */
BEGIN_PROPERTY(ColorCapabilities_Change) BEGIN_PROPERTY(PairInfo_Background)
GB.ReturnBoolean(can_change_color());
END_PROPERTY
BEGIN_PROPERTY(ColorCapabilities_Color)
GB.ReturnBoolean(has_colors());
END_PROPERTY
/*
* .Color.Pair virtual class
*/
BEGIN_PROPERTY(ColorPair_Background)
short f, b; short f, b;
pair_content(_index, &f, &b); pair_content(_pair, &f, &b);
if (READ_PROPERTY) { if (READ_PROPERTY) {
GB.ReturnInteger(b); GB.ReturnInteger(b);
return; return;
@ -189,17 +272,17 @@ BEGIN_PROPERTY(ColorPair_Background)
return; return;
} }
b = VPROP(GB_INTEGER); b = VPROP(GB_INTEGER);
COLOR_setpair(_index, f, b); COLOR_setpair(_pair, f, b);
REAL_REFRESH(); REAL_REFRESH();
END_PROPERTY END_PROPERTY
BEGIN_PROPERTY(ColorPair_Foreground) BEGIN_PROPERTY(PairInfo_Foreground)
short f, b; short f, b;
pair_content(_index, &f, &b); pair_content(_pair, &f, &b);
if (READ_PROPERTY) { if (READ_PROPERTY) {
GB.ReturnInteger(f); GB.ReturnInteger(f);
return; return;
@ -209,44 +292,12 @@ BEGIN_PROPERTY(ColorPair_Foreground)
return; return;
} }
f = VPROP(GB_INTEGER); f = VPROP(GB_INTEGER);
COLOR_setpair(_index, f, b); COLOR_setpair(_pair, f, b);
REAL_REFRESH(); REAL_REFRESH();
END_PROPERTY END_PROPERTY
/* GB_DESC CColorDesc[] = {
* .Color.Content virtual class
*/
BEGIN_PROPERTY(ColorContent_Red)
short red;
COLOR_content(_color, &red, NULL, NULL);
GB.ReturnInteger(red);
END_PROPERTY
BEGIN_PROPERTY(ColorContent_Green)
short green;
COLOR_content(_color, NULL, &green, NULL);
GB.ReturnInteger(green);
END_PROPERTY
BEGIN_PROPERTY(ColorContent_Blue)
short blue;
COLOR_content(_color, NULL, NULL, &blue);
GB.ReturnInteger(blue);
END_PROPERTY
GB_DESC CColorDesc[] =
{
GB_DECLARE("Color", 0), GB_DECLARE("Color", 0),
GB_NOT_CREATABLE(), GB_NOT_CREATABLE(),
@ -259,47 +310,44 @@ GB_DESC CColorDesc[] =
GB_CONSTANT("Cyan", "i", COLOR_CYAN), GB_CONSTANT("Cyan", "i", COLOR_CYAN),
GB_CONSTANT("White", "i", COLOR_WHITE), GB_CONSTANT("White", "i", COLOR_WHITE),
GB_STATIC_PROPERTY_READ("Capabilities", ".Color.Capabilities", Color_Capabilities), GB_STATIC_PROPERTY_READ("Available", "b", Color_Available),
GB_STATIC_PROPERTY_READ("Colors", "i", Color_Colors), GB_STATIC_PROPERTY_READ("CanChange", "b", Color_CanChange),
GB_STATIC_PROPERTY_READ("Pairs", "i", Color_Pairs), GB_STATIC_PROPERTY_READ("Count", "i", Color_Count),
GB_STATIC_METHOD("_get", ".Color.Pair", Color_get, "(Index)i"), GB_STATIC_METHOD("_get", ".ColorInfo", Color_get, "(Index)i"),
GB_STATIC_METHOD("Define", NULL, Color_Define, "(Color)i(R)i(G)i(B)i"), GB_STATIC_METHOD("Set", NULL, Color_Set, "(Color)i(R)i(G)i(B)i"),
GB_STATIC_METHOD("Content", ".Color.Content", Color_Content, "(Color)i"),
GB_END_DECLARE GB_END_DECLARE
}; };
GB_DESC CColorCapabilitiesDesc[] = GB_DESC CColorInfoDesc[] = {
{ GB_DECLARE(".ColorInfo", 0),
GB_DECLARE(".Color.Capabilities", 0),
GB_VIRTUAL_CLASS(), GB_VIRTUAL_CLASS(),
GB_STATIC_PROPERTY_READ("Color", "b", ColorCapabilities_Color), GB_STATIC_PROPERTY("Red", "i", ColorInfo_Red),
GB_STATIC_PROPERTY_READ("Change", "b", ColorCapabilities_Change), GB_STATIC_PROPERTY("Green", "i", ColorInfo_Green),
GB_STATIC_PROPERTY("Blue", "i", ColorInfo_Blue),
GB_END_DECLARE GB_END_DECLARE
}; };
GB_DESC CColorPairDesc[] = GB_DESC CPairDesc[] = {
{ GB_DECLARE("Pair", 0),
GB_DECLARE(".Color.Pair", 0), GB_NOT_CREATABLE(),
GB_STATIC_PROPERTY_READ("Count", "i", Pair_Count),
GB_STATIC_METHOD("_get", ".PairInfo", Pair_get, "(Index)i"),
GB_END_DECLARE
};
GB_DESC CPairInfoDesc[] = {
GB_DECLARE(".PairInfo", 0),
GB_VIRTUAL_CLASS(), GB_VIRTUAL_CLASS(),
GB_STATIC_PROPERTY("Background", "i", ColorPair_Background), GB_STATIC_PROPERTY("Background", "i", PairInfo_Background),
GB_STATIC_PROPERTY("Foreground", "i", ColorPair_Foreground), GB_STATIC_PROPERTY("Foreground", "i", PairInfo_Foreground),
GB_END_DECLARE
};
GB_DESC CColorContentDesc[] =
{
GB_DECLARE(".Color.Content", 0),
GB_VIRTUAL_CLASS(),
GB_STATIC_PROPERTY_READ("Red", "i", ColorContent_Red),
GB_STATIC_PROPERTY_READ("Green", "i", ColorContent_Green),
GB_STATIC_PROPERTY_READ("Blue", "i", ColorContent_Blue),
GB_END_DECLARE GB_END_DECLARE
}; };

View file

@ -8,9 +8,9 @@
#ifndef __C_COLOR_C #ifndef __C_COLOR_C
extern GB_DESC CColorDesc[]; extern GB_DESC CColorDesc[];
extern GB_DESC CColorCapabilitiesDesc[]; extern GB_DESC CColorInfoDesc[];
extern GB_DESC CColorPairDesc[]; extern GB_DESC CPairDesc[];
extern GB_DESC CColorContentDesc[]; extern GB_DESC CPairInfoDesc[];
#endif #endif
enum { enum {
@ -19,7 +19,9 @@ enum {
}; };
void COLOR_init(); void COLOR_init();
int COLOR_setpair(short index, short fg, short bg); int COLOR_setcolor(short, float, float, float);
int COLOR_setpair_one(short index, short val, int what); int COLOR_setcolor_one(short, float, int);
int COLOR_setpair(short, short, short);
int COLOR_setpair_one(short, short, int);
#endif /* __C_COLOR_H */ #endif /* __C_COLOR_H */

View file

@ -1,5 +1,5 @@
/* /*
* input.c - gb.ncurses opaque input routines * c_input.c - gb.ncurses opaque input routines
* *
* Copyright (C) 2012 Tobias Boege <tobias@gambas-buch.de> * Copyright (C) 2012 Tobias Boege <tobias@gambas-buch.de>
* *
@ -19,7 +19,7 @@
* MA 02110-1301, USA. * MA 02110-1301, USA.
*/ */
#define __INPUT_C #define __C_INPUT_C
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
@ -36,32 +36,17 @@
#include "gb_common.h" #include "gb_common.h"
#include "main.h" #include "main.h"
#include "input.h" #include "c_input.h"
#include "c_window.h" #include "c_window.h"
#define E_UNSUPP "Unsupported value" #define E_UNSUPP "Unsupported value"
#define E_NO_NODELAY "Could not initialise NoDelay mode" #define E_NO_NODELAY "Could not initialise NoDelay mode"
/* True input mode is inherited from terminal, we need a surely invalid
* value to be reset upon initialisation */
static int _input = -1; static int _input = -1;
static char _watching = 0;
/* Note that this is not safe for functions that are used to change the mode
* settings, in particular INPUT_init_nodelay() and INPUT_exit_nodelay(),
* because @_input is updated after this function */
#define IN_NODELAY (_input == INPUT_NODELAY) #define IN_NODELAY (_input == INPUT_NODELAY)
static char _watch_fd = -1;
static struct {
struct {
struct termios term;
int kbmode;
void (*error_hook)();
} old;
int fd;
unsigned short pressed;
unsigned int delay;
GB_TIMER *timer;
} no_delay;
static char _exiting_nodelay = 0;
/** /**
* Input initialisation * Input initialisation
@ -69,7 +54,8 @@ static char _exiting_nodelay = 0;
int INPUT_init() int INPUT_init()
{ {
INPUT_mode(INPUT_CBREAK); INPUT_mode(INPUT_CBREAK);
INPUT_repeater_delay(100); INPUT_watch(0);
NODELAY_repeater_delay(1);
return 0; return 0;
} }
@ -84,179 +70,46 @@ void INPUT_exit()
} }
} }
/**
* Begin or stop watching the input queue in question depending on @_input
*/
static int INPUT_watch(char start)
{
int fd = IN_NODELAY ? no_delay.fd : 0;
if (!start && !_watching)
#define MY_DEBUG() fprintf(stderr, "in %s\n", __func__)
/**
* Begin watching the given fd (for read)
* @fd: fd to watch. -1 means to stop watching at all.
* This automatically stops watching the previously watched fd, if any
*/
static int INPUT_watch(int fd)
{
MY_DEBUG();
if (fd == _watch_fd)
return 0; return 0;
if (start && _watching)
INPUT_watch(!start); if (_watch_fd != -1)
GB.Watch(fd, start ? GB_WATCH_READ : GB_WATCH_NONE, GB.Watch(_watch_fd, GB_WATCH_NONE, NULL, 0);
INPUT_callback, 0); if (fd == -1)
_watching = start; return 0;
GB.Watch(fd, GB_WATCH_READ, INPUT_callback, 0);
_watch_fd = fd;
return 0; return 0;
} }
/** /**
* Function to be called by Gambas when data arrives * Function to be called by Gambas when data arrives
* Params currently not used
*/ */
static void INPUT_callback(int fd, int flag, intptr_t arg) static void INPUT_callback(int fd, int flag, intptr_t arg)
{ {
WINDOW_raise_read(NULL); MY_DEBUG();
}
/** /* if (IN_NODELAY)
* Return if the given fd can be used with console_ioctls NODELAY_change_pressed(NODELAY_get(-1));
* @fd: file descriptor to test else
* The idea was derived from "kbd" package, getfd.c, is_a_console() */ WINDOW_raise_read(NULL);
*/
static inline char is_cons(int fd)
{
char type;
if (fd != -1 && isatty(fd) && ioctl(fd, KDGKBTYPE, &type) != -1
&& (type == KB_101 || type == KB_84))
return 1;
return 0;
}
/**
* Returns an fd that can be used with console_ioctls or -1 if none
* available
*/
int INPUT_consolefd()
{
int fd;
if (is_cons(0))
return 0;
fd = open("/dev/tty", O_RDWR);
if (fd == -1)
return -1;
if (is_cons(fd))
return fd;
close(fd);
return -1;
}
/**
* Init NoDelay mode
* We save old settings and prepare the TTY driver and Gambas
*/
static int INPUT_init_nodelay()
{
int fd = INPUT_consolefd();
struct termios term;
if (fd == -1)
return -1;
/* TODO: implement switching between vts, need available signals to
* be sent */
tcgetattr(fd, &no_delay.old.term);
ioctl(fd, KDGKBMODE, &no_delay.old.kbmode);
memcpy(&term, &no_delay.old.term, sizeof(term));
term.c_lflag &= ~(ICANON | ECHO | ISIG);
term.c_iflag &= ~(ISTRIP | IGNCR | ICRNL | INLCR | IXOFF | IXON);
/* Have no timeout per default */
term.c_cc[VMIN] = 0;
term.c_cc[VTIME] = -1;
tcsetattr(fd, TCSAFLUSH, &term);
no_delay.old.error_hook = GB.Hook(GB_HOOK_ERROR,
INPUT_nodelay_error_hook);
no_delay.fd = fd;
no_delay.timer = NULL;
/* Switch to K_MEDIUMRAW now. Could not have been done on-the-fly
* when reading from the console fd, because our key repeat code
* relies on data maybe present on that fd (to determine if we can
* safely inject new events for the currently pressed key or shall
* examine if there is another keypress) and there wouldn't be
* anything if we switch on-the-fly */
ioctl(no_delay.fd, KDSKBMODE, K_MEDIUMRAW);
return 0;
}
/**
* Cleanup NoDelay mode
* Restore old settings
* This assumes that @_input reflects the current settings
*/
static int INPUT_exit_nodelay()
{
/* @_input must be updated after this function, if even, after this
* function */
if (!IN_NODELAY)
return 0;
if (_exiting_nodelay)
return 0;
_exiting_nodelay = 1;
ioctl(no_delay.fd, KDSKBMODE, no_delay.old.kbmode);
tcsetattr(no_delay.fd, TCSANOW, &no_delay.old.term);
GB.Hook(GB_HOOK_ERROR, no_delay.old.error_hook);
if (no_delay.timer)
GB.Unref((void **) &no_delay.timer);
close(no_delay.fd);
_exiting_nodelay = 0;
return 0;
}
/**
* The NoDelay mode error hook
* This calls the former error hook, saved by INPUT_init_nodelay() to not
* disturb any piece code
*/
static void INPUT_nodelay_error_hook()
{
if (_exiting_nodelay)
return;
INPUT_exit_nodelay();
no_delay.old.error_hook();
}
/**
* Return or set the repeater delay
* @val: value to set the delay to. This value must be at least 1 or an
* error is returned. If it is REPEATER_RETURN, the current value is
* returned to the caller.
* Note that this setting affects the repeater function itself, that gets
* called in this interval to generate events and the INPUT_get_nodelay()
* function which will wait to return the amount of milliseconds if it is to
* return the pressed key.
*/
int INPUT_repeater_delay(int val)
{
if (val == REPEATER_RETURN)
return no_delay.delay;
if (val < 1)
return -1;
no_delay.delay = (unsigned int) val;
return 0;
}
/**
* NoDelay mode event repeater
* Used to insert Window_Read events if there is a key pressed
*/
static int INPUT_nodelay_repeater()
{
fprintf(stderr, "here\n");
if (!no_delay.pressed)
return TRUE;
WINDOW_raise_read(NULL);
return FALSE;
} }
/** /**
@ -271,26 +124,21 @@ int INPUT_mode(int mode)
if (mode == _input) if (mode == _input)
return 0; return 0;
INPUT_watch(0); if (IN_NODELAY)
NODELAY_exit();
if (_input == INPUT_NODELAY)
INPUT_exit_nodelay();
switch (mode) { switch (mode) {
case INPUT_COOKED: case INPUT_COOKED:
noraw();
nocbreak(); nocbreak();
break; break;
case INPUT_CBREAK: case INPUT_CBREAK:
noraw();
cbreak(); cbreak();
break; break;
case INPUT_RAW: case INPUT_RAW:
nocbreak();
raw(); raw();
break; break;
case INPUT_NODELAY: case INPUT_NODELAY:
if (INPUT_init_nodelay() == -1) { if (NODELAY_init() == -1) {
GB.Error(E_NO_NODELAY); GB.Error(E_NO_NODELAY);
/* We return 0 to not override the previous /* We return 0 to not override the previous
* error message with the one emitted by the * error message with the one emitted by the
@ -304,11 +152,20 @@ int INPUT_mode(int mode)
} }
_input = mode; _input = mode;
INPUT_watch(1);
return 0; return 0;
} }
/**
* Drain the input queue
*/
void INPUT_drain()
{
if (IN_NODELAY)
NODELAY_drain();
else
flushinp();
}
/** /**
* Retrieve input from ncurses * Retrieve input from ncurses
*/ */
@ -332,8 +189,261 @@ static int INPUT_get_ncurses(int timeout)
return ret; return ret;
} }
/**
* Get a keypress within the given timeout
* @timeout: number of milliseconds to wait. If no key is pressed during it,
* 0 will be returned.
*/
int INPUT_get(int timeout)
{
if (IN_NODELAY)
return NODELAY_get(timeout);
else
return INPUT_get_ncurses(timeout);
}
BEGIN_PROPERTY(Input_IsConsole)
int fd = NODELAY_consolefd();
if (fd == -1) {
GB.ReturnBoolean(FALSE);
return;
}
close(fd);
GB.ReturnBoolean(TRUE);
END_PROPERTY
BEGIN_PROPERTY(Input_RepeatDelay)
if (READ_PROPERTY) {
GB.ReturnInteger(NODELAY_repeater_delay(REPEATER_RETURN));
return;
}
if (NODELAY_repeater_delay(VPROP(GB_INTEGER)) == -1) {
GB.Error("Invalid value");
return;
}
END_PROPERTY
GB_DESC CInputDesc[] = {
GB_DECLARE("Input", 0),
GB_NOT_CREATABLE(),
GB_CONSTANT("NoTimeout", "i", TIMEOUT_NOTIMEOUT),
GB_CONSTANT("Cooked", "i", INPUT_COOKED),
GB_CONSTANT("CBreak", "i", INPUT_CBREAK),
GB_CONSTANT("Raw", "i", INPUT_RAW),
GB_CONSTANT("NoDelay", "i", INPUT_NODELAY),
GB_STATIC_PROPERTY_READ("IsConsole", "b", Input_IsConsole),
GB_STATIC_PROPERTY("RepeatDelay", "i", Input_RepeatDelay),
};
/* /*
* Return codes from INPUT_trans_keycode() * NODELAY routines
*/
static struct {
struct {
struct termios term;
int kbmode;
void (*error_hook)();
} old;
int fd;
unsigned short pressed;
unsigned int delay;
GB_TIMER *timer;
} no_delay;
static char _exiting_nodelay = 0;
/**
* Init NoDelay mode
* We save old settings and prepare the TTY driver and Gambas
* Precisely:
* - (TTY driver:)
* - Save all related values
* - Set terminal to (ncurses) raw()-like input mode as base for NoDelay
* - Set keyboard to K_MEDIUMRAW mode to see key make and break codes
* - (Gambas:)
* - Install specific error hook
* - Reset repeater timer
* - Begin watching console fd
*/
static int NODELAY_init()
{
int fd = NODELAY_consolefd();
struct termios term;
if (fd == -1)
return -1;
/* TODO: implement switching between vts, need available signals to
* be sent */
tcgetattr(fd, &no_delay.old.term);
ioctl(fd, KDGKBMODE, &no_delay.old.kbmode);
memcpy(&term, &no_delay.old.term, sizeof(term));
term.c_lflag &= ~(ICANON | ECHO | ISIG);
term.c_iflag &= ~(ISTRIP | IGNCR | ICRNL | INLCR | IXOFF | IXON);
/* Have no timeout per default */
term.c_cc[VMIN] = 0;
term.c_cc[VTIME] = TIMEOUT_NOTIMEOUT;
tcsetattr(fd, TCSAFLUSH, &term);
no_delay.old.error_hook = GB.Hook(GB_HOOK_ERROR,
NODELAY_error_hook);
no_delay.fd = fd;
no_delay.timer = NULL;
ioctl(no_delay.fd, KDSKBMODE, K_MEDIUMRAW);
INPUT_watch(fd);
return 0;
}
/**
* Cleanup NoDelay mode
* Restore old settings, see NODELAY_init() for details
* This function gets called by our error hook and the error hook is removed
* here. When removing a hook, it gets automatically called: Hence
* @_exiting_nodelay
*/
static int NODELAY_exit()
{
if (_exiting_nodelay)
return 0;
_exiting_nodelay = 1;
INPUT_watch(0);
ioctl(no_delay.fd, KDSKBMODE, no_delay.old.kbmode);
if (no_delay.timer)
GB.Unref((void **) &no_delay.timer);
GB.Hook(GB_HOOK_ERROR, no_delay.old.error_hook);
tcsetattr(no_delay.fd, TCSANOW, &no_delay.old.term);
close(no_delay.fd);
_exiting_nodelay = 0;
return 0;
}
/**
* The NoDelay mode error hook
* This calls the former error hook, saved by NODELAY_init() to not
* disturb any piece code
*/
static void NODELAY_error_hook()
{
if (_exiting_nodelay)
return;
NODELAY_exit();
no_delay.old.error_hook();
}
/**
* Return if the given fd can be used with console_ioctls
* @fd: file descriptor to test
* The idea was derived from "kbd" package, getfd.c, is_a_console()
*/
static inline char NODELAY_is_cons(int fd)
{
char type;
if (fd != -1 && isatty(fd) && ioctl(fd, KDGKBTYPE, &type) != -1
&& (type == KB_101 || type == KB_84))
return 1;
return 0;
}
/**
* Returns an fd that can be used with console_ioctls or -1 if none
* available
*/
static int NODELAY_consolefd()
{
int fd;
if (NODELAY_is_cons(0))
return 0;
fd = open("/dev/tty", O_RDWR);
if (fd == -1)
return -1;
if (NODELAY_is_cons(fd))
return fd;
close(fd);
return -1;
}
/**
* Drain NoDelay input queue
*/
static inline void NODELAY_drain()
{
tcflush(no_delay.fd, TCIFLUSH);
}
/**
* Return or set the repeater delay
* @val: value to set the delay to. This value must be at least 1 or an
* error is returned. If it is REPEATER_RETURN, the current value is
* returned to the caller.
* Note that this setting affects the repeater function itself, that gets
* called in this interval to generate events and the INPUT_get_nodelay()
* function which will wait to return the amount of milliseconds if it is to
* return the pressed key.
*/
static int NODELAY_repeater_delay(int val)
{
if (val == REPEATER_RETURN)
return no_delay.delay;
if (val < 1)
return -1;
no_delay.delay = (unsigned int) val;
return 0;
}
/**
* Post callback to insert new read event during next event loop.
* This is important because the NODELAY_repeater() gets called by the
* timer. If we raise an event from there, the event handler may destroy the
* timer we are currently in by issuing a NODELAY_change_pressed(). This has
* consequently be done outside the timer tick.
* @arg: unused
*/
static void NODELAY_post_read(intptr_t arg)
{
WINDOW_raise_read(NULL);
}
/**
* NoDelay mode event repeater. This function is the timer callback
* Used to insert Window_Read events if there is a key pressed
*/
static int NODELAY_repeater()
{
MY_DEBUG();
if (!no_delay.pressed)
return TRUE;
GB.Post(NODELAY_post_read, 0);
return FALSE;
}
/*
* Return codes from NODELAY_trans_keycode()
*/ */
enum { enum {
TRANS_NEED_MORE = -1, TRANS_NEED_MORE = -1,
@ -366,19 +476,23 @@ enum {
* we exploit ncurses key_defined() routine to translate the sequence to an * we exploit ncurses key_defined() routine to translate the sequence to an
* int for us. This gives an int like ncurses getch() would do. * int for us. This gives an int like ncurses getch() would do.
*/ */
static int INPUT_trans_keycode(unsigned char kc) static int NODELAY_trans_keycode(unsigned char kc)
{ {
/* Pause/Break has the largest scancode: e1 1d 45 e1 9d c5 */ /* Pause/Break has the largest scancode: e1 1d 45 e1 9d c5 */
static unsigned char codes[8]; static unsigned char codes[8];
static int num = 0; static int num = 0;
static int modifiers = MOD_NONE; static int modifiers = MOD_NONE;
unsigned char seq[8];
struct kbentry kbe; struct kbentry kbe;
struct kbsentry kbs; struct kbsentry kbs;
register int mod; register int mod;
/* TODO: Hope they're fixed */
MY_DEBUG();
#define KEYCODE_LCTRL 0x1d #define KEYCODE_LCTRL 0x1d
#define KEYCODE_RCTRL 0x61 #define KEYCODE_RCTRL 0x61
#define KEYCODE_ALT 0x38 #define KEYCODE_ALT 0x38
@ -417,7 +531,7 @@ account_key:
return TRANS_NEED_MORE; return TRANS_NEED_MORE;
} }
/* Keys with two keycodes, no matter, those correspond to /* Keys with two keycodes, no matter, those correspond to
* single-keycode keys, we can safely use @kc */ * single-keycode keys, we can safely use @kc != 0xe0 */
if (codes[0] == '\xe0' && num != 2) if (codes[0] == '\xe0' && num != 2)
return TRANS_NEED_MORE; return TRANS_NEED_MORE;
@ -437,35 +551,39 @@ account_key:
kbe.kb_index = kc & 0x7f; kbe.kb_index = kc & 0x7f;
kbe.kb_value = 0; kbe.kb_value = 0;
ioctl(no_delay.fd, KDGKBENT, &kbe); ioctl(no_delay.fd, KDGKBENT, &kbe);
seq[0] = (unsigned char) kbe.kb_value;
/* Has an escape sequence? */ /* Has an escape sequence? */
kbs.kb_func = seq[0]; kbs.kb_func = (unsigned char) kbe.kb_value;
ioctl(no_delay.fd, KDGKBSENT, &kbs); ioctl(no_delay.fd, KDGKBSENT, &kbs);
if (kbs.kb_string[0]) { if (kbs.kb_string[0])
strncpy((char *) seq, (char *) kbs.kb_string, 7); return key_defined((char *) kbs.kb_string);
seq[7] = '\0'; else
return key_defined((char *) seq); return (int) ((unsigned char) kbe.kb_value);
} else {
return (int) seq[0];
}
} }
/** /**
* Change the currently pressed key * Change the currently pressed key
* @key: current key. 0 means that no key is pressed * @key: current key. 0 means that no key is pressed
* This install the repeater * This installs the repeater
*/ */
static void INPUT_change_pressed(int key) static void NODELAY_change_pressed(int key)
{ {
fprintf(stderr, "%d\n", no_delay.delay);
MY_DEBUG();
/* if (key == no_delay.pressed)
return;
if (key == 0) { if (key == 0) {
GB.Unref((void **) no_delay.timer); GB.Unref((void **) no_delay.timer);
no_delay.timer = NULL;
} else { } else {
no_delay.timer = GB.Every(no_delay.delay, no_delay.timer = GB.Every(no_delay.delay,
(GB_TIMER_CALLBACK) INPUT_nodelay_repeater, 0); (GB_TIMER_CALLBACK) NODELAY_repeater, 0);
} }
no_delay.pressed = key; */ no_delay.pressed = key;
//NODELAY_repeater();
WINDOW_raise_read(NULL);
} }
/** /**
@ -480,7 +598,7 @@ static void INPUT_change_pressed(int key)
* Note carefully, that there is an emergency exit: pressing ESC thrice * Note carefully, that there is an emergency exit: pressing ESC thrice
* during one second will immediately abort NoDelay mode and enter CBreak. * during one second will immediately abort NoDelay mode and enter CBreak.
*/ */
static int INPUT_get_nodelay(int timeout) static int NODELAY_get(int timeout)
{ {
static char esc = 0; static char esc = 0;
struct termios old, new; struct termios old, new;
@ -490,6 +608,13 @@ static int INPUT_get_nodelay(int timeout)
int key; /* This will already be ncurses compatible */ int key; /* This will already be ncurses compatible */
struct timeval tv1, tv2; struct timeval tv1, tv2;
MY_DEBUG();
if (timeout > -1) { if (timeout > -1) {
gettimeofday(&tv1, NULL); gettimeofday(&tv1, NULL);
tcgetattr(no_delay.fd, &old); tcgetattr(no_delay.fd, &old);
@ -543,26 +668,26 @@ recalc_timeout:
if (time(NULL) - stamp > 0) if (time(NULL) - stamp > 0)
esc = 0; esc = 0;
if (++esc == 3) { if (++esc == 3) {
INPUT_exit_nodelay(); NODELAY_exit();
INPUT_mode(INPUT_CBREAK); INPUT_mode(INPUT_CBREAK);
return 0; return 0;
} }
stamp = time(NULL); stamp = time(NULL);
} }
/* We use ncurses keys for operations on our no_delay data */ /* We use ncurses keys for operations on our no_delay data */
if ((key = INPUT_trans_keycode(b)) == TRANS_NEED_MORE) if ((key = NODELAY_trans_keycode(b)) == TRANS_NEED_MORE)
goto recalc_timeout; goto recalc_timeout;
/* Ignore break codes, except when it is the currently /* Ignore break codes, except when it is the currently
pressed key */ pressed key */
if (IS_BREAK(b)) { if (IS_BREAK(b)) {
if (no_delay.pressed == key) if (no_delay.pressed == key)
INPUT_change_pressed(0); // NODELAY_change_pressed(0);
/* Key release isn't visible to the gambas programmer /* Key release isn't visible to the gambas programmer
* and thus not really an event to gb.ncurses. If * and thus not really an event to gb.ncurses. If
* time is left, we try again reading another key */ * time is left, we try again reading another key */
goto recalc_timeout; goto recalc_timeout;
} else { } else {
INPUT_change_pressed(key); // NODELAY_change_pressed(key);
} }
} }
@ -573,16 +698,3 @@ cleanup:
tcsetattr(no_delay.fd, TCSANOW, &old); tcsetattr(no_delay.fd, TCSANOW, &old);
return ret; return ret;
} }
/**
* Get a keypress within the given timeout
* @timeout: number of milliseconds to wait. If no key is pressed during it,
* 0 will be returned.
*/
int INPUT_get(int timeout)
{
if (_input == INPUT_NODELAY)
return INPUT_get_nodelay(timeout);
else
return INPUT_get_ncurses(timeout);
}

View file

@ -1,5 +1,5 @@
/* /*
* input.h - gb.ncurses opaque input routines for use of either ncurses or * c_input.h - gb.ncurses opaque input routines for use of either ncurses or
* the terminal driver directly * the terminal driver directly
* *
* Copyright (C) 2012 Tobias Boege <tobias@gambas-buch.de> * Copyright (C) 2012 Tobias Boege <tobias@gambas-buch.de>
@ -20,12 +20,12 @@
* MA 02110-1301, USA. * MA 02110-1301, USA.
*/ */
#ifndef __INPUT_H #ifndef __C_INPUT_H
#define __INPUT_H #define __C_INPUT_H
enum { enum {
/* Return the current mode */ /* Return the current mode */
INPUT_RETURN, INPUT_RETURN = -1,
/* Line discipline, signal generation */ /* Line discipline, signal generation */
INPUT_COOKED, INPUT_COOKED,
/* No line discipline, signal generation */ /* No line discipline, signal generation */
@ -39,23 +39,34 @@ enum {
}; };
enum { enum {
/* Let INPUT_repeater_delay() return the current value of the TIMEOUT_NOTIMEOUT = -1
repeater delay */ };
enum {
REPEATER_RETURN = -1 REPEATER_RETURN = -1
}; };
int INPUT_init(); #ifndef __C_INPUT_C
void INPUT_exit(); extern GB_DESC CInputDesc[];
int INPUT_consolefd();
int INPUT_mode(int mode);
int INPUT_get(int timeout);
int INPUT_repeater_delay(int val);
#ifdef __INPUT_C
static int INPUT_init_nodelay();
static int INPUT_exit_nodelay();
static void INPUT_nodelay_error_hook();
static int INPUT_nodelay_repeater();
static void INPUT_callback(int fd, int flag, intptr_t arg);
#endif #endif
#endif /* __INPUT_H */ int INPUT_init();
void INPUT_exit();
int INPUT_mode(int);
int INPUT_get(int);
void INPUT_drain();
#ifdef __C_INPUT_C
static int INPUT_watch(int);
static void INPUT_callback(int, int, intptr_t);
static int NODELAY_init();
static int NODELAY_exit();
static void NODELAY_error_hook();
static int NODELAY_consolefd();
static inline void NODELAY_drain();
static int NODELAY_repeater();
static int NODELAY_repeater_delay(int);
static void NODELAY_change_pressed(int);
static int NODELAY_get(int);
#endif
#endif /* __C_INPUT_H */

View file

@ -34,8 +34,7 @@ BEGIN_METHOD(Key_get, GB_STRING key)
END_METHOD END_METHOD
GB_DESC CKeyDesc[] = GB_DESC CKeyDesc[] = {
{
GB_DECLARE("Key", 0), GB_DECLARE("Key", 0),
GB_NOT_CREATABLE(), GB_NOT_CREATABLE(),

View file

@ -29,22 +29,18 @@
#include "gambas.h" #include "gambas.h"
#include "c_screen.h"
#include "main.h" #include "main.h"
#include "input.h" #include "c_screen.h"
#include "c_input.h"
#define THIS ((CSCREEN *) _object) #define THIS ((CSCREEN *) _object)
#define IS_BUFFERED (THIS->buffered) #define IS_BUFFERED (THIS->buffered)
#define E_UNSUPP "Unsupported value" #define E_UNSUPP "Unsupported value"
static bool _cursor;
static int _echo;
/* Currently active screen */
static CSCREEN *active = NULL; static CSCREEN *active = NULL;
GB_SIGNAL_CALLBACK *callback; GB_SIGNAL_CALLBACK *_sigwinch_cb;
DECLARE_EVENT(EVENT_Read); DECLARE_EVENT(EVENT_Read);
DECLARE_EVENT(EVENT_Resize); DECLARE_EVENT(EVENT_Resize);
@ -52,69 +48,28 @@ DECLARE_EVENT(EVENT_Resize);
/* /*
* Signal handler for the SIGWINCH signal. * Signal handler for the SIGWINCH signal.
* @signum: signal number given * @signum: signal number given
* @arg: unused
* This routine dispatches the Resize Event * This routine dispatches the Resize Event
*/ */
void SCREEN_sigwinch_handler(int signum, intptr_t data) static void SCREEN_sigwinch_handler(int signum, intptr_t arg)
{ {
if (signum == SIGWINCH) if (signum == SIGWINCH)
GB.Raise(active, EVENT_Resize, 0); GB.Raise(active, EVENT_Resize, 0);
} }
/**
* Set mode of @_cursor
*/
static int SCREEN_cursor_mode(int mode)
{
switch (mode) {
case CURSOR_HIDDEN:
curs_set(0);
break;
case CURSOR_VISIBLE:
curs_set(1);
break;
case CURSOR_VERY:
curs_set(2);
break;
default:
return -1;
}
_cursor = mode;
return 0;
}
/**
* Set mode of @_echo
*/
static int SCREEN_echo_mode(int mode)
{
switch (mode) {
case ECHO_NOECHO:
noecho();
break;
case ECHO_ECHO:
echo();
break;
default:
return -1;
}
_echo = mode;
return 0;
}
/** /**
* Screen initialisation * Screen initialisation
*/ */
int SCREEN_init() int SCREEN_init()
{ {
/* Global variable default setup */ /* Global variable default setup */
SCREEN_cursor_mode(CURSOR_VISIBLE); CURSOR_mode(CURSOR_VISIBLE);
SCREEN_echo_mode(ECHO_NOECHO); ECHO_mode(ECHO_NOECHO);
INPUT_init(); INPUT_init();
/* callback = GB.Signal.Register(SIGSEGV, // _sigwinch_cb = GB.Signal.Register(SIGWINCH,
SCREEN_sigwinch_handler, (intptr_t) NULL); // SCREEN_sigwinch_handler, (intptr_t) NULL);
*/
return 0; return 0;
} }
@ -125,8 +80,7 @@ void SCREEN_exit()
{ {
INPUT_exit(); INPUT_exit();
/* GB.Signal.Unregister(SIGWINCH, callback); // GB.Signal.Unregister(SIGWINCH, _sigwinch_cb);
*/
} }
/** /**
@ -173,14 +127,23 @@ void SCREEN_refresh(void *_object)
/** /**
* Let the specified screen raise its Read event. If the _object is NULL, * Let the specified screen raise its Read event. If the _object is NULL,
* the currently active screen will raise. * the currently active screen will raise. The latter case is the last
* resort for the difficulty of raising Read event in Window-Screen
* relation, while there need not be a focused window to raise this event,
* there is always an active screen.
*/ */
void SCREEN_raise_read(void *_object) void SCREEN_raise_read(void *_object)
{ {
bool risen;
if (!_object) if (!_object)
GB.Raise(active, EVENT_Read, 0); risen = GB.Raise(active, EVENT_Read, 0);
else else
GB.Raise(_object, EVENT_Read, 0); risen = GB.Raise(_object, EVENT_Read, 0);
/* Reduce watch callback calling overhead */
if (!risen)
INPUT_drain();
} }
BEGIN_PROPERTY(Screen_Buffered) BEGIN_PROPERTY(Screen_Buffered)
@ -196,11 +159,11 @@ END_PROPERTY
BEGIN_PROPERTY(Screen_Cursor) BEGIN_PROPERTY(Screen_Cursor)
if (READ_PROPERTY) { if (READ_PROPERTY) {
GB.ReturnInteger(_cursor); GB.ReturnInteger(CURSOR_mode(CURSOR_RETURN));
return; return;
} }
if (SCREEN_cursor_mode(VPROP(GB_INTEGER)) == -1) if (CURSOR_mode(VPROP(GB_INTEGER)) == -1)
GB.Error(E_UNSUPP); GB.Error(E_UNSUPP);
END_PROPERTY END_PROPERTY
@ -208,11 +171,11 @@ END_PROPERTY
BEGIN_PROPERTY(Screen_Echo) BEGIN_PROPERTY(Screen_Echo)
if (READ_PROPERTY) { if (READ_PROPERTY) {
GB.ReturnBoolean(_echo); GB.ReturnBoolean(ECHO_mode(ECHO_RETURN));
return; return;
} }
if (SCREEN_echo_mode(!!VPROP(GB_BOOLEAN)) == -1) if (ECHO_mode(!!VPROP(GB_BOOLEAN)) == -1)
GB.Error(E_UNSUPP); GB.Error(E_UNSUPP);
END_PROPERTY END_PROPERTY
@ -229,32 +192,6 @@ BEGIN_PROPERTY(Screen_Input)
END_PROPERTY END_PROPERTY
BEGIN_PROPERTY(Screen_IsConsole)
int fd = INPUT_consolefd();
if (fd == -1) {
GB.ReturnBoolean(FALSE);
return;
}
close(fd);
GB.ReturnBoolean(TRUE);
END_PROPERTY
BEGIN_PROPERTY(Screen_Repeater)
if (READ_PROPERTY) {
GB.ReturnInteger(INPUT_repeater_delay(REPEATER_RETURN));
return;
}
if (INPUT_repeater_delay(VPROP(GB_INTEGER)) == -1) {
GB.Error("Invalid value");
return;
}
END_PROPERTY
BEGIN_PROPERTY(Screen_Lines) BEGIN_PROPERTY(Screen_Lines)
GB.ReturnInteger(LINES); GB.ReturnInteger(LINES);
@ -275,6 +212,8 @@ END_METHOD
BEGIN_METHOD_VOID(Screen_free) BEGIN_METHOD_VOID(Screen_free)
/* TODO: for now, while Screen instantiation wouldn't do anything
* useful */
SCREEN_exit(); SCREEN_exit();
END_METHOD END_METHOD
@ -285,29 +224,18 @@ BEGIN_METHOD_VOID(Screen_Refresh)
END_METHOD END_METHOD
GB_DESC CScreenDesc[] = GB_DESC CScreenDesc[] = {
{
GB_DECLARE("Screen", sizeof(CSCREEN)), GB_DECLARE("Screen", sizeof(CSCREEN)),
GB_AUTO_CREATABLE(), GB_AUTO_CREATABLE(),
GB_EVENT("Read", NULL, NULL, &EVENT_Read), GB_EVENT("Read", NULL, NULL, &EVENT_Read),
GB_EVENT("Resize", NULL, NULL, &EVENT_Resize), GB_EVENT("Resize", NULL, NULL, &EVENT_Resize),
GB_CONSTANT("Hidden", "i", CURSOR_HIDDEN),
GB_CONSTANT("Visible", "i", CURSOR_VISIBLE),
GB_CONSTANT("Cooked", "i", INPUT_COOKED),
GB_CONSTANT("CBreak", "i", INPUT_CBREAK),
GB_CONSTANT("Raw", "i", INPUT_RAW),
GB_CONSTANT("NoDelay", "i", INPUT_NODELAY),
GB_PROPERTY("Buffered", "b", Screen_Buffered), GB_PROPERTY("Buffered", "b", Screen_Buffered),
GB_STATIC_PROPERTY("Cursor", "i", Screen_Cursor), GB_STATIC_PROPERTY("Cursor", "i", Screen_Cursor),
GB_STATIC_PROPERTY("Echo", "b", Screen_Echo), GB_STATIC_PROPERTY("Echo", "b", Screen_Echo),
GB_STATIC_PROPERTY("Input", "i", Screen_Input), GB_STATIC_PROPERTY("Input", "i", Screen_Input),
GB_STATIC_PROPERTY_READ("IsConsole", "b", Screen_IsConsole),
GB_STATIC_PROPERTY("Repeater", "i", Screen_Repeater),
GB_STATIC_PROPERTY_READ("Lines", "i", Screen_Lines), //GB_PROPERTY GB_STATIC_PROPERTY_READ("Lines", "i", Screen_Lines), //GB_PROPERTY
GB_STATIC_PROPERTY_READ("Cols", "i", Screen_Cols), GB_STATIC_PROPERTY_READ("Cols", "i", Screen_Cols),
@ -319,3 +247,73 @@ GB_DESC CScreenDesc[] =
GB_END_DECLARE GB_END_DECLARE
}; };
/*
* CURSOR routines
*/
static int _cursor;
/**
* Return or set cursor mode
* @mode: choose operation
*/
int CURSOR_mode(int mode)
{
switch (mode) {
case CURSOR_RETURN:
return _cursor;
case CURSOR_HIDDEN:
curs_set(0);
break;
case CURSOR_VISIBLE:
curs_set(1);
break;
case CURSOR_VERY:
curs_set(2);
break;
default:
return -1;
}
_cursor = mode;
return 0;
}
GB_DESC CCursorDesc[] = {
GB_DECLARE("Cursor", 0),
GB_NOT_CREATABLE(),
GB_CONSTANT("Hidden", "i", CURSOR_HIDDEN),
GB_CONSTANT("Visible", "i", CURSOR_VISIBLE),
GB_CONSTANT("Very", "i", CURSOR_VERY),
GB_END_DECLARE
};
/*
* ECHO routines
*/
static int _echo;
/**
* Return or set echo mode
* @mode: choose operation
*/
int ECHO_mode(int mode)
{
switch (mode) {
case ECHO_RETURN:
return _echo;
case ECHO_NOECHO:
noecho();
break;
case ECHO_ECHO:
echo();
break;
default:
return -1;
}
_echo = mode;
return 0;
}

View file

@ -23,6 +23,7 @@
#define __C_SCREEN_H #define __C_SCREEN_H
#include "gambas.h" #include "gambas.h"
#include "c_input.h"
/* This will produce final output on terminal screen */ /* This will produce final output on terminal screen */
#define REAL_REFRESH() SCREEN_real_refresh() #define REAL_REFRESH() SCREEN_real_refresh()
@ -30,11 +31,14 @@
(read: to check if the output is buffered and if not produce output by means of (read: to check if the output is buffered and if not produce output by means of
REAL_REFRESH()) */ REAL_REFRESH()) */
#define REFRESH() SCREEN_refresh(NULL) #define REFRESH() SCREEN_refresh(NULL)
/* The Screen - like in ncurses - is the sole source of input */
#define SCREEN_get(t) INPUT_get(t)
/* /*
* Cursor modes * Cursor modes
*/ */
enum { enum {
CURSOR_RETURN = -1,
CURSOR_HIDDEN, CURSOR_HIDDEN,
CURSOR_VISIBLE, CURSOR_VISIBLE,
CURSOR_VERY CURSOR_VERY
@ -44,12 +48,14 @@ enum {
* Echo modes * Echo modes
*/ */
enum { enum {
ECHO_RETURN = -1,
ECHO_NOECHO, ECHO_NOECHO,
ECHO_ECHO ECHO_ECHO
}; };
#ifndef __C_SCREEN_C #ifndef __C_SCREEN_C
extern GB_DESC CScreenDesc[]; extern GB_DESC CScreenDesc[];
extern GB_DESC CCursorDesc[];
#endif #endif
typedef struct { typedef struct {
@ -65,4 +71,7 @@ void SCREEN_refresh();
void SCREEN_real_refresh(); void SCREEN_real_refresh();
void SCREEN_raise_read(void *); void SCREEN_raise_read(void *);
int CURSOR_mode(int);
int ECHO_mode(int);
#endif /* __C_SCREEN_H */ #endif /* __C_SCREEN_H */

View file

@ -35,7 +35,6 @@
#include "main.h" #include "main.h"
#include "c_screen.h" #include "c_screen.h"
#include "c_color.h" #include "c_color.h"
#include "input.h"
#define THIS ((CWINDOW *) _object) #define THIS ((CWINDOW *) _object)
#define HAS_BORDER (THIS->border != BORDER_NONE) #define HAS_BORDER (THIS->border != BORDER_NONE)
@ -118,7 +117,7 @@ DECLARE_EVENT(EVENT_Read);
/* /*
* C Window interface * C Window interface
* @_object: Reference to the struct nc_window representing the current object. * @_object: Reference to the CWINDOW representing the current object.
* *
* The theory: Each window has a main and a content window pointer. The main window * The theory: Each window has a main and a content window pointer. The main window
* represents the window object outwards. It is linked to a panel structure which * represents the window object outwards. It is linked to a panel structure which
@ -382,7 +381,7 @@ static int WINDOW_cursor_move(void *_object, int x, int y)
} }
/** /**
* Move the entire nc_window on screen. * Move the entire window on screen.
* @x: new x coordinate * @x: new x coordinate
* @y: new y coordinate of the upper left corner of the main window relative to the * @y: new y coordinate of the upper left corner of the main window relative to the
* screen. Again, -1 denotes to leave the value as is * screen. Again, -1 denotes to leave the value as is
@ -579,7 +578,55 @@ static int WINDOW_get_str(void *_object, int x, int y, unsigned int len, char **
*/ */
static int WINDOW_key_timeout(void *_object, int timeout, int *ret) static int WINDOW_key_timeout(void *_object, int timeout, int *ret)
{ {
*ret = INPUT_get(timeout); *ret = SCREEN_get(timeout);
return 0;
}
/**
* Read an entire string form the input queue
* @ret: the pointer to a newly malloced string will be stored there
* This function behaves like getstr(3X). Note that getstr() being a
* sequence of getch(), we must stick to its characteristics, too, i.e.
* beep() when there is a non-printable character typed in and echo all
* characters if we echo.
*/
static int WINDOW_readline(void *_object, char **ret)
{
int key;
int i, j;
char *line = NULL, *p, c;
i = j = 0;
while (1) {
if (!(i & 256)) {
if (!(p = realloc(line, (++j * 256) + 1))) {
free(line);
return -1;
}
line = p;
}
key = SCREEN_get(TIMEOUT_NOTIMEOUT);
c = (char) key;
if (c == '\n' || c == '\r')
break;
if (key == KEY_BACKSPACE || key == KEY_LEFT) {
if (i > 0)
i--;
continue;
}
if ((c < 32 && !isspace(c)) || key > 127) {
beep();
continue;
}
line[i++] = c;
if (ECHO_mode(ECHO_RETURN)) {
line[i] = 0;
WINDOW_print(THIS, line + i - 1, -1, -1);
}
}
line[i] = 0;
*ret = line;
return 0; return 0;
} }
@ -588,7 +635,7 @@ static int WINDOW_key_timeout(void *_object, int timeout, int *ret)
*/ */
static void WINDOW_drain() static void WINDOW_drain()
{ {
flushinp(); INPUT_drain();
} }
/** /**
@ -633,7 +680,8 @@ static int WINDOW_show(void *_object)
* Raise the Window_Read event of the given window. If _object is NULL, * Raise the Window_Read event of the given window. If _object is NULL,
* the currently focused window is used. The event is, however, only risen, * the currently focused window is used. The event is, however, only risen,
* if the window can raise events. Otherwise let the parent screen raise the * if the window can raise events. Otherwise let the parent screen raise the
* event. * event. If there is not even a parent screen accessible, use the active
* screen.
*/ */
void WINDOW_raise_read(void *_object) void WINDOW_raise_read(void *_object)
{ {
@ -657,7 +705,7 @@ static int WINDOW_setfocus(void *_object)
if (focused) if (focused)
GB.Unref((void **) &focused); GB.Unref((void **) &focused);
focused = THIS; focused = THIS;
GB.Ref((void **) &focused); GB.Ref((void *) focused);
/* We watch since SCREEN_init() */ /* We watch since SCREEN_init() */
return 0; return 0;
} }
@ -838,18 +886,6 @@ BEGIN_PROPERTY(Window_Caption)
END_PROPERTY END_PROPERTY
BEGIN_PROPERTY(Window_ContainerHeight)
GB.ReturnInteger(getmaxy(THIS->main));
END_PROPERTY
BEGIN_PROPERTY(Window_ContainerWidth)
GB.ReturnInteger(getmaxx(THIS->main));
END_PROPERTY
BEGIN_PROPERTY(Window_CursorX) BEGIN_PROPERTY(Window_CursorX)
if (READ_PROPERTY) { if (READ_PROPERTY) {
@ -1000,7 +1036,7 @@ BEGIN_METHOD(Window_Ask, GB_STRING opts; GB_INTEGER tries)
o = GB.ToZeroString(ARG(opts)); o = GB.ToZeroString(ARG(opts));
while (miss || t--) { while (miss || t--) {
ch = getch(); WINDOW_key_timeout(THIS, -1, &ch);
/* Per convention, only dealing with byte chars */ /* Per convention, only dealing with byte chars */
if (ch > 255) if (ch > 255)
continue; continue;
@ -1204,9 +1240,14 @@ END_METHOD
BEGIN_METHOD_VOID(Window_ReadLine) BEGIN_METHOD_VOID(Window_ReadLine)
/*abbruch-kondition? ein backspace mehr, als zeichen vorhanden? eine char *line;
bestimmte taste? NULL wird zurueckgegeben. \n und \x4 werden nicht
geliefert.*/ if (WINDOW_readline(THIS, &line) == -1) {
GB.ReturnNull();
return;
}
GB.ReturnNewZeroString(line);
free(line);
END_METHOD END_METHOD
@ -1223,6 +1264,79 @@ BEGIN_METHOD_VOID(Window_SetFocus)
END_METHOD END_METHOD
GB_DESC CWindowDesc[] = {
GB_DECLARE("Window", sizeof(CWINDOW)),
GB_AUTO_CREATABLE(),
GB_EVENT("Read", NULL, NULL, &EVENT_Read),
/* Properties */
GB_PROPERTY_SELF("Attributes", ".Window.Attrs"),
GB_PROPERTY("Background", "i", Window_Background),
GB_PROPERTY("Paper", "i", Window_Background),
GB_PROPERTY("Border", "i", Window_Border),
GB_PROPERTY("Caption", "s", Window_Caption),
GB_PROPERTY("CursorX", "i", Window_CursorX),
GB_PROPERTY("CursorY", "i", Window_CursorY),
GB_PROPERTY("Foreground", "i", Window_Foreground),
GB_PROPERTY("Pen", "i", Window_Foreground),
GB_PROPERTY("Height", "i", Window_Height),
GB_PROPERTY("H", "i", Window_Height),
GB_PROPERTY("Wrap", "b", Window_Wrap),
GB_PROPERTY("Width", "i", Window_Width),
GB_PROPERTY("W", "i", Window_Width),
GB_PROPERTY("X", "i", Window_X),
GB_PROPERTY("Y", "i", Window_Y),
/* Methods */
GB_METHOD("_new", NULL, Window_new, "[(Parent)Screen;(X)i(Y)i(W)i(H)i]"),
GB_METHOD("_free", NULL, Window_free, NULL),
GB_METHOD("_get", ".Char.Attrs", Window_get, "(Y)i(X)i"),
GB_METHOD("Ask", "s", Window_Ask, "(Opts)s[(Tries)i]"),
GB_METHOD("Lower", NULL, Window_Lower, NULL),
GB_METHOD("Raise", NULL, Window_Raise, NULL),
GB_METHOD("Cls", NULL, Window_Cls, NULL),
GB_METHOD("Drain", NULL, Window_Drain, NULL),
GB_METHOD("Full", NULL, Window_Full, NULL),
GB_METHOD("Get", "s", Window_Get, "(X)i(Y)i[(Len)i]"),
GB_METHOD("Hide", NULL, Window_Hide, NULL),
GB_METHOD("Show", NULL, Window_Show, NULL),
GB_METHOD("DrawHLine", NULL, Window_DrawHLine, "(X)i(Y)i(Len)i(Ch)s[(Thickness)i]"),
GB_METHOD("DrawVLine", NULL, Window_DrawVLine, "(X)i(Y)i(Len)i(Ch)s[(Thickness)i]"),
GB_METHOD("Insert", NULL, Window_Insert, "(Text)s[(X)i(Y)i]"),
GB_METHOD("Print", NULL, Window_Print, "(Text)s[(X)i(Y)i]"),
GB_METHOD("PrintCenter", NULL, Window_PrintCenter, "(Text)s"),
GB_METHOD("Locate", NULL, Window_Locate, "(X)i(Y)i"),
GB_METHOD("Move", NULL, Window_Move, "[(X)i(Y)i]"),
GB_METHOD("Resize", NULL, Window_Resize, "[(W)i(H)i]"),
GB_METHOD("Read", "i", Window_Read, "[(Timeout)i]"),
GB_METHOD("ReadLine", "s", Window_ReadLine, NULL),
GB_METHOD("SetFocus", NULL, Window_SetFocus, NULL),
GB_END_DECLARE
};
/* /*
* .Window.Attrs virtual class * .Window.Attrs virtual class
*/ */
@ -1284,6 +1398,21 @@ BEGIN_PROPERTY(WindowAttrs_Color)
END_PROPERTY END_PROPERTY
GB_DESC CWindowAttrsDesc[] = {
GB_DECLARE(".Window.Attrs", 0),
GB_VIRTUAL_CLASS(),
GB_PROPERTY("Normal", "b", WindowAttrs_Normal),
GB_PROPERTY("Underline", "b", WindowAttrs_Underline),
GB_PROPERTY("Reverse", "b", WindowAttrs_Reverse),
GB_PROPERTY("Blink", "b", WindowAttrs_Blink),
GB_PROPERTY("Bold", "b", WindowAttrs_Bold),
GB_PROPERTY("Color", "i", WindowAttrs_Color),
GB_END_DECLARE
};
/* /*
* .Char.Attrs virtual class * .Char.Attrs virtual class
*/ */
@ -1349,109 +1478,7 @@ BEGIN_PROPERTY(CharAttrs_Color)
END_PROPERTY END_PROPERTY
GB_DESC CWindowDesc[] = GB_DESC CCharAttrsDesc[] = {
{
GB_DECLARE("Window", sizeof(CWINDOW)),
GB_AUTO_CREATABLE(),
GB_EVENT("Read", NULL, NULL, &EVENT_Read),
/* Constants */
GB_CONSTANT("NoTimeout", "i", TIMEOUT_NOTIMEOUT),
GB_CONSTANT("None", "i", BORDER_NONE),
GB_CONSTANT("Ascii", "i", BORDER_ASCII),
GB_CONSTANT("ACS", "i", BORDER_ACS),
/* Properties */
GB_PROPERTY_SELF("Attributes", ".Window.Attrs"),
GB_PROPERTY("Background", "i", Window_Background),
GB_PROPERTY("Paper", "i", Window_Background),
GB_PROPERTY("Border", "i", Window_Border),
GB_PROPERTY("Caption", "s", Window_Caption),
GB_PROPERTY_READ("ContainerHeight", "i", Window_ContainerHeight),
GB_PROPERTY_READ("ContainerH", "i", Window_ContainerHeight),
GB_PROPERTY_READ("ContainerWidth", "i", Window_ContainerWidth),
GB_PROPERTY_READ("ContainerW", "i", Window_ContainerWidth),
GB_PROPERTY("CursorX", "i", Window_CursorX),
GB_PROPERTY("CursorY", "i", Window_CursorY),
GB_PROPERTY("Foreground", "i", Window_Foreground),
GB_PROPERTY("Pen", "i", Window_Foreground),
GB_PROPERTY("Height", "i", Window_Height),
GB_PROPERTY("H", "i", Window_Height),
GB_PROPERTY("Wrap", "b", Window_Wrap),
GB_PROPERTY("Width", "i", Window_Width),
GB_PROPERTY("W", "i", Window_Width),
GB_PROPERTY("X", "i", Window_X),
GB_PROPERTY("Y", "i", Window_Y),
/* Methods */
GB_METHOD("_new", NULL, Window_new, "[(Parent)Screen;(X)i(Y)i(W)i(H)i]"),
GB_METHOD("_free", NULL, Window_free, NULL),
GB_METHOD("_get", ".Char.Attrs", Window_get, "(Y)i(X)i"),
GB_METHOD("Ask", "s", Window_Ask, "(Opts)s[(Tries)i]"),
GB_METHOD("Lower", NULL, Window_Lower, NULL),
GB_METHOD("Raise", NULL, Window_Raise, NULL),
GB_METHOD("Cls", NULL, Window_Cls, NULL),
GB_METHOD("Drain", NULL, Window_Drain, NULL),
GB_METHOD("Full", NULL, Window_Full, NULL),
GB_METHOD("Get", "s", Window_Get, "(X)i(Y)i[(Len)i]"),
GB_METHOD("Hide", NULL, Window_Hide, NULL),
GB_METHOD("Show", NULL, Window_Show, NULL),
GB_METHOD("DrawHLine", NULL, Window_DrawHLine, "(X)i(Y)i(Len)i(Ch)s[(Thickness)i]"),
GB_METHOD("DrawVLine", NULL, Window_DrawVLine, "(X)i(Y)i(Len)i(Ch)s[(Thickness)i]"),
GB_METHOD("Insert", NULL, Window_Insert, "(Text)s[(X)i(Y)i]"),
GB_METHOD("Print", NULL, Window_Print, "(Text)s[(X)i(Y)i]"),
GB_METHOD("PrintCenter", NULL, Window_PrintCenter, "(Text)s"),
GB_METHOD("Locate", NULL, Window_Locate, "(X)i(Y)i"),
GB_METHOD("Move", NULL, Window_Move, "[(X)i(Y)i]"),
GB_METHOD("Resize", NULL, Window_Resize, "[(W)i(H)i]"),
GB_METHOD("Read", "i", Window_Read, "[(Timeout)i]"),
GB_METHOD("ReadLine", "s", Window_ReadLine, NULL),
GB_METHOD("SetFocus", NULL, Window_SetFocus, NULL),
GB_END_DECLARE
};
GB_DESC CWindowAttrsDesc[] =
{
GB_DECLARE(".Window.Attrs", 0),
GB_VIRTUAL_CLASS(),
GB_PROPERTY("Normal", "b", WindowAttrs_Normal),
GB_PROPERTY("Underline", "b", WindowAttrs_Underline),
GB_PROPERTY("Reverse", "b", WindowAttrs_Reverse),
GB_PROPERTY("Blink", "b", WindowAttrs_Blink),
GB_PROPERTY("Bold", "b", WindowAttrs_Bold),
GB_PROPERTY("Color", "i", WindowAttrs_Color),
GB_END_DECLARE
};
GB_DESC CCharAttrsDesc[] =
{
GB_DECLARE(".Char.Attrs", 0), GB_DECLARE(".Char.Attrs", 0),
GB_VIRTUAL_CLASS(), GB_VIRTUAL_CLASS(),
@ -1465,3 +1492,18 @@ GB_DESC CCharAttrsDesc[] =
GB_END_DECLARE GB_END_DECLARE
}; };
/*
* Border static class
*/
GB_DESC CBorderDesc[] = {
GB_DECLARE("Border", 0),
GB_NOT_CREATABLE(),
GB_CONSTANT("None", "i", BORDER_NONE),
GB_CONSTANT("Ascii", "i", BORDER_ASCII),
GB_CONSTANT("ACS", "i", BORDER_ACS),
GB_END_DECLARE
};

View file

@ -37,11 +37,6 @@ enum {
BORDER_ACS BORDER_ACS
}; };
/* Timeout constants */
enum {
TIMEOUT_NOTIMEOUT = -1
};
/* @reqs for *_attrs_driver() */ /* @reqs for *_attrs_driver() */
enum enum
{ {
@ -77,6 +72,7 @@ typedef struct {
extern GB_DESC CWindowDesc[]; extern GB_DESC CWindowDesc[];
extern GB_DESC CWindowAttrsDesc[]; extern GB_DESC CWindowAttrsDesc[];
extern GB_DESC CCharAttrsDesc[]; extern GB_DESC CCharAttrsDesc[];
extern GB_DESC CBorderDesc[];
#endif #endif
void WINDOW_raise_read(void *); void WINDOW_raise_read(void *);

View file

@ -25,28 +25,35 @@
#include "c_key.h" #include "c_key.h"
#include "c_color.h" #include "c_color.h"
#include "c_screen.h" #include "c_screen.h"
#include "c_input.h"
#include "main.h" #include "main.h"
GB_INTERFACE GB EXPORT; GB_INTERFACE GB EXPORT;
GB_DESC *GB_CLASSES[] EXPORT = GB_DESC *GB_CLASSES[] EXPORT = {
{
CScreenDesc, CScreenDesc,
CInputDesc,
CCursorDesc,
CWindowDesc, CWindowDesc,
CWindowAttrsDesc, CWindowAttrsDesc,
CCharAttrsDesc, CCharAttrsDesc,
CBorderDesc,
CKeyDesc, CKeyDesc,
CColorDesc, CColorDesc,
CColorCapabilitiesDesc, CColorInfoDesc,
CColorPairDesc, CPairDesc,
CColorContentDesc, CPairInfoDesc,
NULL NULL
}; };
static bool _init = FALSE; static bool _init = FALSE;
/** /**
* Returns if we are in ncurses mode * Return if we are in ncurses mode
*/ */
bool MAIN_running() bool MAIN_running()
{ {