[GB.NCURSES]

* NEW: Move .Buffered and .Refresh() from Window to Screen class as Screen
  reflects better that these routines affect the entire screen
* NEW: Window has a Caption property to display a caption within the border
  frame; only visible when the Window has a border
* NEW: Add NoDelay input mode to Screen class (for use with caution and a
  true tty). This mode lets the programmer artifically set the keyboard
  repeat delay for the program (beware: still broken)
* NEW: Add IsConsole property to check if one can enter NoDelay mode
* NEW: Add Repeater property to Screen to specify keyboard repeat
  delay. This specifies the minimum interval of sucessively risen Read
  events and Window.Read() calls
* NEW: Rename Window.WaitKey() to .Read()
* NEW: Rename Window.Bottom() to .Lower() and .Top() to .Raise()
* NEW: Add very-visible mode for cursor, according to ncurses doc
* NEW: Window class has a new optional parameter to specify the parent
  Screen, if none given, the currently active Screen is used.
* NEW: Window class does not get a default event name anymore. If the Read
  event is risen and the active Window cannot raise events, the parent
  Screen raises it (beware: still broken)
* NEW: Window.Border is an integer, new constants .None, .ASCII, .ACS to
  choose the border look
* NEW: Cursor resides on the new location after Window.Print() now
* OPT: Remove redundant wrapping code in Print function because ncurses
  waddch() does this by itself
* OPT: Stream inheritance entirely useless due to future ReadLine()
  function, hence it was removed
* OPT: Remove NCurses class, it contained only component-global routines
  that were moved to main module
* BUG: Turning off attributes with the Normal attribute now works
* BUG: Changes on color attributes (also on color pairs) are immediately
  visible now
* BUG: Fix string argument handling in Window.Ask(), .Insert(), .Print() and
  .PrintCenter()
* BUG: Newline to Window.Print() really works now and gives the desired
  effect of not returning to x=0 on the next line (like ncurses does)



git-svn-id: svn://localhost/gambas/trunk@4778 867c0c6c-44f3-4631-809d-bfa615b0a4ec
This commit is contained in:
Tobias Boege 2012-05-25 21:09:17 +00:00
parent 5dd86fdb1b
commit 31cdd37ebd
12 changed files with 1288 additions and 653 deletions

View file

@ -9,9 +9,9 @@ gb_ncurses_la_CPPFLAGS = @NCURSES_INC@
gb_ncurses_la_SOURCES = \
main.h main.c \
c_ncurses.h c_ncurses.c \
c_window.h c_window.c \
c_key.h c_key.c \
c_color.h c_color.c \
c_screen.h c_screen.c
c_screen.h c_screen.c \
input.h input.c

View file

@ -27,6 +27,7 @@
#include "main.h"
#include "c_color.h"
#include "c_screen.h"
static int _index;
static int _color;
@ -189,6 +190,7 @@ BEGIN_PROPERTY(ColorPair_Background)
}
b = VPROP(GB_INTEGER);
COLOR_setpair(_index, f, b);
REAL_REFRESH();
END_PROPERTY
@ -208,6 +210,7 @@ BEGIN_PROPERTY(ColorPair_Foreground)
}
f = VPROP(GB_INTEGER);
COLOR_setpair(_index, f, b);
REAL_REFRESH();
END_PROPERTY

View file

@ -1,84 +0,0 @@
/*
* c_ncurses.c - gb.ncurses NCurses static class
*
* Copyright (C) 2012 Tobias Boege <tobias@gambas-buch.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#define __C_NCURSES_C
#include <stdio.h>
#include <signal.h>
#include <ncurses.h>
#include "c_ncurses.h"
#include "c_color.h"
#include "c_screen.h"
#include "main.h"
#define E_END "Could not end ncurses"
static bool _init = FALSE;
bool NCURSES_running()
{
return _init && (!isendwin() || stdscr);
}
void NCURSES_init(void)
{
if (_init)
return;
initscr();
keypad(stdscr, TRUE);
/* Color setup */
COLOR_init(); /* Color._init() would be called before the main hook, apparently */
/* Screen setup */
SCREEN_init();
refresh();
_init = TRUE;
}
void NCURSES_exit()
{
if (_init) {
endwin();
_init = FALSE;
}
}
BEGIN_METHOD_VOID(NCurses_exit)
NCURSES_exit();
END_METHOD
GB_DESC CNCursesDesc[] =
{
GB_DECLARE("NCurses", 0),
GB_NOT_CREATABLE(),
GB_STATIC_METHOD("_exit", NULL, NCurses_exit, NULL),
GB_END_DECLARE
};

View file

@ -1,39 +0,0 @@
/*
* c_ncurses.h - gb.ncurses NCurses static class
*
* Copyright (C) 2012 Tobias Boege <tobias@gambas-buch.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#ifndef __C_NCURSES_H
#define __C_NCURSES_H
#include <ncurses.h>
#include "gambas.h"
#define NCURSES_RUNNING NCURSES_running()
#ifndef __C_NCURSES_C
extern GB_DESC CNCursesDesc[];
#endif
void NCURSES_init(void);
void NCURSES_exit(void);
bool NCURSES_running(void);
#endif /* __C_NCURSES_H */

View file

@ -25,44 +25,81 @@
#include <signal.h>
#include <ncurses.h>
#include <panel.h>
#include "gambas.h"
#include "c_screen.h"
#include "main.h"
#include "input.h"
#define THIS ((CSCREEN *) _object)
#define IS_BUFFERED (THIS->buffered)
#define E_UNSUPP "Unsupported value"
#define CURSOR_HIDDEN 0
#define CURSOR_VISIBLE 1
#define INPUT_COOKED 0
#define INPUT_CBREAK 1
#define INPUT_RAW 2
#define INPUT_KEYBOARD 3
static bool _cursor;
static int _input;
static int _echo;
/* Currently active screen */
static CSCREEN *active = NULL;
GB_SIGNAL_CALLBACK *callback;
DECLARE_EVENT(EVENT_Read);
DECLARE_EVENT(EVENT_Resize);
#if 0
/*
* Signal handler for the SIGWINCH signal.
* @signum: signal number given
* This routine dispatches the Resize Event
*/
void nc_sigwinch_handler(int signum)
void SCREEN_sigwinch_handler(int signum, intptr_t data)
{
/* TODO: I wonder if this works... */
/* BM: Of course not! :-) You can't raise an event from a signal handler
* and moreover you have no sender! */
if (signum == SIGWINCH) GB.Raise(NULL, EVENT_Resize, 0);
if (signum == SIGWINCH)
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;
}
#endif
/**
* Screen initialisation
@ -70,29 +107,92 @@ void nc_sigwinch_handler(int signum)
int SCREEN_init()
{
/* Global variable default setup */
_cursor = CURSOR_VISIBLE;
_input = INPUT_CBREAK;
_echo = 0;
/* accordingly... */
curs_set(1);
cbreak();
noecho();
SCREEN_cursor_mode(CURSOR_VISIBLE);
SCREEN_echo_mode(ECHO_NOECHO);
#if 0
struct sigaction sa;
sa.sa_handler = nc_sigwinch_handler;
sigemptyset(&(sa.sa_mask));
sa.sa_flags = 0;
if (sigaction(SIGWINCH, &sa, NULL) == -1)
{
fprintf(stderr, "gb.ncurses: Could not install SIGWINCH signal handler");
}
#endif
INPUT_init();
/* callback = GB.Signal.Register(SIGSEGV,
SCREEN_sigwinch_handler, (intptr_t) NULL);
*/
return 0;
}
/**
* Screen cleanup
*/
void SCREEN_exit()
{
INPUT_exit();
/* GB.Signal.Unregister(SIGWINCH, callback);
*/
}
/**
* Get the active Screen
* If @active is NULL, the default Screen will be made active and then returned
*/
CSCREEN *SCREEN_get_active()
{
GB_CLASS screen_class;
if (active)
return active;
screen_class = GB.FindClass("Screen");
active = GB.AutoCreate(screen_class, 0);
return active;
}
/**
* Redraw the screen no matter what the buffer settings are.
* Note that this function may not be used to return into ncurses mode once left.
*/
void SCREEN_real_refresh()
{
if (!NCURSES_RUNNING)
return;
update_panels();
doupdate();
}
/**
* Refresh the screen. This respects the currently active buffering wishes
*/
void SCREEN_refresh(void *_object)
{
if (!NCURSES_RUNNING)
return;
if (!_object)
_object = SCREEN_get_active();
if (!IS_BUFFERED)
SCREEN_real_refresh();
}
/**
* Let the specified screen raise its Read event. If the _object is NULL,
* the currently active screen will raise.
*/
void SCREEN_raise_read(void *_object)
{
if (!_object)
GB.Raise(active, EVENT_Read, 0);
else
GB.Raise(_object, EVENT_Read, 0);
}
BEGIN_PROPERTY(Screen_Buffered)
if (READ_PROPERTY) {
GB.ReturnBoolean(IS_BUFFERED);
return;
}
THIS->buffered = VPROP(GB_BOOLEAN);
END_PROPERTY
BEGIN_PROPERTY(Screen_Cursor)
if (READ_PROPERTY) {
@ -100,47 +200,8 @@ BEGIN_PROPERTY(Screen_Cursor)
return;
}
switch (VPROP(GB_INTEGER)) {
case CURSOR_HIDDEN:
curs_set(0);
break;
case CURSOR_VISIBLE:
curs_set(1);
break;
default:
GB.Error(E_UNSUPP);
return;
}
_cursor = VPROP(GB_INTEGER);
END_PROPERTY
BEGIN_PROPERTY(Screen_Input)
if (READ_PROPERTY) {
GB.ReturnInteger(_input);
return;
}
switch (VPROP(GB_INTEGER)) {
case INPUT_COOKED:
noraw();
nocbreak();
break;
case INPUT_CBREAK:
cbreak();
break;
case INPUT_RAW:
raw();
break;
case INPUT_KEYBOARD:
/* TODO: implement! */
break;
default:
GB.Error(E_UNSUPP);
return;
}
_input = VPROP(GB_INTEGER);
if (SCREEN_cursor_mode(VPROP(GB_INTEGER)) == -1)
GB.Error(E_UNSUPP);
END_PROPERTY
@ -151,11 +212,46 @@ BEGIN_PROPERTY(Screen_Echo)
return;
}
_echo = VPROP(GB_BOOLEAN);
if (_echo)
echo();
else
noecho();
if (SCREEN_echo_mode(!!VPROP(GB_BOOLEAN)) == -1)
GB.Error(E_UNSUPP);
END_PROPERTY
BEGIN_PROPERTY(Screen_Input)
if (READ_PROPERTY) {
GB.ReturnInteger(INPUT_mode(INPUT_RETURN));
return;
}
if (INPUT_mode(VPROP(GB_INTEGER)) == -1)
GB.Error(E_UNSUPP);
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
@ -173,30 +269,53 @@ END_PROPERTY
BEGIN_METHOD_VOID(Screen_new)
SCREEN_init();
active = THIS;
END_METHOD
BEGIN_METHOD_VOID(Screen_free)
SCREEN_exit();
END_METHOD
BEGIN_METHOD_VOID(Screen_Refresh)
SCREEN_real_refresh();
END_METHOD
GB_DESC CScreenDesc[] =
{
GB_DECLARE("Screen", 0), //sizeof(struct nc_screen)),
GB_DECLARE("Screen", sizeof(CSCREEN)),
GB_AUTO_CREATABLE(),
GB_EVENT("Read", NULL, NULL, &EVENT_Read),
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_STATIC_PROPERTY("Cursor", "i", Screen_Cursor),
GB_STATIC_PROPERTY("Input", "i", Screen_Input),
GB_STATIC_PROPERTY("Echo", "b", Screen_Echo),
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("Cols", "i", Screen_Cols),
GB_METHOD("_new", NULL, Screen_new, NULL),
GB_METHOD("_free", NULL, Screen_free, NULL),
GB_METHOD("Refresh", NULL, Screen_Refresh, NULL),
GB_END_DECLARE
};

View file

@ -24,10 +24,45 @@
#include "gambas.h"
#ifndef __C_WINDOW_C
/* This will produce final output on terminal screen */
#define REAL_REFRESH() SCREEN_real_refresh()
/* This macro is mostly called by Gambas implementation functions to request output on screen
(read: to check if the output is buffered and if not produce output by means of
REAL_REFRESH()) */
#define REFRESH() SCREEN_refresh(NULL)
/*
* Cursor modes
*/
enum {
CURSOR_HIDDEN,
CURSOR_VISIBLE,
CURSOR_VERY
};
/*
* Echo modes
*/
enum {
ECHO_NOECHO,
ECHO_ECHO
};
#ifndef __C_SCREEN_C
extern GB_DESC CScreenDesc[];
#endif
typedef struct {
GB_BASE ob;
bool buffered; /* Whether output will be buffered, i.e.
only done via Screen.Refresh() */
} CSCREEN;
int SCREEN_init();
void SCREEN_exit();
CSCREEN *SCREEN_get_active();
void SCREEN_refresh();
void SCREEN_real_refresh();
void SCREEN_raise_read(void *);
#endif /* __C_SCREEN_H */

View file

@ -31,46 +31,96 @@
#include "gambas.h"
#include "gb_common.h"
#include "main.h"
#include "c_window.h"
#include "main.h"
#include "c_screen.h"
#include "c_color.h"
#include "input.h"
static int Window_stream_open(GB_STREAM *stream, const char *path, int mode, void *data);
static int Window_stream_close(GB_STREAM *stream);
static int Window_stream_read(GB_STREAM *stream, char *buffer, int len);
static int Window_stream_getchar(GB_STREAM *stream, char *buffer);
static int Window_stream_write(GB_STREAM *stream, char *buffer, int len);
static int Window_stream_seek(GB_STREAM *stream, int64_t pos, int whence);
static int Window_stream_tell(GB_STREAM *stream, int64_t *pos);
static int Window_stream_flush(GB_STREAM *stream);
static int Window_stream_eof(GB_STREAM *stream);
static int Window_stream_lof(GB_STREAM *stream, int64_t *len);
static int Window_stream_handle(GB_STREAM *stream);
#define THIS ((CWINDOW *) _object)
#define HAS_BORDER (THIS->border != BORDER_NONE)
#define IS_WRAPPED (THIS->wrap)
/* The nc_window currently having the focus (raising Read events) */
static struct nc_window *focused = NULL;
/* Windows have their own association to a particular Screen, they don't
* update just the active one */
#ifdef REFRESH
#undef REFRESH
/* Only refresh if we are on the active screen */
#define REFRESH() \
do { \
if (SCREEN_get_active() == THIS->parent) \
SCREEN_refresh(THIS->parent); \
} while (0)
#endif
/* Translate linear (absolute) memory addresses and x,y coordinates into each other
most useful when wrapping is needed. */
#define A2XY(win, a, x, y) do { \
(x) = (a) % getmaxx(win); \
(y) = (a) / getmaxx(win); \
} while (0)
#define XY2A(win, x, y, a) do { \
(a) = (y) * getmaxx(win) + (x); \
} while (0)
/* Interpret the -1 values in coordinates as to insert the current cursor position */
#define MAKE_COORDS(win, x, y) do { \
if ((x) == -1) \
x = getcurx(win); \
if ((y) == -1) \
y = getcury(win); \
} while (0)
/* Check for out-of-range coordinates */
#define BAD_COORDS(win, x, y) ((x) < 0 || (x) >= getmaxx(win) || \
(y) < 0 || (y) >= getmaxy(win))
#define BAD_DIMENSION(w, h) ((w) <= 0 || (w) > COLS || \
(h) <= 0 || (h) > LINES)
#define WIN_ATTR_METHOD(a) \
do { \
if (READ_PROPERTY) \
GB.ReturnBoolean(WINDOW_attrs_driver( \
THIS, (a), ATTR_DRV_RET) & (a)); \
else \
WINDOW_attrs_driver(THIS, (a), \
VPROP(GB_BOOLEAN) ? ATTR_DRV_ON \
: ATTR_DRV_OFF); \
} while (0)
/* Notice the wtouchln() line in the following macro. It seems that a chgat() from
WINDOW_char_attrs_driver() doesn't mark anything dirty (no output on screen from
a REFRESH()). So to make the new attribute available immidiately, we touch the affected
line manually. A higher-callstack function may call REFRESH() to get output. */
#define CHAR_ATTR_METHOD(a) \
do { \
if (READ_PROPERTY) \
GB.ReturnBoolean(WINDOW_char_attrs_driver( \
THIS, (a), THIS->pos.col, THIS->pos.line, \
ATTR_DRV_RET) & (a)); \
else \
WINDOW_char_attrs_driver(THIS, (a), THIS->pos.col, \
THIS->pos.line, \
VPROP(GB_BOOLEAN) ? ATTR_DRV_ON \
: ATTR_DRV_OFF); \
wtouchln(THIS->main, THIS->pos.line \
+ (HAS_BORDER ? 1 : 0), 1, 1); \
} while(0)
#define WINDOW_main_to_content() WINDOW_copy_window(THIS->main, THIS->content, 0, 0, \
getmaxx(THIS->content), \
getmaxy(THIS->content), 0, 0)
#define WINDOW_content_to_main() WINDOW_copy_window(THIS->content, THIS->main, 0, 0, \
getmaxx(THIS->content), \
getmaxy(THIS->content), 0, 0)
/* The window currently having the focus (raising Read events) */
static CWINDOW *focused = NULL;
DECLARE_EVENT(EVENT_Read);
GB_STREAM_DESC WindowStream = {
open: Window_stream_open,
close: Window_stream_close,
read: Window_stream_read,
getchar: Window_stream_getchar,
write: Window_stream_write,
seek: Window_stream_seek,
tell: Window_stream_tell,
flush: Window_stream_flush,
eof: Window_stream_eof,
lof: Window_stream_lof,
handle: Window_stream_handle
};
/*
* C Window interface
* @_object: Reference to the struct nc_window representing the current object.
*
* The theory: Each struct nc_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
* is managed by the ncurses panel extension library to allow overlapping windows.
* One aim of this design is to turn the ncurses-style inner-window border
@ -93,29 +143,9 @@ GB_STREAM_DESC WindowStream = {
#define E_COORDS "Coordinates out of range"
#define E_DIMENSION "Dimensions do not fit on screen"
/*
* Redraw the screen no matter what particular buffering settings the windows have.
* Note that this function may not be used to return into ncurses mode once left.
*/
void WINDOW_real_refresh()
{
if (!NCURSES_running())
return;
update_panels();
doupdate();
}
/**
* Refresh the screen. This respects THIS' buffering wishes
*/
void WINDOW_refresh(void *_object)
{
if (!IS_BUFFERED)
WINDOW_real_refresh();
}
/**
* Copies text with attributes from a window to a newly malloced array as if the window
* was linear memory, too (line by line).
* was linear memory, too (line by line) - this is mainly used by wrapping code.
* @src: source window
* @arrp: pointer to store the newly malloced pointer containing the data in
* @x: x position
@ -157,7 +187,7 @@ static int WINDOW_get_mem(WINDOW *src, chtype **arrp, int sx, int sy, int len)
* @dst: destination window
* @sx: starting x
* @sy: starting y coordinate
* @len: length of data to copy from @arr (may not exceed its limits)
* @len: length of data to copy from @arr
*/
static int WINDOW_put_mem(chtype *arr, WINDOW *dst, int sx, int sy, unsigned int len)
{
@ -176,8 +206,6 @@ static int WINDOW_put_mem(chtype *arr, WINDOW *dst, int sx, int sy, unsigned int
wmove(dst, sy, sx);
/* addch() ORs the current attributes but we want the chtypes drawn as they are in the buffer */
wattrset(dst, A_NORMAL);
/* advancing the cursor position is the good thing about addch() (at least, it is documented
behaviour...) */
for (i = 0; i < len; i++)
waddch(dst, arr[i]);
wattrset(dst, attrs);
@ -237,18 +265,27 @@ static int WINDOW_copy_window(WINDOW *src, WINDOW *dst, int sx, int sy, int nx,
/**
* Unlink and deallocate the main panel together with the window and, if present, the
* content window, refresh the screen to immediately remove leftovers of the window.
* content window and caption, refresh the screen to immediately remove leftovers of
* the window.
*/
static int WINDOW_remove(void *_object)
{
int by, h;
by = getbegy(THIS->main);
h = getmaxy(THIS->main);
wclear(THIS->content);
if (HAS_BORDER) {
delwin(THIS->content);
wclear(THIS->main);
}
REFRESH();
if (THIS->caption) {
GB.FreeString(&THIS->caption);
THIS->caption = NULL;
}
del_panel(THIS->pan);
delwin(THIS->main);
wtouchln(stdscr, by, h, 1);
return 0;
}
@ -293,22 +330,31 @@ static int WINDOW_remove_content(void *_object)
}
/**
* Draws an inner-window border to the main window which appears as outer-window for the content
* @b: 0: erase the border by overwriting it with all spaces
* !0: print a border
* Draws an inner-window border to the main window which appears as
* outer-window for the content. If a caption was set for the window and the
* border is to be drawn, also the caption is.
* @b: one of the BORDER_* constants
*/
static int WINDOW_draw_border(void *_object, bool b)
{
/* TODO: how to check for the possibility of displaying ACS chars?
Terminfo exports the 'eacs' variable. Anyone to tell whether I understood the bare
information correctly? */
if (b) {
if (tigetstr("enacs"))
box(THIS->main, 0, 0);
else
wborder(THIS->main, '|', '|', '-', '-', '+', '+', '+', '+');
} else {
int width;
switch (b) {
case BORDER_NONE:
wborder(THIS->main, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ');
break;
case BORDER_ASCII:
wborder(THIS->main, '|', '|', '-', '-', '+', '+', '+', '+');
break;
case BORDER_ACS:
box(THIS->main, 0, 0);
break;
default:
return -1;
}
if (b && THIS->caption) {
width = MIN(getmaxx(THIS->main) - 1,
strlen(THIS->caption));
mvwaddnstr(THIS->main, 0, 1, THIS->caption, width);
}
return 0;
}
@ -424,21 +470,31 @@ static int WINDOW_print(void *_object, char *str, int x, int y)
do {
if (WINDOW_cursor_move(THIS, x, y) == -1)
return -1;
width = getmaxx(THIS->content) - x;
width = MIN(width, strlen(p));
width = strlen(p);
if (!IS_WRAPPED)
width = MIN(width, getmaxx(THIS->content) - x);
/* waddnstr, as being subsequent calls to waddch,
* continuously overrides the last character in a window
* after reached that end. But we want a clear cut there */
width = MIN(width, getmaxx(THIS->content) *
(getmaxy(THIS->content) - y) - x);
if ((q = strchr(p, '\n')))
width = MIN(width, q - p);
waddnstr(THIS->content, p, width);
p += width;
if (*p == '\n')
x = getcurx(THIS->content);
y = getcury(THIS->content);
if (y == getmaxy(THIS->content) - 1)
break;
if (*p == '\n') {
y++;
p++;
if (!THIS->wrap)
break;
x = 0;
y++;
if (y >= getmaxy(THIS->content))
break;
}
if (*p)
x = 0;
} while (*p);
WINDOW_cursor_move(THIS, x, y);
return 0;
}
@ -523,21 +579,7 @@ 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)
{
/* wtimeout() and wgetch() cause ncurses, for whatever reason, to mess up the panel
layout: the particular window gets risen to the top of everything. Consequently
I use the stdscr here which appears to not suffer from this. */
if (timeout >= 0)
timeout(timeout);
*ret = getch();
if (*ret == ERR) {
/* Had a timeout, the manual doesn't define any errors to happen for wgetch()
besides NULL pointer arguments. The only source of ERR is timeout expired. */
if (timeout >= 0)
*ret = 0;
}
if (timeout >= 0)
timeout(-1);
*ret = INPUT_get(timeout);
return 0;
}
@ -588,32 +630,35 @@ static int WINDOW_show(void *_object)
}
/**
* Callback for data on stdin
* @fd: stdin means 0
* @type: having watched stdin for read means GB_WATCH_READ
* @param: NULL since we don't need it
* This function raises the Read event. Since only one callback appears to be able to
* be registered for an fd, there can only be one window to raise this event, i.e.
* the one being set to "have the focus" via Window.SetFocus()
* 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,
* if the window can raise events. Otherwise let the parent screen raise the
* event.
*/
static void WINDOW_read_callback(int fd, int type, void *param)
void WINDOW_raise_read(void *_object)
{
GB.Raise(focused, EVENT_Read, 0);
if (!_object)
_object = focused;
if (_object && GB.CanRaise(_object, EVENT_Read))
GB.Raise(_object, EVENT_Read, 0);
else
SCREEN_raise_read(_object ? THIS->parent : NULL);
}
/**
* Bring the given nc_window to focus. That means that this window now raises the
* Read event if data arrives on stdin. The global variable @focussed is used to
* keep track of that very window
* Bring the given window to focus. That means that this window now raises the
* Read event whenever data arrives at the input queue _and_ the window can
* raise events. The global variable @focused is used to keep track of that
* very window.
*/
static int WINDOW_setfocus(void *_object)
{
if (!focused)
GB.Watch(0, GB_WATCH_READ, WINDOW_read_callback, 0);
else
if (focused)
GB.Unref((void **) &focused);
focused = THIS;
GB.Ref((void **) &focused);
/* We watch since SCREEN_init() */
return 0;
}
@ -649,16 +694,19 @@ static int WINDOW_import(void *_object, WINDOW *imp)
static int WINDOW_attrs_driver(void *_object, int attr, int req)
{
switch (req) {
case ATTR_DRV_RET:
return getattrs(THIS->content);
case ATTR_DRV_ON:
case ATTR_DRV_RET:
return getattrs(THIS->content);
case ATTR_DRV_ON:
if (attr == A_NORMAL)
wattrset(THIS->content, A_NORMAL);
else
wattron(THIS->content, attr);
break;
case ATTR_DRV_OFF:
wattroff(THIS->content, attr);
break;
case ATTR_DRV_COL:
wbkgd(THIS->content, attr | ' ');
break;
case ATTR_DRV_OFF:
wattroff(THIS->content, attr);
break;
case ATTR_DRV_COL:
wbkgd(THIS->content, attr | ' ');
}
return 0;
}
@ -685,112 +733,28 @@ static int WINDOW_char_attrs_driver(void *_object, int attr, int x, int y, int r
WINDOW_cursor_move(THIS, x, y);
ch = winch(THIS->content);
switch (req) {
case ATTR_DRV_RET:
res = ch & A_ATTRIBUTES;
goto _cleanup;
case ATTR_DRV_ON:
case ATTR_DRV_RET:
res = ch & A_ATTRIBUTES;
goto cleanup;
case ATTR_DRV_ON:
if (attr == A_NORMAL)
wchgat(THIS->content, 1, A_NORMAL, PAIR_NUMBER(ch), NULL);
else
wchgat(THIS->content, 1, (ch & A_ATTRIBUTES) | attr, PAIR_NUMBER(ch), NULL);
break;
case ATTR_DRV_OFF:
wchgat(THIS->content, 1, (ch & A_ATTRIBUTES) & ~attr, PAIR_NUMBER(ch), NULL);
break;
case ATTR_DRV_COL:
wchgat(THIS->content, 1, (ch & A_ATTRIBUTES), PAIR_NUMBER(attr), NULL);
break;
case ATTR_DRV_OFF:
wchgat(THIS->content, 1, (ch & A_ATTRIBUTES) & ~attr, PAIR_NUMBER(ch), NULL);
break;
case ATTR_DRV_COL:
wchgat(THIS->content, 1, (ch & A_ATTRIBUTES), PAIR_NUMBER(attr), NULL);
}
res = 0;
_cleanup:
cleanup:
WINDOW_cursor_move(THIS, ox, oy);
return res;
}
/*
* Window stream functions
*/
static int Window_stream_open(GB_STREAM *stream, const char *path, int mode, void *data)
{
return -1;
}
static int Window_stream_close(GB_STREAM *stream)
{
return -1;
}
static int Window_stream_read(GB_STREAM *stream, char *buffer, int len)
{
STREAM_PROLOGUE();
int key, res;
/* We only support reading Byte or Integer. */
switch (len) {
case sizeof(char):
return Window_stream_getchar(stream, buffer);
case sizeof(int):
res = WINDOW_key_timeout(THIS, -1, &key);
*((int *) buffer) = key;
return res;
default:
return -1;
}
}
static int Window_stream_getchar(GB_STREAM *stream, char *buffer)
{
STREAM_PROLOGUE();
int key, res;
res = WINDOW_key_timeout(THIS, -1, &key);
*buffer = (char) key;
return res;
}
static int Window_stream_write(GB_STREAM *stream, char *buffer, int len)
{
STREAM_PROLOGUE();
/* However, we terminate at the first NUL byte */
return WINDOW_print(THIS, buffer, -1, -1);
}
static int Window_stream_seek(GB_STREAM *steram, int64_t pos, int whence)
{
return -1;
}
static int Window_stream_tell(GB_STREAM *stream, int64_t *pos)
{
return -1;
}
static int Window_stream_flush(GB_STREAM *stream)
{
REAL_REFRESH();
return 0;
}
static int Window_stream_eof(GB_STREAM *stream)
{
int n;
/* Safest method */
if (ioctl(0, TIOCINQ, &n) == -1)
return -1;
return n ? 1 : 0;
}
static int Window_stream_lof(GB_STREAM *stream, int64_t *len)
{
return ioctl(0, TIOCINQ, len);
}
static int Window_stream_handle(GB_STREAM *stream)
{
/* There's no clear 'handle', we operate on stdin or stdout depending on context */
return -1;
}
/*
* Window class
*/
@ -813,20 +777,21 @@ BEGIN_PROPERTY(Window_Background)
}
COLOR_setpair(pair, f, b);
wbkgd(THIS->content, COLOR_PAIR(pair) | ' ');
REFRESH();
END_PROPERTY
BEGIN_PROPERTY(Window_Border)
bool b;
int b;
int x, y, w, h;
if (READ_PROPERTY) {
GB.ReturnBoolean(THIS->border);
GB.ReturnInteger(THIS->border);
return;
}
b = VPROP(GB_BOOLEAN);
b = VPROP(GB_INTEGER);
if (b == THIS->border)
return;
@ -855,13 +820,21 @@ BEGIN_PROPERTY(Window_Border)
END_PROPERTY
BEGIN_PROPERTY(Window_Buffered)
BEGIN_PROPERTY(Window_Caption)
if (READ_PROPERTY) {
GB.ReturnBoolean(THIS->buffered);
if (!THIS->caption)
GB.ReturnNull();
else
GB.ReturnString(THIS->caption);
return;
}
THIS->buffered = VPROP(GB_BOOLEAN);
THIS->caption = GB.NewZeroString(PSTRING());
if (HAS_BORDER) {
WINDOW_draw_border(THIS, 1);
REFRESH();
}
END_PROPERTY
@ -917,6 +890,7 @@ BEGIN_PROPERTY(Window_Foreground)
}
COLOR_setpair(pair, f, b);
wbkgd(THIS->content, COLOR_PAIR(pair) | ' ');
REFRESH();
END_PROPERTY
@ -975,7 +949,7 @@ BEGIN_PROPERTY(Window_Y)
END_PROPERTY
BEGIN_METHOD(Window_new, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h)
BEGIN_METHOD(Window_new, GB_OBJECT parent; GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h)
WINDOW *new;
@ -983,15 +957,11 @@ BEGIN_METHOD(Window_new, GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h)
GB.Error("Not in NCurses mode");
return;
}
new = newwin(MISSING(h) ? LINES : VARG(h), MISSING(w) ? COLS : VARG(w),
MISSING(y) ? 0 : VARG(y), MISSING(x) ? 0 : VARG(x));
new = newwin(VARGOPT(h, LINES), VARGOPT(w, COLS),
VARGOPT(y, 0), VARGOPT(x, 0));
WINDOW_import(THIS, new);
if (!GB.Parent(_object))
GB.Attach(THIS, (void *) GB.Application.StartupClass(), "Window");
THIS->stream.desc = &WindowStream;
THIS->stream.tag = THIS;
THIS->parent = VARGOPT(parent, SCREEN_get_active());
REFRESH();
@ -1026,12 +996,8 @@ BEGIN_METHOD(Window_Ask, GB_STRING opts; GB_INTEGER tries)
char *o, c[2];
miss = MISSING(tries);
if (!miss)
t = VARG(tries);
else
t = -1; /* to silence the compiler */
o = STRING(opts);
t = VARGOPT(tries, -1);
o = GB.ToZeroString(ARG(opts));
while (miss || t--) {
ch = getch();
@ -1049,7 +1015,7 @@ BEGIN_METHOD(Window_Ask, GB_STRING opts; GB_INTEGER tries)
END_METHOD
BEGIN_METHOD_VOID(Window_Bottom)
BEGIN_METHOD_VOID(Window_Lower)
WINDOW_bottom(THIS);
REFRESH();
@ -1071,6 +1037,54 @@ BEGIN_METHOD_VOID(Window_Drain)
END_METHOD
BEGIN_METHOD(Window_DrawHLine, GB_INTEGER x; GB_INTEGER y; GB_INTEGER len; GB_STRING ch; GB_INTEGER thickness)
int ox, oy, gx, gy;
char c;
int length, t;
int i;
getyx(THIS->content, oy, ox);
c = *(STRING(ch));
length = VARG(len);
t = VARGOPT(thickness, 1);
gx = VARG(x);
gy = VARG(y);
for (i = 0; i < t; i++) {
WINDOW_cursor_move(THIS, gx, gy);
whline(THIS->content, c, length);
gy++;
}
WINDOW_cursor_move(THIS, ox, oy);
REFRESH();
END_METHOD
BEGIN_METHOD(Window_DrawVLine, GB_INTEGER x; GB_INTEGER y; GB_INTEGER len; GB_STRING ch; GB_INTEGER thickness)
int ox, oy, gx, gy;
char c;
int length, t;
int i;
getyx(THIS->content, oy, ox);
c = *(STRING(ch));
length = VARG(len);
t = VARGOPT(thickness, 1);
gx = VARG(x);
gy = VARG(y);
for (i = 0; i < t; i++) {
WINDOW_cursor_move(THIS, gx, gy);
wvline(THIS->content, c, length);
gx++;
}
WINDOW_cursor_move(THIS, ox, oy);
REFRESH();
END_METHOD
BEGIN_METHOD_VOID(Window_Full)
WINDOW_move(THIS, 0, 0);
@ -1088,10 +1102,7 @@ BEGIN_METHOD(Window_Get, GB_INTEGER x; GB_INTEGER y; GB_INTEGER len)
char *ret;
/* if no @len is given, we return until the end of the line */
if (MISSING(len))
l = -1;
else
l = VARG(len);
l = VARGOPT(len, -1);
WINDOW_get_str(THIS, VARG(x), VARG(y), l, &ret);
GB.ReturnNewZeroString(ret);
GB.Free((void **) &ret);
@ -1105,36 +1116,9 @@ BEGIN_METHOD_VOID(Window_Hide)
END_METHOD
BEGIN_METHOD(Window_DrawHLine, GB_INTEGER x; GB_INTEGER y; GB_INTEGER len; GB_STRING ch; GB_INTEGER thickness)
int ox, oy, gx, gy;
char c;
int length, t;
int i;
getyx(THIS->content, oy, ox);
c = *(STRING(ch));
length = VARG(len);
if (MISSING(thickness))
t = 1;
else
t = VARG(thickness);
gx = VARG(x);
gy = VARG(y);
for (i = 0; i < t; i++) {
WINDOW_cursor_move(THIS, gx, gy);
whline(THIS->content, c, length);
gy++;
}
WINDOW_cursor_move(THIS, ox, oy);
REFRESH();
END_METHOD
BEGIN_METHOD(Window_Insert, GB_STRING text; GB_INTEGER x; GB_INTEGER y)
WINDOW_insert(THIS, STRING(text), MISSING(x) ? -1 : VARG(x), MISSING(y) ? -1 : VARG(y));
WINDOW_insert(THIS, GB.ToZeroString(ARG(text)), VARGOPT(x, -1), VARGOPT(y, -1));
REFRESH();
END_METHOD
@ -1148,14 +1132,21 @@ END_METHOD
BEGIN_METHOD(Window_Move, GB_INTEGER x; GB_INTEGER y)
WINDOW_move(THIS, MISSING(x) ? -1 : VARG(x), MISSING(y) ? -1 : VARG(y));
WINDOW_move(THIS, VARGOPT(x, -1), VARGOPT(y, -1));
REFRESH();
END_METHOD
BEGIN_METHOD(Window_Print, GB_STRING text; GB_INTEGER x; GB_INTEGER y)
WINDOW_print(THIS, STRING(text), MISSING(x) ? -1 : VARG(x), MISSING(y) ? -1 : VARG(y));
WINDOW_print(THIS, GB.ToZeroString(ARG(text)), VARGOPT(x, -1), VARGOPT(y, -1));
REFRESH();
END_METHOD
BEGIN_METHOD_VOID(Window_Raise)
WINDOW_top(THIS);
REFRESH();
END_METHOD
@ -1166,13 +1157,13 @@ BEGIN_METHOD(Window_PrintCenter, GB_STRING text)
int x, y;
char *p, *q;
p = STRING(text);
p = GB.ToZeroString(ARG(text));
while ((q = strchr(p, '\n'))) {
lines++;
p = q + 1;
}
p = STRING(text);
p = GB.ToZeroString(ARG(text));
y = (getmaxy(THIS->content) - lines) / 2;
while (lines--) {
if (!lines) {
@ -1192,16 +1183,30 @@ BEGIN_METHOD(Window_PrintCenter, GB_STRING text)
END_METHOD
BEGIN_METHOD_VOID(Window_Refresh)
BEGIN_METHOD(Window_Resize, GB_INTEGER w; GB_INTEGER h)
REAL_REFRESH();
WINDOW_resize(THIS, VARGOPT(w, -1), VARGOPT(h, -1));
REFRESH();
END_METHOD
BEGIN_METHOD(Window_Resize, GB_INTEGER w; GB_INTEGER h)
BEGIN_METHOD(Window_Read, GB_INTEGER timeout)
WINDOW_resize(THIS, MISSING(w) ? -1 : VARG(w), MISSING(h) ? -1 : VARG(h));
REFRESH();
int t;
int ret;
t = VARGOPT(timeout, -1);
WINDOW_key_timeout(THIS, t, &ret);
GB.ReturnInteger(ret);
END_METHOD
BEGIN_METHOD_VOID(Window_ReadLine)
/*abbruch-kondition? ein backspace mehr, als zeichen vorhanden? eine
bestimmte taste? NULL wird zurueckgegeben. \n und \x4 werden nicht
geliefert.*/
END_METHOD
@ -1218,55 +1223,6 @@ BEGIN_METHOD_VOID(Window_SetFocus)
END_METHOD
BEGIN_METHOD_VOID(Window_Top)
WINDOW_top(THIS);
REFRESH();
END_METHOD
BEGIN_METHOD(Window_DrawVLine, GB_INTEGER x; GB_INTEGER y; GB_INTEGER len; GB_STRING ch; GB_INTEGER thickness)
int ox, oy, gx, gy;
char c;
int length, t;
int i;
getyx(THIS->content, oy, ox);
c = *(STRING(ch));
length = VARG(len);
if (MISSING(thickness))
t = 1;
else
t = VARG(thickness);
gx = VARG(x);
gy = VARG(y);
for (i = 0; i < t; i++) {
WINDOW_cursor_move(THIS, gx, gy);
wvline(THIS->content, c, length);
gx++;
}
WINDOW_cursor_move(THIS, ox, oy);
REFRESH();
END_METHOD
BEGIN_METHOD(Window_WaitKey, GB_INTEGER timeout)
int t;
int ret;
if (MISSING(timeout))
t = -1;
else
t = VARG(timeout);
WINDOW_key_timeout(THIS, t, &ret);
GB.ReturnInteger(ret);
END_METHOD
/*
* .Window.Attrs virtual class
*/
@ -1278,30 +1234,35 @@ BEGIN_PROPERTY(WindowAttrs_Normal)
GB.ReturnBoolean(getattrs(THIS->content) == A_NORMAL);
if (VPROP(GB_BOOLEAN))
wattrset(THIS->content, A_NORMAL);
REFRESH();
END_PROPERTY
BEGIN_PROPERTY(WindowAttrs_Underline)
WIN_ATTR_METHOD_BOOL(A_UNDERLINE);
WIN_ATTR_METHOD(A_UNDERLINE);
REFRESH();
END_PROPERTY
BEGIN_PROPERTY(WindowAttrs_Reverse)
WIN_ATTR_METHOD_BOOL(A_REVERSE);
WIN_ATTR_METHOD(A_REVERSE);
REFRESH();
END_PROPERTY
BEGIN_PROPERTY(WindowAttrs_Blink)
WIN_ATTR_METHOD_BOOL(A_BLINK);
WIN_ATTR_METHOD(A_BLINK);
REFRESH();
END_PROPERTY
BEGIN_PROPERTY(WindowAttrs_Bold)
WIN_ATTR_METHOD_BOOL(A_BOLD);
WIN_ATTR_METHOD(A_BOLD);
REFRESH();
END_PROPERTY
@ -1319,6 +1280,7 @@ BEGIN_PROPERTY(WindowAttrs_Color)
return;
}
WINDOW_attrs_driver(THIS, COLOR_PAIR(pair), ATTR_DRV_COL);
REFRESH();
END_PROPERTY
@ -1334,33 +1296,34 @@ BEGIN_PROPERTY(CharAttrs_Normal)
return;
}
WINDOW_char_attrs_driver(THIS, A_NORMAL, THIS->pos.col, THIS->pos.line, ATTR_DRV_ON);
REFRESH();
END_PROPERTY
BEGIN_PROPERTY(CharAttrs_Underline)
CHAR_ATTR_METHOD_BOOL(A_UNDERLINE);
CHAR_ATTR_METHOD(A_UNDERLINE);
REFRESH();
END_PROPERTY
BEGIN_PROPERTY(CharAttrs_Reverse)
CHAR_ATTR_METHOD_BOOL(A_REVERSE);
CHAR_ATTR_METHOD(A_REVERSE);
REFRESH();
END_PROPERTY
BEGIN_PROPERTY(CharAttrs_Blink)
CHAR_ATTR_METHOD_BOOL(A_BLINK);
CHAR_ATTR_METHOD(A_BLINK);
REFRESH();
END_PROPERTY
BEGIN_PROPERTY(CharAttrs_Bold)
CHAR_ATTR_METHOD_BOOL(A_BOLD);
CHAR_ATTR_METHOD(A_BOLD);
REFRESH();
END_PROPERTY
@ -1386,18 +1349,18 @@ BEGIN_PROPERTY(CharAttrs_Color)
END_PROPERTY
#define TIMEOUT_NOTIMEOUT -1
GB_DESC CWindowDesc[] =
{
GB_DECLARE("Window", sizeof(struct nc_window)),
GB_INHERITS("Stream"),
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"),
@ -1405,9 +1368,9 @@ GB_DESC CWindowDesc[] =
GB_PROPERTY("Background", "i", Window_Background),
GB_PROPERTY("Paper", "i", Window_Background),
GB_PROPERTY("Border", "b", Window_Border),
GB_PROPERTY("Border", "i", Window_Border),
GB_PROPERTY("Buffered", "b", Window_Buffered),
GB_PROPERTY("Caption", "s", Window_Caption),
GB_PROPERTY_READ("ContainerHeight", "i", Window_ContainerHeight),
GB_PROPERTY_READ("ContainerH", "i", Window_ContainerHeight),
@ -1432,14 +1395,14 @@ GB_DESC CWindowDesc[] =
GB_PROPERTY("Y", "i", Window_Y),
/* Methods */
GB_METHOD("_new", NULL, Window_new, "[(X)i(Y)i(W)i(H)i]"),
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("Bottom", NULL, Window_Bottom, NULL),
GB_METHOD("Top", NULL, Window_Top, NULL),
GB_METHOD("Lower", NULL, Window_Lower, NULL),
GB_METHOD("Raise", NULL, Window_Raise, NULL),
GB_METHOD("Cls", NULL, Window_Cls, NULL),
@ -1463,12 +1426,11 @@ GB_DESC CWindowDesc[] =
GB_METHOD("Move", NULL, Window_Move, "[(X)i(Y)i]"),
GB_METHOD("Resize", NULL, Window_Resize, "[(W)i(H)i]"),
GB_METHOD("Refresh", NULL, Window_Refresh, NULL),
GB_METHOD("Read", "i", Window_Read, "[(Timeout)i]"),
GB_METHOD("ReadLine", "s", Window_ReadLine, NULL),
GB_METHOD("SetFocus", NULL, Window_SetFocus, NULL),
GB_METHOD("WaitKey", "i", Window_WaitKey, "[(Timeout)i]"),
GB_END_DECLARE
};

View file

@ -28,40 +28,19 @@
#include "gambas.h"
#include "gb_common.h"
#include "c_ncurses.h"
#include "c_screen.h"
#define THIS ((CWINDOW *) _object)
#define HAS_BORDER (THIS->border)
#define IS_WRAPPED (THIS->wrap)
#define IS_BUFFERED (THIS->buffered)
/* This will produce final output on terminal screen */
#define REAL_REFRESH() WINDOW_real_refresh()
/* This macro is mostly called by Gambas implementation functions to request output on screen
(read: to check if the output is buffered and if not produce output by means of
REAL_REFRESH(). To check buffering, this needs to get an object parameter.) */
#define REFRESH() WINDOW_refresh(THIS)
/* Border constants */
enum {
BORDER_NONE = 0,
BORDER_ASCII,
BORDER_ACS
};
/* Translate linear (absolute) memory addresses and x,y coordinates into each other
most useful when wrapping is needed. */
#define A2XY(win, a, x, y) do { \
(x) = (a) % getmaxx(win); \
(y) = (a) / getmaxx(win); \
} while (0)
#define XY2A(win, x, y, a) do { \
(a) = (y) * getmaxx(win) + (x); \
} while (0)
/* Interpret the -1 values in coordinates as to insert the current cursor position */
#define MAKE_COORDS(win, x, y) do { \
if ((x) == -1) \
x = getcurx(win); \
if ((y) == -1) \
y = getcury(win); \
} while (0)
/* Check for out-of-range coordinates */
#define BAD_COORDS(win, x, y) ((x) < 0 || (x) >= getmaxx(win) || \
(y) < 0 || (y) >= getmaxy(win))
#define BAD_DIMENSION(w, h) ((w) <= 0 || (w) > COLS || \
(h) <= 0 || (h) > LINES)
/* Timeout constants */
enum {
TIMEOUT_NOTIMEOUT = -1
};
/* @reqs for *_attrs_driver() */
enum
@ -76,49 +55,18 @@ enum
ATTR_DRV_COL
};
#define WIN_ATTR_METHOD(b, a) do { \
if (READ_PROPERTY) \
GB.ReturnBoolean(WINDOW_attrs_driver( \
THIS, (a), ATTR_DRV_RET) \
& (a)); \
else \
WINDOW_attrs_driver(THIS, (a), \
(b) ? ATTR_DRV_ON : ATTR_DRV_OFF); \
} while (0)
#define WIN_ATTR_METHOD_BOOL(a) WIN_ATTR_METHOD(VPROP(GB_BOOLEAN), a)
/* Notice the wtouchln() line in the following macro. It seems that a chgat() from
nc_window_char_attrs_driver() doesn't mark anything dirty (no output on screen from
a REFRESH()). So to make the new attribute available immidiately, we touch the affected
line manually. A higher-callstack function may call REFRESH() to get output. */
#define CHAR_ATTR_METHOD(b, a) do { \
if (READ_PROPERTY) \
GB.ReturnBoolean(WINDOW_char_attrs_driver( \
THIS, (a), THIS->pos.col, THIS->pos.line, \
ATTR_DRV_RET) & (a)); \
else \
WINDOW_char_attrs_driver(THIS, (a), THIS->pos.col, \
THIS->pos.line, (b) ? ATTR_DRV_ON : ATTR_DRV_OFF); \
wtouchln(THIS->main, THIS->pos.line + (HAS_BORDER ? 1 : 0), 1, 1); \
} while(0)
#define CHAR_ATTR_METHOD_BOOL(a) CHAR_ATTR_METHOD(VPROP(GB_BOOLEAN), a)
#define STREAM_PROLOGUE() void *_object = stream->tag
//TODO: [-] Stream
// [-] background/foreground colors
typedef struct nc_window {
typedef struct {
GB_BASE ob;
GB_STREAM stream; /* Gambas stream structure to enable stream-related syntaxes */
WINDOW *main; /* The main window. */
CSCREEN *parent; /* The parent Screen */
WINDOW *main; /* The main window */
WINDOW *content; /* This window is used for all content-related operations. Its purpose is turning
the ncurses window borders which are inner-window to outer-window ones thus
separating border from content. If there is no border, this is the same as @main
otherwise a subwindow of it. */
otherwise a subwindow of it */
PANEL *pan; /* Panel of the main window to provide overlapping windows */
bool border; /* Whether there is a border */
int border; /* What kind of border */
bool wrap; /* Whether text shall be truncated or wrapped on line ends */
bool buffered; /* Whether the output via REFRESH() macro shall be buffered (only a call to
Window.Refresh() will then produce any output) */
char *caption; /* Text to be displayed in the main window if there is a border */
struct { /* This structure is used to pass a line and a column number to virtual objects */
int line;
int col;
@ -129,17 +77,8 @@ typedef struct nc_window {
extern GB_DESC CWindowDesc[];
extern GB_DESC CWindowAttrsDesc[];
extern GB_DESC CCharAttrsDesc[];
extern GB_STREAM_DESC WindowStream;
#endif
#define WINDOW_main_to_content() WINDOW_copy_window(THIS->main, THIS->content, 0, 0, \
getmaxx(THIS->content), \
getmaxy(THIS->content), 0, 0)
#define WINDOW_content_to_main() WINDOW_copy_window(THIS->content, THIS->main, 0, 0, \
getmaxx(THIS->content), \
getmaxy(THIS->content), 0, 0)
void WINDOW_real_refresh();
void WINDOW_refresh();
void WINDOW_raise_read(void *);
#endif /* __C_WINDOW_C */

588
gb.ncurses/src/input.c Normal file
View file

@ -0,0 +1,588 @@
/*
* input.c - gb.ncurses opaque input routines
*
* Copyright (C) 2012 Tobias Boege <tobias@gambas-buch.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#define __INPUT_C
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <linux/kd.h>
#include <time.h>
#include <sys/time.h>
#include <errno.h>
#include <ncurses.h>
#include "gambas.h"
#include "gb_common.h"
#include "main.h"
#include "input.h"
#include "c_window.h"
#define E_UNSUPP "Unsupported value"
#define E_NO_NODELAY "Could not initialise NoDelay mode"
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)
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
*/
int INPUT_init()
{
INPUT_mode(INPUT_CBREAK);
INPUT_repeater_delay(100);
return 0;
}
/**
* Input cleanup
*/
void INPUT_exit()
{
/* If we are still in NoDelay mode, exit it */
if (IN_NODELAY) {
INPUT_mode(INPUT_CBREAK);
}
}
/**
* 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)
return 0;
if (start && _watching)
INPUT_watch(!start);
GB.Watch(fd, start ? GB_WATCH_READ : GB_WATCH_NONE,
INPUT_callback, 0);
_watching = start;
return 0;
}
/**
* Function to be called by Gambas when data arrives
*/
static void INPUT_callback(int fd, int flag, intptr_t arg)
{
WINDOW_raise_read(NULL);
}
/**
* 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 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;
}
/**
* Return or set the current input mode
* @mode: one of INPUT_* enums
*/
int INPUT_mode(int mode)
{
if (mode == INPUT_RETURN)
return _input;
if (mode == _input)
return 0;
INPUT_watch(0);
if (_input == INPUT_NODELAY)
INPUT_exit_nodelay();
switch (mode) {
case INPUT_COOKED:
noraw();
nocbreak();
break;
case INPUT_CBREAK:
noraw();
cbreak();
break;
case INPUT_RAW:
nocbreak();
raw();
break;
case INPUT_NODELAY:
if (INPUT_init_nodelay() == -1) {
GB.Error(E_NO_NODELAY);
/* We return 0 to not override the previous
* error message with the one emitted by the
* caller if we return error */
return 0;
}
break;
default:
GB.Error(E_UNSUPP);
return -1;
}
_input = mode;
INPUT_watch(1);
return 0;
}
/**
* Retrieve input from ncurses
*/
static int INPUT_get_ncurses(int timeout)
{
int ret;
if (timeout >= 0)
timeout(timeout);
ret = getch();
if (ret == ERR) {
/* Had a timeout, the manual doesn't define any errors to
happen for wgetch() besides NULL pointer arguments. The
only source of ERR is timeout expired. */
if (timeout >= 0)
ret = 0;
}
if (timeout >= 0)
timeout(-1);
return ret;
}
/*
* Return codes from INPUT_trans_keycode()
*/
enum {
TRANS_NEED_MORE = -1,
TRANS_KEY_MIN
};
/*
* States of the modifier keys that we recognise
*/
enum {
MOD_NONE = 0,
MOD_SHIFT = 1,
MOD_CTRL = 2,
MOD_ALT = 4
};
#define IS_BREAK(k) ((k) & 0x80)
/**
* Translate a keycode (or a sequence) to an ncurses compatible int
* @kc: keycode to translate
* This function returns one of the above codes. TRANS_NEED_MORE means that
* the given @kc is considered part of a multi-keycode sequence (or is a
* Shift, Control, Alt), so we need more keys which are (in first case
* likely) available without waiting from the tty driver then; in the latter
* case (modifier keys), it does not count as a keypress anyway.
* Note that after a usual tty key sequence is assembled it is passed to the
* driver to try to get an escape sequence for it. If there is no escape
* seqeuence for that key, we can simply return the plain value. Otherwise
* we exploit ncurses key_defined() routine to translate the sequence to an
* int for us. This gives an int like ncurses getch() would do.
*/
static int INPUT_trans_keycode(unsigned char kc)
{
/* Pause/Break has the largest scancode: e1 1d 45 e1 9d c5 */
static unsigned char codes[8];
static int num = 0;
static int modifiers = MOD_NONE;
unsigned char seq[8];
struct kbentry kbe;
struct kbsentry kbs;
register int mod;
/* TODO: Hope they're fixed */
#define KEYCODE_LCTRL 0x1d
#define KEYCODE_RCTRL 0x61
#define KEYCODE_ALT 0x38
#define KEYCODE_LSHIFT 0x2a
#define KEYCODE_RSHIFT 0x36
/* Modifiers */
switch (kc) {
case KEYCODE_LCTRL:
case KEYCODE_RCTRL:
mod = MOD_CTRL;
goto apply_mod;
case KEYCODE_ALT:
mod = MOD_ALT;
goto apply_mod;
case KEYCODE_LSHIFT:
case KEYCODE_RSHIFT:
mod = MOD_SHIFT;
goto apply_mod;
default:
goto account_key;
}
apply_mod:
if (IS_BREAK(kc))
modifiers &= ~mod;
else
modifiers |= mod;
return TRANS_NEED_MORE;
account_key:
codes[num++] = kc;
/* Break key, sends make and break code together */
if (codes[0] == '\xe1') {
if (num == 6)
return KEY_BREAK;
else
return TRANS_NEED_MORE;
}
/* Keys with two keycodes, no matter, those correspond to
* single-keycode keys, we can safely use @kc */
if (codes[0] == '\xe0' && num != 2)
return TRANS_NEED_MORE;
/* TODO: what to do with ctrl- ? */
/* Set table and get action code */
if (modifiers & MOD_ALT) {
if (modifiers & MOD_SHIFT)
kbe.kb_table = K_ALTSHIFTTAB;
else
kbe.kb_table = K_ALTTAB;
} else if (modifiers & MOD_SHIFT) {
kbe.kb_table = K_SHIFTTAB;
} else {
kbe.kb_table = K_NORMTAB;
}
kbe.kb_index = kc & 0x7f;
kbe.kb_value = 0;
ioctl(no_delay.fd, KDGKBENT, &kbe);
seq[0] = (unsigned char) kbe.kb_value;
/* Has an escape sequence? */
kbs.kb_func = seq[0];
ioctl(no_delay.fd, KDGKBSENT, &kbs);
if (kbs.kb_string[0]) {
strncpy((char *) seq, (char *) kbs.kb_string, 7);
seq[7] = '\0';
return key_defined((char *) seq);
} else {
return (int) seq[0];
}
}
/**
* Change the currently pressed key
* @key: current key. 0 means that no key is pressed
* This install the repeater
*/
static void INPUT_change_pressed(int key)
{
fprintf(stderr, "%d\n", no_delay.delay);
if (key == 0) {
GB.Unref((void **) no_delay.timer);
no_delay.timer = NULL;
} else {
no_delay.timer = GB.Every(no_delay.delay,
(GB_TIMER_CALLBACK) INPUT_nodelay_repeater, 0);
}
no_delay.pressed = key;
}
/**
* Retrieve input in NoDelay mode, using console_ioctl(4).
* If there is a key pressed and no input available on the input queue,
* the pressed key is returned.
* @timeout: timeout in milliseconds. If that timeout expires, we return 0.
* Note that @timeout can only be in the scope of deciseconds and is
* silently cut down to those.
* The Repeater Delay applies to this function, too, in that we only return
* a pressed key when we waited for that delay.
* Note carefully, that there is an emergency exit: pressing ESC thrice
* during one second will immediately abort NoDelay mode and enter CBreak.
*/
static int INPUT_get_nodelay(int timeout)
{
static char esc = 0;
struct termios old, new;
unsigned char b;
static time_t stamp;
int ret, res, num;
int key; /* This will already be ncurses compatible */
struct timeval tv1, tv2;
if (timeout > -1) {
gettimeofday(&tv1, NULL);
tcgetattr(no_delay.fd, &old);
memcpy(&new, &old, sizeof(new));
}
recalc_timeout:
#define USEC2MSEC(us) (us / 1000)
#define MSEC2USEC(ms) (ms * 1000)
#define MSEC2DSEC(ms) (ms / 100)
#define DSEC2MSEC(ds) (ds * 100)
#define SEC2MSEC(s) (s * 1000)
#define MSEC2SEC(ms) (ms / 1000)
if (timeout > -1) {
gettimeofday(&tv2, NULL);
timeout -= USEC2MSEC(tv2.tv_usec - tv1.tv_usec);
timeout -= SEC2MSEC(tv2.tv_sec - tv1.tv_sec);
if (timeout < 0) {
ret = 0;
goto cleanup;
}
}
/* Set timeout */
ioctl(no_delay.fd, TIOCINQ, &num);
/* We don't need to set any timeout if there are bytes available */
if (!num && timeout > -1) {
new.c_cc[VTIME] = MSEC2DSEC(timeout);
tcsetattr(no_delay.fd, TCSANOW, &new);
gettimeofday(&tv1, NULL);
}
/* Begin reading */
if (no_delay.pressed && !num) {
usleep(MSEC2USEC(no_delay.delay));
ret = no_delay.pressed;
goto cleanup;
}
/* Try to stick to the user-supplied timeout */
ioctl(no_delay.fd, TIOCINQ, &num);
if ((res = read(no_delay.fd, &b, 1)) == -1 && errno == EINTR) {
goto recalc_timeout;
} else if (res == 0) { /* Timeout expired */
ret = 0;
goto cleanup;
} else { /* Got a key */
/* Emergency exit from NoDelay mode */
#define KEYCODE_ESC 0x01
if (b == KEYCODE_ESC) {
if (time(NULL) - stamp > 0)
esc = 0;
if (++esc == 3) {
INPUT_exit_nodelay();
INPUT_mode(INPUT_CBREAK);
return 0;
}
stamp = time(NULL);
}
/* We use ncurses keys for operations on our no_delay data */
if ((key = INPUT_trans_keycode(b)) == TRANS_NEED_MORE)
goto recalc_timeout;
/* Ignore break codes, except when it is the currently
pressed key */
if (IS_BREAK(b)) {
if (no_delay.pressed == key)
INPUT_change_pressed(0);
/* Key release isn't visible to the gambas programmer
* and thus not really an event to gb.ncurses. If
* time is left, we try again reading another key */
goto recalc_timeout;
} else {
INPUT_change_pressed(key);
}
}
ret = key;
cleanup:
if (timeout > -1)
tcsetattr(no_delay.fd, TCSANOW, &old);
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);
}

61
gb.ncurses/src/input.h Normal file
View file

@ -0,0 +1,61 @@
/*
* input.h - gb.ncurses opaque input routines for use of either ncurses or
* the terminal driver directly
*
* Copyright (C) 2012 Tobias Boege <tobias@gambas-buch.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#ifndef __INPUT_H
#define __INPUT_H
enum {
/* Return the current mode */
INPUT_RETURN,
/* Line discipline, signal generation */
INPUT_COOKED,
/* No line discipline, signal generation */
INPUT_CBREAK,
/* No line discipline, no signal generation */
INPUT_RAW,
/* Use terminal driver, enabled to use raw scancodes
(which are internally converted to ncurses keys but enable to
distinguish key press and release) */
INPUT_NODELAY
};
enum {
/* Let INPUT_repeater_delay() return the current value of the
repeater delay */
REPEATER_RETURN = -1
};
int INPUT_init();
void INPUT_exit();
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 /* __INPUT_H */

View file

@ -21,7 +21,6 @@
#define __MAIN_C
#include "c_ncurses.h"
#include "c_window.h"
#include "c_key.h"
#include "c_color.h"
@ -32,38 +31,86 @@ GB_INTERFACE GB EXPORT;
GB_DESC *GB_CLASSES[] EXPORT =
{
CNCursesDesc,
CWindowDesc,
CWindowAttrsDesc,
CCharAttrsDesc,
CKeyDesc,
CColorDesc,
CColorCapabilitiesDesc,
CColorPairDesc,
CColorContentDesc,
CScreenDesc,
NULL
CScreenDesc,
CWindowDesc,
CWindowAttrsDesc,
CCharAttrsDesc,
CKeyDesc,
CColorDesc,
CColorCapabilitiesDesc,
CColorPairDesc,
CColorContentDesc,
NULL
};
static void hook_error(int code, char *error, char *where)
static bool _init = FALSE;
/**
* Returns if we are in ncurses mode
*/
bool MAIN_running()
{
NCURSES_exit();
return _init && (!isendwin() || stdscr);
}
static void hook_main(int *argc, char **argv)
/**
* Component-global initialisation
* Start ncurses and prepare stdscr. Call other relevant initialisation
* routines.
*/
static void MAIN_init()
{
NCURSES_init();
if (_init)
return;
initscr();
keypad(stdscr, TRUE);
SCREEN_init();
COLOR_init();
refresh();
_init = TRUE;
}
/**
* Cleanup and exit ncurses
*/
static void MAIN_exit()
{
if (_init) {
SCREEN_exit();
endwin();
_init = FALSE;
}
}
/**
* Error hook
*/
static void MAIN_hook_error(int code, char *error, char *where)
{
MAIN_exit();
}
/**
* Main hook
*/
static void MAIN_hook_main(int *argc, char **argv)
{
MAIN_init();
}
int EXPORT GB_INIT()
{
GB.Hook(GB_HOOK_ERROR, (void *) hook_error);
GB.Hook(GB_HOOK_MAIN, (void *) hook_main);
GB.Hook(GB_HOOK_ERROR, (void *) MAIN_hook_error);
GB.Hook(GB_HOOK_MAIN, (void *) MAIN_hook_main);
return 0;
}
void EXPORT GB_EXIT()
{
NCURSES_exit();
MAIN_exit();
}

View file

@ -26,8 +26,12 @@
#include "gb_common.h"
#include "gambas.h"
#define NCURSES_RUNNING MAIN_running()
#ifndef __MAIN_C
extern GB_INTERFACE GB;
#endif
bool MAIN_running();
#endif /* __MAIN_H */