[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:
parent
5dd86fdb1b
commit
31cdd37ebd
12 changed files with 1288 additions and 653 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
|
@ -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 */
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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
588
gb.ncurses/src/input.c
Normal 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
61
gb.ncurses/src/input.h
Normal 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 */
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in a new issue