From 0a4dd6f9d21a7bcba4c5e406934aa4e021ff98a0 Mon Sep 17 00:00:00 2001 From: Tobias Boege Date: Wed, 13 Jun 2012 15:47:00 +0000 Subject: [PATCH] [GB.NCURSES] * NEW: Add ReadLine() function to Window that does the same as ncurses getstr(3X) but usable in all input modes * NEW: Encapsulate Input, Cursor and Border modes into static objects * NEW: Tidy up Color class in various places * NEW: Specifiying color values is now done via floats instead of ints * NEW: Remove ContainerW and ContainerH (and corresponding long forms) properties from Window class. It is always .Width/.Height + 2 * NEW: Rename Input.Repeater to Input.RepeatDelay * OPT: Remove unnecessary ncurses input mode changes * OPT: Tidy up Input and Screen code * OPT: Reduce useless calls to input queue read callback in Screen class * BUG: Add constant for already implemented "very visible" cursor mode * BUG: Use opaque input module in Window.Ask() * BUG: Note that NoDelay and Window/Screen events still not work git-svn-id: svn://localhost/gambas/trunk@4827 867c0c6c-44f3-4631-809d-bfa615b0a4ec --- gb.ncurses/src/Makefile.am | 2 +- gb.ncurses/src/c_color.c | 248 ++++++----- gb.ncurses/src/c_color.h | 12 +- gb.ncurses/src/{input.c => c_input.c} | 576 +++++++++++++++----------- gb.ncurses/src/{input.h => c_input.h} | 49 ++- gb.ncurses/src/c_key.c | 3 +- gb.ncurses/src/c_screen.c | 202 +++++---- gb.ncurses/src/c_screen.h | 9 + gb.ncurses/src/c_window.c | 294 +++++++------ gb.ncurses/src/c_window.h | 6 +- gb.ncurses/src/main.c | 19 +- 11 files changed, 822 insertions(+), 598 deletions(-) rename gb.ncurses/src/{input.c => c_input.c} (71%) rename gb.ncurses/src/{input.h => c_input.h} (64%) diff --git a/gb.ncurses/src/Makefile.am b/gb.ncurses/src/Makefile.am index 5efbc305d..b9c54bd58 100644 --- a/gb.ncurses/src/Makefile.am +++ b/gb.ncurses/src/Makefile.am @@ -13,5 +13,5 @@ gb_ncurses_la_SOURCES = \ c_key.h c_key.c \ c_color.h c_color.c \ c_screen.h c_screen.c \ - input.h input.c + c_input.h c_input.c diff --git a/gb.ncurses/src/c_color.c b/gb.ncurses/src/c_color.c index b68e06984..2b45f7fde 100644 --- a/gb.ncurses/src/c_color.c +++ b/gb.ncurses/src/c_color.c @@ -29,8 +29,8 @@ #include "c_color.h" #include "c_screen.h" -static int _index; static int _color; +static int _pair; /** * Colour initialisation @@ -41,6 +41,50 @@ void COLOR_init() use_default_colors(); } +/** + * Initialise a colour + * @index: the color number + * @r: red + * @g: green + * @b: blue + * Note that one gives colour amounts in floats 0.0 - 1.0 + */ +int COLOR_setcolor(short index, float r, float g, float b) +{ + return init_color(index, r * 1000, g * 1000, b * 1000); +} + +/** + * Initialise one field of a colour + * @index: color number + * @val: value as float 0.0 - 1.0 + * @what: field number + */ +int COLOR_setcolor_one(short index, float val, int what) +{ + short r, g, b; + float rf, gf, bf; + + color_content(index, &r, &g, &b); + rf = ((float) r) / 1000; + gf = ((float) g) / 1000; + bf = ((float) b) / 1000; + switch (what) { + case 0: + rf = val; + break; + case 1: + gf = val; + break; + case 2: + bf = val; + break; + default: + return -1; + } + return COLOR_setcolor(index, rf, gf, bf); +} + /** * Initialise a colour pair * @index: colour pair index @@ -105,81 +149,120 @@ static int COLOR_content(short color, short *r, short *g, short *b) * Color static class */ -BEGIN_PROPERTY(Color_Capabilities) +BEGIN_PROPERTY(Color_Available) - RETURN_SELF(); + GB.ReturnBoolean(has_colors()); END_PROPERTY -BEGIN_PROPERTY(Color_Colors) +BEGIN_PROPERTY(Color_CanChange) + + GB.ReturnBoolean(can_change_color()); + +END_PROPERTY + +BEGIN_PROPERTY(Color_Count) GB.ReturnInteger(COLORS); END_PROPERTY -BEGIN_PROPERTY(Color_Pairs) - - GB.ReturnInteger(COLOR_PAIRS); - -END_PROPERTY - BEGIN_METHOD(Color_get, GB_INTEGER index) - if (!PAIR_VALID(VARG(index))) { + if (!COLOR_VALID(VARG(index))) { GB.Error(GB_ERR_BOUND); return; } - _index = VARG(index); + _color = VARG(index); RETURN_SELF(); END_METHOD -BEGIN_METHOD(Color_Define, GB_INTEGER color; GB_INTEGER r; GB_INTEGER g; GB_INTEGER b) +BEGIN_METHOD(Color_Set, GB_INTEGER color; GB_INTEGER r; GB_INTEGER g; GB_INTEGER b) if (!COLOR_VALID(VARG(color))) { GB.Error(GB_ERR_BOUND); return; } init_color(VARG(color), VARG(r), VARG(g), VARG(b)); + REFRESH(); END_METHOD -BEGIN_METHOD(Color_Content, GB_INTEGER color) +/* + * .ColorInfo virtual class + */ - if (!COLOR_VALID(VARG(color))) { +BEGIN_PROPERTY(ColorInfo_Red) + + short red; + + if (READ_PROPERTY) { + COLOR_content(_color, &red, NULL, NULL); + GB.ReturnInteger(red); + return; + } + COLOR_setcolor_one(_color, VPROP(GB_FLOAT), 0); + REAL_REFRESH(); + +END_PROPERTY + +BEGIN_PROPERTY(ColorInfo_Green) + + short green; + + if (READ_PROPERTY) { + COLOR_content(_color, NULL, &green, NULL); + GB.ReturnInteger(green); + return; + } + COLOR_setcolor_one(_color, VPROP(GB_FLOAT), 1); + +END_PROPERTY + +BEGIN_PROPERTY(ColorInfo_Blue) + + short blue; + + if (READ_PROPERTY) { + COLOR_content(_color, NULL, NULL, &blue); + GB.ReturnInteger(blue); + return; + } + COLOR_setcolor_one(_color, VPROP(GB_FLOAT), 2); + +END_PROPERTY + +/* + * Pair static class + */ + +BEGIN_PROPERTY(Pair_Count) + + GB.ReturnInteger(COLOR_PAIRS); + +END_PROPERTY + +BEGIN_METHOD(Pair_get, GB_INTEGER index) + + if (!PAIR_VALID(VARG(index))) { GB.Error(GB_ERR_BOUND); return; } - _color = VARG(color); + _pair = VARG(index); RETURN_SELF(); END_METHOD /* - * .Color.Capabilities virtual class + * .PairInfo virtual class */ -BEGIN_PROPERTY(ColorCapabilities_Change) - - GB.ReturnBoolean(can_change_color()); - -END_PROPERTY - -BEGIN_PROPERTY(ColorCapabilities_Color) - - GB.ReturnBoolean(has_colors()); - -END_PROPERTY - -/* - * .Color.Pair virtual class - */ - -BEGIN_PROPERTY(ColorPair_Background) +BEGIN_PROPERTY(PairInfo_Background) short f, b; - pair_content(_index, &f, &b); + pair_content(_pair, &f, &b); if (READ_PROPERTY) { GB.ReturnInteger(b); return; @@ -189,17 +272,17 @@ BEGIN_PROPERTY(ColorPair_Background) return; } b = VPROP(GB_INTEGER); - COLOR_setpair(_index, f, b); + COLOR_setpair(_pair, f, b); REAL_REFRESH(); END_PROPERTY -BEGIN_PROPERTY(ColorPair_Foreground) +BEGIN_PROPERTY(PairInfo_Foreground) short f, b; - pair_content(_index, &f, &b); + pair_content(_pair, &f, &b); if (READ_PROPERTY) { GB.ReturnInteger(f); return; @@ -209,44 +292,12 @@ BEGIN_PROPERTY(ColorPair_Foreground) return; } f = VPROP(GB_INTEGER); - COLOR_setpair(_index, f, b); + COLOR_setpair(_pair, f, b); REAL_REFRESH(); END_PROPERTY -/* - * .Color.Content virtual class - */ - -BEGIN_PROPERTY(ColorContent_Red) - - short red; - - COLOR_content(_color, &red, NULL, NULL); - GB.ReturnInteger(red); - -END_PROPERTY - -BEGIN_PROPERTY(ColorContent_Green) - - short green; - - COLOR_content(_color, NULL, &green, NULL); - GB.ReturnInteger(green); - -END_PROPERTY - -BEGIN_PROPERTY(ColorContent_Blue) - - short blue; - - COLOR_content(_color, NULL, NULL, &blue); - GB.ReturnInteger(blue); - -END_PROPERTY - -GB_DESC CColorDesc[] = -{ +GB_DESC CColorDesc[] = { GB_DECLARE("Color", 0), GB_NOT_CREATABLE(), @@ -259,47 +310,44 @@ GB_DESC CColorDesc[] = GB_CONSTANT("Cyan", "i", COLOR_CYAN), GB_CONSTANT("White", "i", COLOR_WHITE), - GB_STATIC_PROPERTY_READ("Capabilities", ".Color.Capabilities", Color_Capabilities), - GB_STATIC_PROPERTY_READ("Colors", "i", Color_Colors), - GB_STATIC_PROPERTY_READ("Pairs", "i", Color_Pairs), + GB_STATIC_PROPERTY_READ("Available", "b", Color_Available), + GB_STATIC_PROPERTY_READ("CanChange", "b", Color_CanChange), + GB_STATIC_PROPERTY_READ("Count", "i", Color_Count), - GB_STATIC_METHOD("_get", ".Color.Pair", Color_get, "(Index)i"), - GB_STATIC_METHOD("Define", NULL, Color_Define, "(Color)i(R)i(G)i(B)i"), - GB_STATIC_METHOD("Content", ".Color.Content", Color_Content, "(Color)i"), + GB_STATIC_METHOD("_get", ".ColorInfo", Color_get, "(Index)i"), + GB_STATIC_METHOD("Set", NULL, Color_Set, "(Color)i(R)i(G)i(B)i"), GB_END_DECLARE }; -GB_DESC CColorCapabilitiesDesc[] = -{ - GB_DECLARE(".Color.Capabilities", 0), +GB_DESC CColorInfoDesc[] = { + GB_DECLARE(".ColorInfo", 0), GB_VIRTUAL_CLASS(), - GB_STATIC_PROPERTY_READ("Color", "b", ColorCapabilities_Color), - GB_STATIC_PROPERTY_READ("Change", "b", ColorCapabilities_Change), + GB_STATIC_PROPERTY("Red", "i", ColorInfo_Red), + GB_STATIC_PROPERTY("Green", "i", ColorInfo_Green), + GB_STATIC_PROPERTY("Blue", "i", ColorInfo_Blue), GB_END_DECLARE }; -GB_DESC CColorPairDesc[] = -{ - GB_DECLARE(".Color.Pair", 0), +GB_DESC CPairDesc[] = { + GB_DECLARE("Pair", 0), + GB_NOT_CREATABLE(), + + GB_STATIC_PROPERTY_READ("Count", "i", Pair_Count), + + GB_STATIC_METHOD("_get", ".PairInfo", Pair_get, "(Index)i"), + + GB_END_DECLARE +}; + +GB_DESC CPairInfoDesc[] = { + GB_DECLARE(".PairInfo", 0), GB_VIRTUAL_CLASS(), - GB_STATIC_PROPERTY("Background", "i", ColorPair_Background), - GB_STATIC_PROPERTY("Foreground", "i", ColorPair_Foreground), - - GB_END_DECLARE -}; - -GB_DESC CColorContentDesc[] = -{ - GB_DECLARE(".Color.Content", 0), - GB_VIRTUAL_CLASS(), - - GB_STATIC_PROPERTY_READ("Red", "i", ColorContent_Red), - GB_STATIC_PROPERTY_READ("Green", "i", ColorContent_Green), - GB_STATIC_PROPERTY_READ("Blue", "i", ColorContent_Blue), + GB_STATIC_PROPERTY("Background", "i", PairInfo_Background), + GB_STATIC_PROPERTY("Foreground", "i", PairInfo_Foreground), GB_END_DECLARE }; diff --git a/gb.ncurses/src/c_color.h b/gb.ncurses/src/c_color.h index 367241055..cd4a2839c 100644 --- a/gb.ncurses/src/c_color.h +++ b/gb.ncurses/src/c_color.h @@ -8,9 +8,9 @@ #ifndef __C_COLOR_C extern GB_DESC CColorDesc[]; -extern GB_DESC CColorCapabilitiesDesc[]; -extern GB_DESC CColorPairDesc[]; -extern GB_DESC CColorContentDesc[]; +extern GB_DESC CColorInfoDesc[]; +extern GB_DESC CPairDesc[]; +extern GB_DESC CPairInfoDesc[]; #endif enum { @@ -19,7 +19,9 @@ enum { }; void COLOR_init(); -int COLOR_setpair(short index, short fg, short bg); -int COLOR_setpair_one(short index, short val, int what); +int COLOR_setcolor(short, float, float, float); +int COLOR_setcolor_one(short, float, int); +int COLOR_setpair(short, short, short); +int COLOR_setpair_one(short, short, int); #endif /* __C_COLOR_H */ diff --git a/gb.ncurses/src/input.c b/gb.ncurses/src/c_input.c similarity index 71% rename from gb.ncurses/src/input.c rename to gb.ncurses/src/c_input.c index 828c11789..8ec603490 100644 --- a/gb.ncurses/src/input.c +++ b/gb.ncurses/src/c_input.c @@ -1,5 +1,5 @@ /* - * input.c - gb.ncurses opaque input routines + * c_input.c - gb.ncurses opaque input routines * * Copyright (C) 2012 Tobias Boege * @@ -19,7 +19,7 @@ * MA 02110-1301, USA. */ -#define __INPUT_C +#define __C_INPUT_C #include #include @@ -36,32 +36,17 @@ #include "gb_common.h" #include "main.h" -#include "input.h" +#include "c_input.h" #include "c_window.h" #define E_UNSUPP "Unsupported value" #define E_NO_NODELAY "Could not initialise NoDelay mode" +/* True input mode is inherited from terminal, we need a surely invalid + * value to be reset upon initialisation */ static int _input = -1; -static 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; +static char _watch_fd = -1; /** * Input initialisation @@ -69,7 +54,8 @@ static char _exiting_nodelay = 0; int INPUT_init() { INPUT_mode(INPUT_CBREAK); - INPUT_repeater_delay(100); + INPUT_watch(0); + NODELAY_repeater_delay(1); return 0; } @@ -84,179 +70,46 @@ void INPUT_exit() } } -/** - * Begin or stop watching the input queue in question depending on @_input - */ -static int INPUT_watch(char start) -{ - int fd = IN_NODELAY ? no_delay.fd : 0; - if (!start && !_watching) + +#define MY_DEBUG() fprintf(stderr, "in %s\n", __func__) + + + +/** + * Begin watching the given fd (for read) + * @fd: fd to watch. -1 means to stop watching at all. + * This automatically stops watching the previously watched fd, if any + */ +static int INPUT_watch(int fd) +{ + MY_DEBUG(); + + if (fd == _watch_fd) return 0; - if (start && _watching) - INPUT_watch(!start); - GB.Watch(fd, start ? GB_WATCH_READ : GB_WATCH_NONE, - INPUT_callback, 0); - _watching = start; + + if (_watch_fd != -1) + GB.Watch(_watch_fd, GB_WATCH_NONE, NULL, 0); + if (fd == -1) + return 0; + + GB.Watch(fd, GB_WATCH_READ, INPUT_callback, 0); + _watch_fd = fd; return 0; } /** * Function to be called by Gambas when data arrives + * Params currently not used */ static void INPUT_callback(int fd, int flag, intptr_t arg) { - WINDOW_raise_read(NULL); -} + MY_DEBUG(); -/** - * 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; +/* if (IN_NODELAY) + NODELAY_change_pressed(NODELAY_get(-1)); + else +*/ WINDOW_raise_read(NULL); } /** @@ -271,26 +124,21 @@ int INPUT_mode(int mode) if (mode == _input) return 0; - INPUT_watch(0); - - if (_input == INPUT_NODELAY) - INPUT_exit_nodelay(); + if (IN_NODELAY) + NODELAY_exit(); 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) { + if (NODELAY_init() == -1) { GB.Error(E_NO_NODELAY); /* We return 0 to not override the previous * error message with the one emitted by the @@ -304,11 +152,20 @@ int INPUT_mode(int mode) } _input = mode; - INPUT_watch(1); - return 0; } +/** + * Drain the input queue + */ +void INPUT_drain() +{ + if (IN_NODELAY) + NODELAY_drain(); + else + flushinp(); +} + /** * Retrieve input from ncurses */ @@ -332,8 +189,261 @@ static int INPUT_get_ncurses(int timeout) return ret; } +/** + * Get a keypress within the given timeout + * @timeout: number of milliseconds to wait. If no key is pressed during it, + * 0 will be returned. + */ +int INPUT_get(int timeout) +{ + if (IN_NODELAY) + return NODELAY_get(timeout); + else + return INPUT_get_ncurses(timeout); +} + +BEGIN_PROPERTY(Input_IsConsole) + + int fd = NODELAY_consolefd(); + + if (fd == -1) { + GB.ReturnBoolean(FALSE); + return; + } + close(fd); + GB.ReturnBoolean(TRUE); + +END_PROPERTY + +BEGIN_PROPERTY(Input_RepeatDelay) + + if (READ_PROPERTY) { + GB.ReturnInteger(NODELAY_repeater_delay(REPEATER_RETURN)); + return; + } + if (NODELAY_repeater_delay(VPROP(GB_INTEGER)) == -1) { + GB.Error("Invalid value"); + return; + } + +END_PROPERTY + +GB_DESC CInputDesc[] = { + GB_DECLARE("Input", 0), + GB_NOT_CREATABLE(), + + GB_CONSTANT("NoTimeout", "i", TIMEOUT_NOTIMEOUT), + + GB_CONSTANT("Cooked", "i", INPUT_COOKED), + GB_CONSTANT("CBreak", "i", INPUT_CBREAK), + GB_CONSTANT("Raw", "i", INPUT_RAW), + GB_CONSTANT("NoDelay", "i", INPUT_NODELAY), + + GB_STATIC_PROPERTY_READ("IsConsole", "b", Input_IsConsole), + GB_STATIC_PROPERTY("RepeatDelay", "i", Input_RepeatDelay), +}; + /* - * Return codes from INPUT_trans_keycode() + * NODELAY routines + */ +static struct { + struct { + struct termios term; + int kbmode; + void (*error_hook)(); + } old; + int fd; + unsigned short pressed; + unsigned int delay; + GB_TIMER *timer; +} no_delay; +static char _exiting_nodelay = 0; + +/** + * Init NoDelay mode + * We save old settings and prepare the TTY driver and Gambas + * Precisely: + * - (TTY driver:) + * - Save all related values + * - Set terminal to (ncurses) raw()-like input mode as base for NoDelay + * - Set keyboard to K_MEDIUMRAW mode to see key make and break codes + * - (Gambas:) + * - Install specific error hook + * - Reset repeater timer + * - Begin watching console fd + */ +static int NODELAY_init() +{ + int fd = NODELAY_consolefd(); + struct termios term; + + if (fd == -1) + return -1; + + /* TODO: implement switching between vts, need available signals to + * be sent */ + + tcgetattr(fd, &no_delay.old.term); + ioctl(fd, KDGKBMODE, &no_delay.old.kbmode); + + memcpy(&term, &no_delay.old.term, sizeof(term)); + term.c_lflag &= ~(ICANON | ECHO | ISIG); + term.c_iflag &= ~(ISTRIP | IGNCR | ICRNL | INLCR | IXOFF | IXON); + /* Have no timeout per default */ + term.c_cc[VMIN] = 0; + term.c_cc[VTIME] = TIMEOUT_NOTIMEOUT; + tcsetattr(fd, TCSAFLUSH, &term); + no_delay.old.error_hook = GB.Hook(GB_HOOK_ERROR, + NODELAY_error_hook); + no_delay.fd = fd; + + no_delay.timer = NULL; + + ioctl(no_delay.fd, KDSKBMODE, K_MEDIUMRAW); + + INPUT_watch(fd); + + return 0; +} + +/** + * Cleanup NoDelay mode + * Restore old settings, see NODELAY_init() for details + * This function gets called by our error hook and the error hook is removed + * here. When removing a hook, it gets automatically called: Hence + * @_exiting_nodelay + */ +static int NODELAY_exit() +{ + if (_exiting_nodelay) + return 0; + + _exiting_nodelay = 1; + + INPUT_watch(0); + + ioctl(no_delay.fd, KDSKBMODE, no_delay.old.kbmode); + + if (no_delay.timer) + GB.Unref((void **) &no_delay.timer); + + GB.Hook(GB_HOOK_ERROR, no_delay.old.error_hook); + tcsetattr(no_delay.fd, TCSANOW, &no_delay.old.term); + + close(no_delay.fd); + + _exiting_nodelay = 0; + + return 0; +} + +/** + * The NoDelay mode error hook + * This calls the former error hook, saved by NODELAY_init() to not + * disturb any piece code + */ +static void NODELAY_error_hook() +{ + if (_exiting_nodelay) + return; + NODELAY_exit(); + no_delay.old.error_hook(); +} + +/** + * Return if the given fd can be used with console_ioctls + * @fd: file descriptor to test + * The idea was derived from "kbd" package, getfd.c, is_a_console() + */ +static inline char NODELAY_is_cons(int fd) +{ + char type; + + if (fd != -1 && isatty(fd) && ioctl(fd, KDGKBTYPE, &type) != -1 + && (type == KB_101 || type == KB_84)) + return 1; + return 0; +} + +/** + * Returns an fd that can be used with console_ioctls or -1 if none + * available + */ +static int NODELAY_consolefd() +{ + int fd; + + if (NODELAY_is_cons(0)) + return 0; + + fd = open("/dev/tty", O_RDWR); + if (fd == -1) + return -1; + if (NODELAY_is_cons(fd)) + return fd; + + close(fd); + return -1; +} + +/** + * Drain NoDelay input queue + */ +static inline void NODELAY_drain() +{ + tcflush(no_delay.fd, TCIFLUSH); +} + +/** + * Return or set the repeater delay + * @val: value to set the delay to. This value must be at least 1 or an + * error is returned. If it is REPEATER_RETURN, the current value is + * returned to the caller. + * Note that this setting affects the repeater function itself, that gets + * called in this interval to generate events and the INPUT_get_nodelay() + * function which will wait to return the amount of milliseconds if it is to + * return the pressed key. + */ +static int NODELAY_repeater_delay(int val) +{ + if (val == REPEATER_RETURN) + return no_delay.delay; + if (val < 1) + return -1; + no_delay.delay = (unsigned int) val; + return 0; +} + +/** + * Post callback to insert new read event during next event loop. + * This is important because the NODELAY_repeater() gets called by the + * timer. If we raise an event from there, the event handler may destroy the + * timer we are currently in by issuing a NODELAY_change_pressed(). This has + * consequently be done outside the timer tick. + * @arg: unused + */ +static void NODELAY_post_read(intptr_t arg) +{ + WINDOW_raise_read(NULL); +} + +/** + * NoDelay mode event repeater. This function is the timer callback + * Used to insert Window_Read events if there is a key pressed + */ +static int NODELAY_repeater() +{ + MY_DEBUG(); + + + if (!no_delay.pressed) + return TRUE; + GB.Post(NODELAY_post_read, 0); + return FALSE; +} + +/* + * Return codes from NODELAY_trans_keycode() */ enum { TRANS_NEED_MORE = -1, @@ -366,19 +476,23 @@ enum { * 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) +static int NODELAY_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 */ + + + MY_DEBUG(); + + + #define KEYCODE_LCTRL 0x1d #define KEYCODE_RCTRL 0x61 #define KEYCODE_ALT 0x38 @@ -417,7 +531,7 @@ account_key: return TRANS_NEED_MORE; } /* Keys with two keycodes, no matter, those correspond to - * single-keycode keys, we can safely use @kc */ + * single-keycode keys, we can safely use @kc != 0xe0 */ if (codes[0] == '\xe0' && num != 2) return TRANS_NEED_MORE; @@ -437,35 +551,39 @@ account_key: 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]; + kbs.kb_func = (unsigned char) kbe.kb_value; 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]; - } + if (kbs.kb_string[0]) + return key_defined((char *) kbs.kb_string); + else + return (int) ((unsigned char) kbe.kb_value); } /** * Change the currently pressed key * @key: current key. 0 means that no key is pressed - * This install the repeater + * This installs the repeater */ -static void INPUT_change_pressed(int key) +static void NODELAY_change_pressed(int key) { - fprintf(stderr, "%d\n", no_delay.delay); + + + MY_DEBUG(); + + + +/* if (key == no_delay.pressed) + return; if (key == 0) { 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); + (GB_TIMER_CALLBACK) NODELAY_repeater, 0); } - no_delay.pressed = key; +*/ no_delay.pressed = key; + //NODELAY_repeater(); + WINDOW_raise_read(NULL); } /** @@ -480,7 +598,7 @@ static void INPUT_change_pressed(int key) * Note carefully, that there is an emergency exit: pressing ESC thrice * during one second will immediately abort NoDelay mode and enter CBreak. */ -static int INPUT_get_nodelay(int timeout) +static int NODELAY_get(int timeout) { static char esc = 0; struct termios old, new; @@ -490,6 +608,13 @@ static int INPUT_get_nodelay(int timeout) int key; /* This will already be ncurses compatible */ struct timeval tv1, tv2; + + + + MY_DEBUG(); + + + if (timeout > -1) { gettimeofday(&tv1, NULL); tcgetattr(no_delay.fd, &old); @@ -543,26 +668,26 @@ recalc_timeout: if (time(NULL) - stamp > 0) esc = 0; if (++esc == 3) { - INPUT_exit_nodelay(); + NODELAY_exit(); 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) + if ((key = NODELAY_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); +// NODELAY_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); +// NODELAY_change_pressed(key); } } @@ -573,16 +698,3 @@ cleanup: 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); -} diff --git a/gb.ncurses/src/input.h b/gb.ncurses/src/c_input.h similarity index 64% rename from gb.ncurses/src/input.h rename to gb.ncurses/src/c_input.h index 1d3d95997..da81285f5 100644 --- a/gb.ncurses/src/input.h +++ b/gb.ncurses/src/c_input.h @@ -1,5 +1,5 @@ /* - * input.h - gb.ncurses opaque input routines for use of either ncurses or + * c_input.h - gb.ncurses opaque input routines for use of either ncurses or * the terminal driver directly * * Copyright (C) 2012 Tobias Boege @@ -20,12 +20,12 @@ * MA 02110-1301, USA. */ -#ifndef __INPUT_H -#define __INPUT_H +#ifndef __C_INPUT_H +#define __C_INPUT_H enum { /* Return the current mode */ - INPUT_RETURN, + INPUT_RETURN = -1, /* Line discipline, signal generation */ INPUT_COOKED, /* No line discipline, signal generation */ @@ -39,23 +39,34 @@ enum { }; enum { - /* Let INPUT_repeater_delay() return the current value of the - repeater delay */ + TIMEOUT_NOTIMEOUT = -1 +}; + +enum { 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); +#ifndef __C_INPUT_C +extern GB_DESC CInputDesc[]; #endif -#endif /* __INPUT_H */ +int INPUT_init(); +void INPUT_exit(); +int INPUT_mode(int); +int INPUT_get(int); +void INPUT_drain(); +#ifdef __C_INPUT_C +static int INPUT_watch(int); +static void INPUT_callback(int, int, intptr_t); +static int NODELAY_init(); +static int NODELAY_exit(); +static void NODELAY_error_hook(); +static int NODELAY_consolefd(); +static inline void NODELAY_drain(); +static int NODELAY_repeater(); +static int NODELAY_repeater_delay(int); +static void NODELAY_change_pressed(int); +static int NODELAY_get(int); +#endif + +#endif /* __C_INPUT_H */ diff --git a/gb.ncurses/src/c_key.c b/gb.ncurses/src/c_key.c index 1a6902634..0a375327f 100644 --- a/gb.ncurses/src/c_key.c +++ b/gb.ncurses/src/c_key.c @@ -34,8 +34,7 @@ BEGIN_METHOD(Key_get, GB_STRING key) END_METHOD -GB_DESC CKeyDesc[] = -{ +GB_DESC CKeyDesc[] = { GB_DECLARE("Key", 0), GB_NOT_CREATABLE(), diff --git a/gb.ncurses/src/c_screen.c b/gb.ncurses/src/c_screen.c index e599f63b4..86ccd3484 100644 --- a/gb.ncurses/src/c_screen.c +++ b/gb.ncurses/src/c_screen.c @@ -29,22 +29,18 @@ #include "gambas.h" -#include "c_screen.h" #include "main.h" -#include "input.h" +#include "c_screen.h" +#include "c_input.h" #define THIS ((CSCREEN *) _object) #define IS_BUFFERED (THIS->buffered) #define E_UNSUPP "Unsupported value" -static bool _cursor; -static int _echo; - -/* Currently active screen */ static CSCREEN *active = NULL; -GB_SIGNAL_CALLBACK *callback; +GB_SIGNAL_CALLBACK *_sigwinch_cb; DECLARE_EVENT(EVENT_Read); DECLARE_EVENT(EVENT_Resize); @@ -52,69 +48,28 @@ DECLARE_EVENT(EVENT_Resize); /* * Signal handler for the SIGWINCH signal. * @signum: signal number given + * @arg: unused * This routine dispatches the Resize Event */ -void SCREEN_sigwinch_handler(int signum, intptr_t data) +static void SCREEN_sigwinch_handler(int signum, intptr_t arg) { if (signum == SIGWINCH) GB.Raise(active, EVENT_Resize, 0); } -/** - * Set mode of @_cursor - */ -static int SCREEN_cursor_mode(int mode) -{ - switch (mode) { - case CURSOR_HIDDEN: - curs_set(0); - break; - case CURSOR_VISIBLE: - curs_set(1); - break; - case CURSOR_VERY: - curs_set(2); - break; - default: - return -1; - } - _cursor = mode; - return 0; -} - -/** - * Set mode of @_echo - */ -static int SCREEN_echo_mode(int mode) -{ - switch (mode) { - case ECHO_NOECHO: - noecho(); - break; - case ECHO_ECHO: - echo(); - break; - default: - return -1; - } - _echo = mode; - return 0; -} - /** * Screen initialisation */ int SCREEN_init() { /* Global variable default setup */ - SCREEN_cursor_mode(CURSOR_VISIBLE); - SCREEN_echo_mode(ECHO_NOECHO); - + CURSOR_mode(CURSOR_VISIBLE); + ECHO_mode(ECHO_NOECHO); INPUT_init(); -/* callback = GB.Signal.Register(SIGSEGV, - SCREEN_sigwinch_handler, (intptr_t) NULL); -*/ +// _sigwinch_cb = GB.Signal.Register(SIGWINCH, +// SCREEN_sigwinch_handler, (intptr_t) NULL); + return 0; } @@ -125,8 +80,7 @@ void SCREEN_exit() { INPUT_exit(); -/* GB.Signal.Unregister(SIGWINCH, callback); -*/ +// GB.Signal.Unregister(SIGWINCH, _sigwinch_cb); } /** @@ -173,14 +127,23 @@ void SCREEN_refresh(void *_object) /** * Let the specified screen raise its Read event. If the _object is NULL, - * the currently active screen will raise. + * the currently active screen will raise. The latter case is the last + * resort for the difficulty of raising Read event in Window-Screen + * relation, while there need not be a focused window to raise this event, + * there is always an active screen. */ void SCREEN_raise_read(void *_object) { + bool risen; + if (!_object) - GB.Raise(active, EVENT_Read, 0); + risen = GB.Raise(active, EVENT_Read, 0); else - GB.Raise(_object, EVENT_Read, 0); + risen = GB.Raise(_object, EVENT_Read, 0); + + /* Reduce watch callback calling overhead */ + if (!risen) + INPUT_drain(); } BEGIN_PROPERTY(Screen_Buffered) @@ -196,11 +159,11 @@ END_PROPERTY BEGIN_PROPERTY(Screen_Cursor) if (READ_PROPERTY) { - GB.ReturnInteger(_cursor); + GB.ReturnInteger(CURSOR_mode(CURSOR_RETURN)); return; } - if (SCREEN_cursor_mode(VPROP(GB_INTEGER)) == -1) + if (CURSOR_mode(VPROP(GB_INTEGER)) == -1) GB.Error(E_UNSUPP); END_PROPERTY @@ -208,11 +171,11 @@ END_PROPERTY BEGIN_PROPERTY(Screen_Echo) if (READ_PROPERTY) { - GB.ReturnBoolean(_echo); + GB.ReturnBoolean(ECHO_mode(ECHO_RETURN)); return; } - if (SCREEN_echo_mode(!!VPROP(GB_BOOLEAN)) == -1) + if (ECHO_mode(!!VPROP(GB_BOOLEAN)) == -1) GB.Error(E_UNSUPP); END_PROPERTY @@ -229,32 +192,6 @@ BEGIN_PROPERTY(Screen_Input) END_PROPERTY -BEGIN_PROPERTY(Screen_IsConsole) - - int fd = INPUT_consolefd(); - - if (fd == -1) { - GB.ReturnBoolean(FALSE); - return; - } - close(fd); - GB.ReturnBoolean(TRUE); - -END_PROPERTY - -BEGIN_PROPERTY(Screen_Repeater) - - if (READ_PROPERTY) { - GB.ReturnInteger(INPUT_repeater_delay(REPEATER_RETURN)); - return; - } - if (INPUT_repeater_delay(VPROP(GB_INTEGER)) == -1) { - GB.Error("Invalid value"); - return; - } - -END_PROPERTY - BEGIN_PROPERTY(Screen_Lines) GB.ReturnInteger(LINES); @@ -275,6 +212,8 @@ END_METHOD BEGIN_METHOD_VOID(Screen_free) + /* TODO: for now, while Screen instantiation wouldn't do anything + * useful */ SCREEN_exit(); END_METHOD @@ -285,29 +224,18 @@ BEGIN_METHOD_VOID(Screen_Refresh) END_METHOD -GB_DESC CScreenDesc[] = -{ +GB_DESC CScreenDesc[] = { 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("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), @@ -319,3 +247,73 @@ GB_DESC CScreenDesc[] = GB_END_DECLARE }; + +/* + * CURSOR routines + */ + +static int _cursor; + +/** + * Return or set cursor mode + * @mode: choose operation + */ +int CURSOR_mode(int mode) +{ + switch (mode) { + case CURSOR_RETURN: + return _cursor; + case CURSOR_HIDDEN: + curs_set(0); + break; + case CURSOR_VISIBLE: + curs_set(1); + break; + case CURSOR_VERY: + curs_set(2); + break; + default: + return -1; + } + _cursor = mode; + return 0; +} + +GB_DESC CCursorDesc[] = { + GB_DECLARE("Cursor", 0), + GB_NOT_CREATABLE(), + + GB_CONSTANT("Hidden", "i", CURSOR_HIDDEN), + GB_CONSTANT("Visible", "i", CURSOR_VISIBLE), + GB_CONSTANT("Very", "i", CURSOR_VERY), + + GB_END_DECLARE +}; + +/* + * ECHO routines + */ + +static int _echo; + +/** + * Return or set echo mode + * @mode: choose operation + */ +int ECHO_mode(int mode) +{ + switch (mode) { + case ECHO_RETURN: + return _echo; + case ECHO_NOECHO: + noecho(); + break; + case ECHO_ECHO: + echo(); + break; + default: + return -1; + } + _echo = mode; + return 0; +} diff --git a/gb.ncurses/src/c_screen.h b/gb.ncurses/src/c_screen.h index 7c1069d37..3183b0a9f 100644 --- a/gb.ncurses/src/c_screen.h +++ b/gb.ncurses/src/c_screen.h @@ -23,6 +23,7 @@ #define __C_SCREEN_H #include "gambas.h" +#include "c_input.h" /* This will produce final output on terminal screen */ #define REAL_REFRESH() SCREEN_real_refresh() @@ -30,11 +31,14 @@ (read: to check if the output is buffered and if not produce output by means of REAL_REFRESH()) */ #define REFRESH() SCREEN_refresh(NULL) +/* The Screen - like in ncurses - is the sole source of input */ +#define SCREEN_get(t) INPUT_get(t) /* * Cursor modes */ enum { + CURSOR_RETURN = -1, CURSOR_HIDDEN, CURSOR_VISIBLE, CURSOR_VERY @@ -44,12 +48,14 @@ enum { * Echo modes */ enum { + ECHO_RETURN = -1, ECHO_NOECHO, ECHO_ECHO }; #ifndef __C_SCREEN_C extern GB_DESC CScreenDesc[]; +extern GB_DESC CCursorDesc[]; #endif typedef struct { @@ -65,4 +71,7 @@ void SCREEN_refresh(); void SCREEN_real_refresh(); void SCREEN_raise_read(void *); +int CURSOR_mode(int); +int ECHO_mode(int); + #endif /* __C_SCREEN_H */ diff --git a/gb.ncurses/src/c_window.c b/gb.ncurses/src/c_window.c index 9b3456ff1..13a550a2b 100644 --- a/gb.ncurses/src/c_window.c +++ b/gb.ncurses/src/c_window.c @@ -35,7 +35,6 @@ #include "main.h" #include "c_screen.h" #include "c_color.h" -#include "input.h" #define THIS ((CWINDOW *) _object) #define HAS_BORDER (THIS->border != BORDER_NONE) @@ -118,7 +117,7 @@ DECLARE_EVENT(EVENT_Read); /* * C Window interface - * @_object: Reference to the struct nc_window representing the current object. + * @_object: Reference to the CWINDOW representing the current object. * * The theory: Each window has a main and a content window pointer. The main window * represents the window object outwards. It is linked to a panel structure which @@ -382,7 +381,7 @@ static int WINDOW_cursor_move(void *_object, int x, int y) } /** - * Move the entire nc_window on screen. + * Move the entire window on screen. * @x: new x coordinate * @y: new y coordinate of the upper left corner of the main window relative to the * screen. Again, -1 denotes to leave the value as is @@ -579,7 +578,55 @@ static int WINDOW_get_str(void *_object, int x, int y, unsigned int len, char ** */ static int WINDOW_key_timeout(void *_object, int timeout, int *ret) { - *ret = INPUT_get(timeout); + *ret = SCREEN_get(timeout); + return 0; +} + +/** + * Read an entire string form the input queue + * @ret: the pointer to a newly malloced string will be stored there + * This function behaves like getstr(3X). Note that getstr() being a + * sequence of getch(), we must stick to its characteristics, too, i.e. + * beep() when there is a non-printable character typed in and echo all + * characters if we echo. + */ +static int WINDOW_readline(void *_object, char **ret) +{ + int key; + int i, j; + char *line = NULL, *p, c; + + i = j = 0; + while (1) { + if (!(i & 256)) { + if (!(p = realloc(line, (++j * 256) + 1))) { + free(line); + return -1; + } + line = p; + } + key = SCREEN_get(TIMEOUT_NOTIMEOUT); + c = (char) key; + if (c == '\n' || c == '\r') + break; + if (key == KEY_BACKSPACE || key == KEY_LEFT) { + if (i > 0) + i--; + continue; + } + if ((c < 32 && !isspace(c)) || key > 127) { + beep(); + continue; + } + + line[i++] = c; + if (ECHO_mode(ECHO_RETURN)) { + line[i] = 0; + WINDOW_print(THIS, line + i - 1, -1, -1); + } + } + line[i] = 0; + *ret = line; return 0; } @@ -588,7 +635,7 @@ static int WINDOW_key_timeout(void *_object, int timeout, int *ret) */ static void WINDOW_drain() { - flushinp(); + INPUT_drain(); } /** @@ -633,7 +680,8 @@ static int WINDOW_show(void *_object) * Raise the Window_Read event of the given window. If _object is NULL, * 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. + * event. If there is not even a parent screen accessible, use the active + * screen. */ void WINDOW_raise_read(void *_object) { @@ -657,7 +705,7 @@ static int WINDOW_setfocus(void *_object) if (focused) GB.Unref((void **) &focused); focused = THIS; - GB.Ref((void **) &focused); + GB.Ref((void *) focused); /* We watch since SCREEN_init() */ return 0; } @@ -838,18 +886,6 @@ BEGIN_PROPERTY(Window_Caption) END_PROPERTY -BEGIN_PROPERTY(Window_ContainerHeight) - - GB.ReturnInteger(getmaxy(THIS->main)); - -END_PROPERTY - -BEGIN_PROPERTY(Window_ContainerWidth) - - GB.ReturnInteger(getmaxx(THIS->main)); - -END_PROPERTY - BEGIN_PROPERTY(Window_CursorX) if (READ_PROPERTY) { @@ -1000,7 +1036,7 @@ BEGIN_METHOD(Window_Ask, GB_STRING opts; GB_INTEGER tries) o = GB.ToZeroString(ARG(opts)); while (miss || t--) { - ch = getch(); + WINDOW_key_timeout(THIS, -1, &ch); /* Per convention, only dealing with byte chars */ if (ch > 255) continue; @@ -1204,9 +1240,14 @@ 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.*/ + char *line; + + if (WINDOW_readline(THIS, &line) == -1) { + GB.ReturnNull(); + return; + } + GB.ReturnNewZeroString(line); + free(line); END_METHOD @@ -1223,6 +1264,79 @@ BEGIN_METHOD_VOID(Window_SetFocus) END_METHOD +GB_DESC CWindowDesc[] = { + GB_DECLARE("Window", sizeof(CWINDOW)), + GB_AUTO_CREATABLE(), + + GB_EVENT("Read", NULL, NULL, &EVENT_Read), + + /* Properties */ + GB_PROPERTY_SELF("Attributes", ".Window.Attrs"), + + GB_PROPERTY("Background", "i", Window_Background), + GB_PROPERTY("Paper", "i", Window_Background), + + GB_PROPERTY("Border", "i", Window_Border), + + GB_PROPERTY("Caption", "s", Window_Caption), + + GB_PROPERTY("CursorX", "i", Window_CursorX), + GB_PROPERTY("CursorY", "i", Window_CursorY), + + GB_PROPERTY("Foreground", "i", Window_Foreground), + GB_PROPERTY("Pen", "i", Window_Foreground), + + GB_PROPERTY("Height", "i", Window_Height), + GB_PROPERTY("H", "i", Window_Height), + + GB_PROPERTY("Wrap", "b", Window_Wrap), + + GB_PROPERTY("Width", "i", Window_Width), + GB_PROPERTY("W", "i", Window_Width), + + GB_PROPERTY("X", "i", Window_X), + GB_PROPERTY("Y", "i", Window_Y), + + /* Methods */ + GB_METHOD("_new", NULL, Window_new, "[(Parent)Screen;(X)i(Y)i(W)i(H)i]"), + GB_METHOD("_free", NULL, Window_free, NULL), + GB_METHOD("_get", ".Char.Attrs", Window_get, "(Y)i(X)i"), + + GB_METHOD("Ask", "s", Window_Ask, "(Opts)s[(Tries)i]"), + + GB_METHOD("Lower", NULL, Window_Lower, NULL), + GB_METHOD("Raise", NULL, Window_Raise, NULL), + + GB_METHOD("Cls", NULL, Window_Cls, NULL), + + GB_METHOD("Drain", NULL, Window_Drain, NULL), + + GB_METHOD("Full", NULL, Window_Full, NULL), + + GB_METHOD("Get", "s", Window_Get, "(X)i(Y)i[(Len)i]"), + + GB_METHOD("Hide", NULL, Window_Hide, NULL), + GB_METHOD("Show", NULL, Window_Show, NULL), + + GB_METHOD("DrawHLine", NULL, Window_DrawHLine, "(X)i(Y)i(Len)i(Ch)s[(Thickness)i]"), + GB_METHOD("DrawVLine", NULL, Window_DrawVLine, "(X)i(Y)i(Len)i(Ch)s[(Thickness)i]"), + + GB_METHOD("Insert", NULL, Window_Insert, "(Text)s[(X)i(Y)i]"), + GB_METHOD("Print", NULL, Window_Print, "(Text)s[(X)i(Y)i]"), + GB_METHOD("PrintCenter", NULL, Window_PrintCenter, "(Text)s"), + + GB_METHOD("Locate", NULL, Window_Locate, "(X)i(Y)i"), + GB_METHOD("Move", NULL, Window_Move, "[(X)i(Y)i]"), + GB_METHOD("Resize", NULL, Window_Resize, "[(W)i(H)i]"), + + GB_METHOD("Read", "i", Window_Read, "[(Timeout)i]"), + GB_METHOD("ReadLine", "s", Window_ReadLine, NULL), + + GB_METHOD("SetFocus", NULL, Window_SetFocus, NULL), + + GB_END_DECLARE +}; + /* * .Window.Attrs virtual class */ @@ -1284,6 +1398,21 @@ BEGIN_PROPERTY(WindowAttrs_Color) END_PROPERTY +GB_DESC CWindowAttrsDesc[] = { + GB_DECLARE(".Window.Attrs", 0), + GB_VIRTUAL_CLASS(), + + GB_PROPERTY("Normal", "b", WindowAttrs_Normal), + GB_PROPERTY("Underline", "b", WindowAttrs_Underline), + GB_PROPERTY("Reverse", "b", WindowAttrs_Reverse), + GB_PROPERTY("Blink", "b", WindowAttrs_Blink), + GB_PROPERTY("Bold", "b", WindowAttrs_Bold), + + GB_PROPERTY("Color", "i", WindowAttrs_Color), + + GB_END_DECLARE +}; + /* * .Char.Attrs virtual class */ @@ -1349,109 +1478,7 @@ BEGIN_PROPERTY(CharAttrs_Color) END_PROPERTY -GB_DESC CWindowDesc[] = -{ - GB_DECLARE("Window", sizeof(CWINDOW)), - GB_AUTO_CREATABLE(), - - GB_EVENT("Read", NULL, NULL, &EVENT_Read), - - /* Constants */ - GB_CONSTANT("NoTimeout", "i", TIMEOUT_NOTIMEOUT), - GB_CONSTANT("None", "i", BORDER_NONE), - GB_CONSTANT("Ascii", "i", BORDER_ASCII), - GB_CONSTANT("ACS", "i", BORDER_ACS), - - /* Properties */ - GB_PROPERTY_SELF("Attributes", ".Window.Attrs"), - - GB_PROPERTY("Background", "i", Window_Background), - GB_PROPERTY("Paper", "i", Window_Background), - - GB_PROPERTY("Border", "i", Window_Border), - - GB_PROPERTY("Caption", "s", Window_Caption), - - GB_PROPERTY_READ("ContainerHeight", "i", Window_ContainerHeight), - GB_PROPERTY_READ("ContainerH", "i", Window_ContainerHeight), - GB_PROPERTY_READ("ContainerWidth", "i", Window_ContainerWidth), - GB_PROPERTY_READ("ContainerW", "i", Window_ContainerWidth), - - GB_PROPERTY("CursorX", "i", Window_CursorX), - GB_PROPERTY("CursorY", "i", Window_CursorY), - - GB_PROPERTY("Foreground", "i", Window_Foreground), - GB_PROPERTY("Pen", "i", Window_Foreground), - - GB_PROPERTY("Height", "i", Window_Height), - GB_PROPERTY("H", "i", Window_Height), - - GB_PROPERTY("Wrap", "b", Window_Wrap), - - GB_PROPERTY("Width", "i", Window_Width), - GB_PROPERTY("W", "i", Window_Width), - - GB_PROPERTY("X", "i", Window_X), - GB_PROPERTY("Y", "i", Window_Y), - - /* Methods */ - GB_METHOD("_new", NULL, Window_new, "[(Parent)Screen;(X)i(Y)i(W)i(H)i]"), - GB_METHOD("_free", NULL, Window_free, NULL), - GB_METHOD("_get", ".Char.Attrs", Window_get, "(Y)i(X)i"), - - GB_METHOD("Ask", "s", Window_Ask, "(Opts)s[(Tries)i]"), - - GB_METHOD("Lower", NULL, Window_Lower, NULL), - GB_METHOD("Raise", NULL, Window_Raise, NULL), - - GB_METHOD("Cls", NULL, Window_Cls, NULL), - - GB_METHOD("Drain", NULL, Window_Drain, NULL), - - GB_METHOD("Full", NULL, Window_Full, NULL), - - GB_METHOD("Get", "s", Window_Get, "(X)i(Y)i[(Len)i]"), - - GB_METHOD("Hide", NULL, Window_Hide, NULL), - GB_METHOD("Show", NULL, Window_Show, NULL), - - GB_METHOD("DrawHLine", NULL, Window_DrawHLine, "(X)i(Y)i(Len)i(Ch)s[(Thickness)i]"), - GB_METHOD("DrawVLine", NULL, Window_DrawVLine, "(X)i(Y)i(Len)i(Ch)s[(Thickness)i]"), - - GB_METHOD("Insert", NULL, Window_Insert, "(Text)s[(X)i(Y)i]"), - GB_METHOD("Print", NULL, Window_Print, "(Text)s[(X)i(Y)i]"), - GB_METHOD("PrintCenter", NULL, Window_PrintCenter, "(Text)s"), - - GB_METHOD("Locate", NULL, Window_Locate, "(X)i(Y)i"), - GB_METHOD("Move", NULL, Window_Move, "[(X)i(Y)i]"), - GB_METHOD("Resize", NULL, Window_Resize, "[(W)i(H)i]"), - - GB_METHOD("Read", "i", Window_Read, "[(Timeout)i]"), - GB_METHOD("ReadLine", "s", Window_ReadLine, NULL), - - GB_METHOD("SetFocus", NULL, Window_SetFocus, NULL), - - GB_END_DECLARE -}; - -GB_DESC CWindowAttrsDesc[] = -{ - GB_DECLARE(".Window.Attrs", 0), - GB_VIRTUAL_CLASS(), - - GB_PROPERTY("Normal", "b", WindowAttrs_Normal), - GB_PROPERTY("Underline", "b", WindowAttrs_Underline), - GB_PROPERTY("Reverse", "b", WindowAttrs_Reverse), - GB_PROPERTY("Blink", "b", WindowAttrs_Blink), - GB_PROPERTY("Bold", "b", WindowAttrs_Bold), - - GB_PROPERTY("Color", "i", WindowAttrs_Color), - - GB_END_DECLARE -}; - -GB_DESC CCharAttrsDesc[] = -{ +GB_DESC CCharAttrsDesc[] = { GB_DECLARE(".Char.Attrs", 0), GB_VIRTUAL_CLASS(), @@ -1465,3 +1492,18 @@ GB_DESC CCharAttrsDesc[] = GB_END_DECLARE }; + +/* + * Border static class + */ + +GB_DESC CBorderDesc[] = { + GB_DECLARE("Border", 0), + GB_NOT_CREATABLE(), + + GB_CONSTANT("None", "i", BORDER_NONE), + GB_CONSTANT("Ascii", "i", BORDER_ASCII), + GB_CONSTANT("ACS", "i", BORDER_ACS), + + GB_END_DECLARE +}; diff --git a/gb.ncurses/src/c_window.h b/gb.ncurses/src/c_window.h index f8bf9f59a..542272175 100644 --- a/gb.ncurses/src/c_window.h +++ b/gb.ncurses/src/c_window.h @@ -37,11 +37,6 @@ enum { BORDER_ACS }; -/* Timeout constants */ -enum { - TIMEOUT_NOTIMEOUT = -1 -}; - /* @reqs for *_attrs_driver() */ enum { @@ -77,6 +72,7 @@ typedef struct { extern GB_DESC CWindowDesc[]; extern GB_DESC CWindowAttrsDesc[]; extern GB_DESC CCharAttrsDesc[]; +extern GB_DESC CBorderDesc[]; #endif void WINDOW_raise_read(void *); diff --git a/gb.ncurses/src/main.c b/gb.ncurses/src/main.c index 756fe23ad..8fe7facc6 100644 --- a/gb.ncurses/src/main.c +++ b/gb.ncurses/src/main.c @@ -25,28 +25,35 @@ #include "c_key.h" #include "c_color.h" #include "c_screen.h" +#include "c_input.h" #include "main.h" GB_INTERFACE GB EXPORT; -GB_DESC *GB_CLASSES[] EXPORT = -{ +GB_DESC *GB_CLASSES[] EXPORT = { CScreenDesc, + CInputDesc, + CCursorDesc, + CWindowDesc, CWindowAttrsDesc, CCharAttrsDesc, + CBorderDesc, + CKeyDesc, + CColorDesc, - CColorCapabilitiesDesc, - CColorPairDesc, - CColorContentDesc, + CColorInfoDesc, + CPairDesc, + CPairInfoDesc, + NULL }; static bool _init = FALSE; /** - * Returns if we are in ncurses mode + * Return if we are in ncurses mode */ bool MAIN_running() {