/* * $Id$ */ /* * Harbour Project source code: * Keyboard subsystem based on Slang screen library. * * Copyright 2000 Marek Paliwoda * 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 #ifdef M_UNIX #include #include #endif /* we're assuming target has termios - should be better done */ #include /* *********************************************************************** */ /* 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(); /* 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 ^[ 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( int iEventMask ) { static int InDeadState = FALSE; unsigned int ch, tmp, kbdflags; BOOL fInput; int iKey; HB_TRACE(HB_TR_DEBUG, ("hb_gt_sln_ReadKey(%d)", (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_gt_Resize( 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, (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, (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() { #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_slngt_Alt_Pressed() { return ( hb_sln_try_get_Kbd_State() & ALT_PRESSED ) != 0; } /* *********************************************************************** */ int hb_sln_Kbd_State() { return hb_sln_try_get_Kbd_State(); } /* *********************************************************************** */