* harbour/include/hbgtcore.h
* harbour/source/rtl/hbgtcore.c
* harbour/source/rtl/gtapi.c
* harbour/source/rtl/inkeyapi.c
* harbour/source/rtl/mouseapi.c
- removed hb_gt_*(), hb_inkey_*(), hb_mouse_*() functions
+ implemented HB_GTSELF_*() functions and changed HB_GTSUPER_*()
ones to operate on GT context passed ad 1-st parameter.
Now GT API allows to create many GTs working simultaneously
+ added hb_gt_Base() core function which returns GT context
it will be extended soon to allow using many GT contexts,
setting thread default one or switch between them using some
.prg function.
* harbour/source/rtl/gtstd/gtstd.c
* harbour/source/rtl/gtcgi/gtcgi.c
* harbour/source/rtl/gtpca/gtpca.c
* harbour/source/rtl/gttrm/gttrm.c
* harbour/source/rtl/gtxwc/gtxwc.c
* harbour/source/rtl/gtcrs/gtcrs.c
* harbour/source/rtl/gtcrs/gtcrs.h
* harbour/source/rtl/gtsln/gtsln.c
* harbour/source/rtl/gtsln/gtsln.h
* harbour/source/rtl/gtsln/kbsln.c
* harbour/source/rtl/gtsln/mousesln.c
* harbour/source/rtl/gtalleg/gtalleg.c
* harbour/source/rtl/gtgui/gtgui.c
* harbour/source/rtl/gtwin/gtwin.c
* harbour/source/rtl/gtwvt/gtwvt.h
* harbour/source/rtl/gtwvt/gtwvt.c
* harbour/contrib/hbct/ctwin.c
* harbour/contrib/hbct/ctwin.h
* harbour/contrib/hbct/ctwfunc.c
* harbour/contrib/hbgtwvg/gtwvt.h
* harbour/contrib/hbgtwvg/gtwvt.c
* updated GT code for new GT API. I still haven't added GT cloning
to them and only GTTRM is ready to use in multi window MT programs
but now they can be systematically modified and it can be done
locally without core code modifications.
* harbour/contrib/hbgtwvg/wvtutils.c
! fixed some memory leaks in Unicode conversions
534 lines
16 KiB
C
534 lines
16 KiB
C
/*
|
|
* $Id$
|
|
*/
|
|
|
|
/*
|
|
* Harbour Project source code:
|
|
* Keyboard subsystem based on Slang screen library.
|
|
*
|
|
* Copyright 2000 Marek Paliwoda <paliwoda@inetia.pl>
|
|
* www - http://www.harbour-project.org
|
|
*
|
|
* 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 software; see the file COPYING. If not, write to
|
|
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
|
* Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/).
|
|
*
|
|
* As a special exception, the Harbour Project gives permission for
|
|
* additional uses of the text contained in its release of Harbour.
|
|
*
|
|
* The exception is that, if you link the Harbour libraries with other
|
|
* files to produce an executable, this does not by itself cause the
|
|
* resulting executable to be covered by the GNU General Public License.
|
|
* Your use of that executable is in no way restricted on account of
|
|
* linking the Harbour library code into it.
|
|
*
|
|
* This exception does not however invalidate any other reasons why
|
|
* the executable file might be covered by the GNU General Public License.
|
|
*
|
|
* This exception applies only to the code released by the Harbour
|
|
* Project under the name Harbour. If you copy code from other
|
|
* Harbour Project or Free Software Foundation releases into a copy of
|
|
* Harbour, as the General Public License permits, the exception does
|
|
* not apply to the code that you add in this way. To avoid misleading
|
|
* anyone as to the status of such modified files, you must delete
|
|
* this exception notice from them.
|
|
*
|
|
* If you write modifications of your own for Harbour, it is your choice
|
|
* whether to permit this exception to apply to your modifications.
|
|
* If you do not wish that, delete this exception notice.
|
|
*
|
|
*/
|
|
|
|
/* NOTE: User programs should never call this layer directly! */
|
|
|
|
/* *********************************************************************** */
|
|
|
|
#include "gtsln.h"
|
|
#include <sys/ioctl.h>
|
|
|
|
#ifdef M_UNIX
|
|
#include <sys/termio.h>
|
|
#include <sys/vtkd.h>
|
|
#endif
|
|
|
|
/* we're assuming target has termios - should be better done */
|
|
#include <termios.h>
|
|
|
|
/* *********************************************************************** */
|
|
|
|
/* keyboard states - these should be taken
|
|
from system includes, not be hard coded */
|
|
#if defined(__linux__)
|
|
#define SHIFT_PRESSED 1
|
|
#define ALTR_PRESSED 2
|
|
#define CONTROL_PRESSED 4
|
|
#define ALTL_PRESSED 8
|
|
#define ALT_PRESSED ALTL_PRESSED
|
|
#elif defined(M_UNIX) /* SCO */
|
|
#define SHIFT_PRESSED 1
|
|
#define ALTR_PRESSED 8
|
|
#define CONTROL_PRESSED 2
|
|
#define ALTL_PRESSED 4
|
|
#define ALT_PRESSED ALTL_PRESSED
|
|
#else /* we don't know how to do this */
|
|
#define SHIFT_PRESSED 0
|
|
#define ALTR_PRESSED 0
|
|
#define CONTROL_PRESSED 0
|
|
#define ALTL_PRESSED 0
|
|
#define ALT_PRESSED ALTL_PRESSED
|
|
#endif
|
|
#define HB_GT_KBD_MODIF_MASK \
|
|
( ( SHIFT_PRESSED | CONTROL_PRESSED | ALTL_PRESSED ) << 16 )
|
|
|
|
#define MOUSE_ALL_EVENTS_MASK \
|
|
( INKEY_MOVE | INKEY_LDOWN | INKEY_LUP | INKEY_RDOWN | INKEY_RUP )
|
|
|
|
/* extra keysyms definitions */
|
|
#define SL_KEY_NUM_5 SL_KEY_B2 /* this is checked explicitly */
|
|
#define SL_KEY_MAX ( ( unsigned int ) 0x2000 )
|
|
#define SL_KEY_ESC ( SL_KEY_MAX + 1 )
|
|
#define SL_KEY_MOU ( SL_KEY_ESC + 1 )
|
|
#define SL_KEY_ALT( ch ) ( SL_KEY_MAX + ( ( unsigned int ) ch ) )
|
|
|
|
/* we choose Ctrl+\ as an abort key on Unixes where it is a SIGQUIT key by default */
|
|
/* abort key is Ctrl+\ on Unix ( but Ctrl+@ on Linux console ) */
|
|
static int s_hb_sln_Abort_key = 28;
|
|
|
|
/* *********************************************************************** */
|
|
|
|
/* DeadKey definition's ENVVAR name. This EnvVar contains */
|
|
/* an ASCII value of a key, which serves as a DeadKey */
|
|
static char *hb_DeadKeyEnvName = "HRBNATIONDEADKEY";
|
|
|
|
/* a table for Keys work with a Dead key. The first
|
|
element contains a number of defined keys */
|
|
unsigned char hb_sln_convKDeadKeys[ 257 ]; /* it should be allocated by hb_xalloc() */
|
|
|
|
/* contains an integer value of a DeadKey or -1 */
|
|
int hb_DeadKey = -1;
|
|
|
|
/* escape key delay */
|
|
#ifdef HB_SLANG_ONE_ESC
|
|
int hb_sln_escDelay = 250;
|
|
#else
|
|
int hb_sln_escDelay = 0;
|
|
#endif
|
|
|
|
BOOL hb_sln_UnderLinuxConsole = FALSE;
|
|
BOOL hb_sln_UnderXterm = FALSE;
|
|
|
|
static int hb_sln_try_get_Kbd_State( void );
|
|
|
|
/* key translations tables - notice problems with compilation after changes */
|
|
#include "keytrans.c"
|
|
|
|
/* *********************************************************************** */
|
|
|
|
static void hb_sln_Init_TermType()
|
|
{
|
|
char * Env;
|
|
|
|
/* an uncertain way to check if we run under linux console */
|
|
Env = hb_getenv( "TERM" );
|
|
if( Env )
|
|
{
|
|
hb_sln_UnderLinuxConsole = *Env && ( strncmp( Env, "linux", 5 ) == 0 );
|
|
hb_xfree( ( void * ) Env );
|
|
}
|
|
/* an uncertain way to check if we run under xterm */
|
|
Env = hb_getenv( "TERM" );
|
|
if( Env )
|
|
{
|
|
hb_sln_UnderXterm = *Env && ( strstr( Env, "xterm" ) != NULL ||
|
|
strncmp( Env, "rxvt", 4 ) == 0 );
|
|
hb_xfree( ( void * ) Env );
|
|
}
|
|
}
|
|
|
|
/* *********************************************************************** */
|
|
|
|
static void hb_sln_Init_KeyTranslations()
|
|
{
|
|
char ch, keyname[ SLANG_MAX_KEYMAP_KEY_SEQ + 1 ];
|
|
int keynum, i;
|
|
char * keyseq;
|
|
|
|
/* for defining ^[<Key> sequences - this simulates Alt+Keys */
|
|
char AltChars[][ 2 ] =
|
|
{
|
|
{ '0', '9' },
|
|
{ 'A', 'Z' },
|
|
{ 'a', 'z' }
|
|
};
|
|
|
|
/* on Unix systems ESC is a special key so let
|
|
assume ESC is a doble pressed ESC key */
|
|
SLkp_define_keysym( "^[^[", SL_KEY_ESC );
|
|
|
|
/* try to define Shft-Fn and Ctrl-Fn keys.
|
|
Because we assume terminal has only 10 Fkeys
|
|
so F11-F30 is generated with Shift & Ctrl.
|
|
This is not guaranteed to work in all cases */
|
|
keynum = 11;
|
|
keyname[ 0 ] = 'F';
|
|
keyname[ 2 ] = 0;
|
|
|
|
/* Shft & Ctrl FKeys definition takes place in two
|
|
phases : from '1' to '9' and from 'A' to 'K' */
|
|
for( i = 1; i <= 2; i++ )
|
|
{
|
|
for( ch = ( i == 1 ? '1' : 'A' ); ch <= ( i == 1 ? '9' : 'K' ); ch++ )
|
|
{
|
|
keyname[ 1 ] = ch;
|
|
keyseq = SLtt_tgetstr( keyname );
|
|
if( keyseq != NULL && keyseq[ 0 ] != 0 )
|
|
SLkp_define_keysym( keyseq, SL_KEY_F( keynum ) );
|
|
keynum++;
|
|
}
|
|
}
|
|
|
|
/* We assume Esc key is a Meta key which is treated as an Alt key.
|
|
Also pressing Alt+Key on linux console and linux xterm gives the
|
|
same key sequences as with Meta key so we are happy */
|
|
|
|
keyname[ 0 ] = 033;
|
|
keyname[ 2 ] = 0;
|
|
|
|
/* Alt+Letter & Alt+digit definition takes place in three phases :
|
|
from '0' to '9', from 'A' to 'Z' and from 'a' to 'z' */
|
|
for( i = 0; i < 3; i++ )
|
|
{
|
|
for( ch = AltChars[ i ][ 0 ]; ch <= AltChars[ i ][ 1 ]; ch++ )
|
|
{
|
|
/* fprintf( stderr, "%d %c\n", i, ch ); */
|
|
keyname[ 1 ] = ch;
|
|
|
|
/* QUESTION: why Slang reports error for defining Alt+O ???.
|
|
Have I any hidden error in key definitiions ??? */
|
|
if( ch != 'O' )
|
|
SLkp_define_keysym( keyname, SL_KEY_ALT( ch ) );
|
|
}
|
|
}
|
|
|
|
/* mouse events under xterm */
|
|
if( hb_sln_UnderXterm )
|
|
{
|
|
keyseq = SLtt_tgetstr( "Km" );
|
|
if( ( keyseq != NULL ) && ( keyseq[ 0 ] != 0 ) )
|
|
{
|
|
/* fprintf( stderr, "%s\r\n", keyseq ); */
|
|
SLkp_define_keysym( keyseq, SL_KEY_MOU );
|
|
}
|
|
}
|
|
|
|
/* five on numeric console */
|
|
/* SLkp_define_keysym( "^[[G", SL_KEY_NUM_5 ); */
|
|
}
|
|
|
|
/* *********************************************************************** */
|
|
|
|
int hb_sln_Init_Terminal( int phase )
|
|
{
|
|
struct termios newTTY;
|
|
unsigned char * p;
|
|
int ret = 0;
|
|
|
|
/* first time init phase - we don't want this after
|
|
return from system command ( see run.c ) */
|
|
if( phase == 0 )
|
|
{
|
|
/* check if we run under linux console or under xterm */
|
|
hb_sln_Init_TermType();
|
|
|
|
#ifdef HB_OS_LINUX
|
|
/* for Linux console */
|
|
if( hb_sln_UnderLinuxConsole )
|
|
s_hb_sln_Abort_key = 0;
|
|
#endif
|
|
|
|
/* get Dead key definition */
|
|
p = ( unsigned char * ) hb_getenv( hb_DeadKeyEnvName );
|
|
|
|
if( p && p[ 0 ] != '\0' )
|
|
{
|
|
int len = strlen( ( char * ) p );
|
|
if( len > 0 )
|
|
hb_DeadKey = ( int ) *p;
|
|
}
|
|
if( p )
|
|
hb_xfree( ( void * ) p );
|
|
|
|
/* number of keys dealing with a Dead key */
|
|
hb_sln_convKDeadKeys[ 0 ] = 0;
|
|
}
|
|
|
|
/* Ctrl+\ to abort, no flow-control, no output processing */
|
|
if( SLang_init_tty( s_hb_sln_Abort_key, 0, 0 ) != -1 )
|
|
{
|
|
/* do missing disable of start/stop processing */
|
|
if( tcgetattr( SLang_TT_Read_FD, &newTTY ) == 0 )
|
|
{
|
|
newTTY.c_cc[ VSTOP ] = 255; /* disable ^S start/stop processing */
|
|
newTTY.c_cc[ VSTART ] = 255; /* disable ^Q start/stop processing */
|
|
newTTY.c_cc[ VSUSP ] = 255; /* disable ^Z suspend processing */
|
|
/* already done in Slang library */
|
|
/* newTTY.c_cc[ VDSUSP ] = 255; */ /* disable ^Y delayed suspend processing */
|
|
|
|
if( tcsetattr( SLang_TT_Read_FD, TCSADRAIN, &newTTY ) == 0 )
|
|
/* everything looks ok so far */
|
|
ret = 1;
|
|
}
|
|
}
|
|
|
|
/* first time init phase - we don't want this after
|
|
return from system command ( see run.c ) */
|
|
if( ret && ( phase == 0 ) )
|
|
{
|
|
/* define keyboard translations */
|
|
hb_sln_Init_KeyTranslations();
|
|
|
|
/* for binary search of key translations */
|
|
hb_sln_SortKeyTranslationTable();
|
|
}
|
|
|
|
return( ret );
|
|
}
|
|
|
|
/* *********************************************************************** */
|
|
|
|
int hb_gt_sln_ReadKey( PHB_GT pGT, int iEventMask )
|
|
{
|
|
static int InDeadState = FALSE;
|
|
unsigned int ch, tmp, kbdflags;
|
|
BOOL fInput;
|
|
int iKey;
|
|
|
|
HB_TRACE(HB_TR_DEBUG, ("hb_gt_sln_ReadKey(%p,%d)", pGT, (int) iEventMask));
|
|
|
|
/* user AbortKey break */
|
|
if( SLKeyBoard_Quit == 1 )
|
|
return HB_BREAK_FLAG;
|
|
|
|
/* has screen size changed ? */
|
|
if( hb_sln_bScreen_Size_Changed )
|
|
{
|
|
hb_sln_bScreen_Size_Changed = FALSE;
|
|
SLtt_get_screen_size();
|
|
#if SLANG_VERSION > 10202
|
|
SLsmg_reinit_smg();
|
|
#endif
|
|
|
|
/* TODO: we need here some kind of screen redrawing */
|
|
/*SLsmg_refresh();*/
|
|
HB_GTSELF_RESIZE( pGT, SLtt_Screen_Rows, SLtt_Screen_Cols );
|
|
}
|
|
|
|
fInput = SLang_input_pending( 0 ) != 0;
|
|
iKey = hb_gt_sln_mouse_Inkey( iEventMask, !fInput );
|
|
if( !fInput || iKey != 0 )
|
|
return iKey;
|
|
|
|
#if HB_GT_KBD_MODIF_MASK
|
|
kbdflags = hb_sln_try_get_Kbd_State();
|
|
#else
|
|
kbdflags = 0;
|
|
#endif
|
|
|
|
/* ------- one key ESC handling ----------------- */
|
|
/* NOTE: This will probably not work on slow terminals
|
|
or on very busy lines (i.e. modem lines ) */
|
|
ch = SLang_getkey();
|
|
if( ch == 033 ) /* escape char received, check for any pending chars */
|
|
{
|
|
if( hb_sln_escDelay == 0 )
|
|
{
|
|
/* standard acction, wait a 1 second for next char and if not then exit */
|
|
if( 0 == SLang_input_pending( 10 ) )
|
|
return( 0 );
|
|
}
|
|
else
|
|
{
|
|
/* wait hb_sln_escDelay milisec for next char and in not return ESC keycode */
|
|
if( 0 == SLang_input_pending( - HB_MAX( hb_sln_escDelay, 0 ) ) )
|
|
return( 033 );
|
|
}
|
|
}
|
|
|
|
/* user AbortKey break */
|
|
if( (int) ch == s_hb_sln_Abort_key )
|
|
return HB_BREAK_FLAG;
|
|
|
|
SLang_ungetkey( ch );
|
|
/* ------------------------------------------------- */
|
|
|
|
ch = SLkp_getkey();
|
|
|
|
/* unrecognized character */
|
|
if( ch == SL_KEY_ERR )
|
|
return( 0 );
|
|
|
|
/* Dead key handling */
|
|
if( InDeadState )
|
|
{
|
|
InDeadState = FALSE;
|
|
if( (int) ch == hb_DeadKey ) /* double press Dead key */
|
|
return( ch );
|
|
if( ch < 256 ) /* is this needed ??? */
|
|
{
|
|
int i;
|
|
for( i=0; i < ( int ) hb_sln_convKDeadKeys[ 0 ]; i++ )
|
|
if( ( int ) hb_sln_convKDeadKeys[ 2 * i + 1 ] == (int) ch )
|
|
return( ( int ) hb_sln_convKDeadKeys[ 2 * i + 2 ] );
|
|
}
|
|
return( 0 );
|
|
}
|
|
else if( (int) ch == hb_DeadKey )
|
|
{
|
|
/* entering Dead key state */
|
|
InDeadState = TRUE;
|
|
return( 0 );
|
|
}
|
|
|
|
/* any special key ? */
|
|
if( ( tmp = ( ch | ( kbdflags << 16 ) ) ) > 256 )
|
|
{
|
|
if( tmp == SL_KEY_MOU )
|
|
{
|
|
hb_gt_sln_mouse_ProcessTerminalEvent();
|
|
return hb_gt_sln_mouse_Inkey( iEventMask, FALSE );
|
|
}
|
|
|
|
if( ( iEventMask & INKEY_RAW ) != 0 )
|
|
return tmp;
|
|
|
|
tmp = hb_sln_FindKeyTranslation( tmp );
|
|
if( tmp != 0 )
|
|
return tmp;
|
|
|
|
/* TOFIX: this code is broken - needs a diffrent aproach */
|
|
tmp = hb_sln_FindKeyTranslation( ch );
|
|
if( tmp != 0 || ch > 256 )
|
|
return tmp;
|
|
}
|
|
|
|
#if !defined( HB_CDP_SUPPORT_OFF ) && ( defined( HB_SLN_UTF8 ) || defined( HB_SLN_UNICODE ) )
|
|
if ( hb_sln_Is_Unicode && ch < 256 )
|
|
{
|
|
int n = 0;
|
|
USHORT uc = 0;
|
|
|
|
if ( hb_cdpGetFromUTF8( hb_sln_cdpIN, FALSE, (BYTE) ch, &n, &uc ) )
|
|
{
|
|
unsigned int buf[ 10 ], i = 0;
|
|
|
|
while ( n > 0 )
|
|
{
|
|
if( SLang_input_pending( hb_sln_escDelay == 0 ? -100 :
|
|
- HB_MAX( hb_sln_escDelay, 0 ) ) == 0 )
|
|
break;
|
|
buf[ i++ ] = SLang_getkey();
|
|
if ( !hb_cdpGetFromUTF8( hb_sln_cdpIN, FALSE, (BYTE) buf[ i - 1 ], &n, &uc ) )
|
|
break;
|
|
}
|
|
if ( n > 0 )
|
|
{
|
|
while ( i > 0 )
|
|
SLang_ungetkey( buf[ --i ] );
|
|
}
|
|
else
|
|
ch = uc;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* standard ASCII key */
|
|
return ch < 256 ? hb_sln_inputTab[ ch ] : ch;
|
|
}
|
|
|
|
/* *********************************************************************** */
|
|
|
|
static int hb_sln_try_get_Kbd_State( void )
|
|
{
|
|
#if defined(__linux__)
|
|
unsigned char modifiers = 6;
|
|
|
|
if( ioctl( 0, TIOCLINUX, &modifiers ) < 0 )
|
|
return 0;
|
|
|
|
return ( int ) modifiers;
|
|
|
|
#elif defined(M_UNIX)
|
|
|
|
int modifiers = 0;
|
|
int IOcommand = 0;
|
|
|
|
if( ioctl( 0, TCGETSC, &IOcommand ) >= 0 )
|
|
{
|
|
/* if keyboard is not in SCANCODE mode */
|
|
if( IOcommand == KB_XSCANCODE )
|
|
{
|
|
/* try to set it to SCANCODE mode */
|
|
IOcommand = KB_ISSCANCODE;
|
|
if( ioctl( 0, TCSETSC, &IOcommand ) >= 0 )
|
|
{
|
|
/* if SCANCODE mode is set corectly try get KBD state */
|
|
if( ioctl( 0, KDGKBSTATE, &modifiers ) < 0 )
|
|
modifiers = 0;
|
|
|
|
/* turn a keyboard to a normal mode ( translation mode ) */
|
|
IOcommand = KB_XSCANCODE;
|
|
( void ) ioctl( 0, TCSETSC, &IOcommand )
|
|
}
|
|
}
|
|
/* keyboard is already in SCANCODE mode */
|
|
else if( ioctl( 0, KDGKBSTATE, &modifiers ) < 0 )
|
|
modifiers = 0;
|
|
|
|
return modifiers;
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* *********************************************************************** */
|
|
|
|
int hb_sln_Shft_Pressed()
|
|
{
|
|
return ( hb_sln_try_get_Kbd_State() & SHIFT_PRESSED ) != 0;
|
|
}
|
|
|
|
/* *********************************************************************** */
|
|
|
|
int hb_sln_Ctrl_Pressed()
|
|
{
|
|
return ( hb_sln_try_get_Kbd_State() & CONTROL_PRESSED ) != 0;
|
|
}
|
|
|
|
/* *********************************************************************** */
|
|
|
|
int hb_sln_Alt_Pressed()
|
|
{
|
|
return ( hb_sln_try_get_Kbd_State() & ALT_PRESSED ) != 0;
|
|
}
|
|
|
|
/* *********************************************************************** */
|
|
|
|
int hb_sln_Kbd_State()
|
|
{
|
|
return hb_sln_try_get_Kbd_State();
|
|
}
|
|
|
|
/* *********************************************************************** */
|