diff options
Diffstat (limited to 'rhtvision/classes/linux/linuxkey.cc')
-rw-r--r-- | rhtvision/classes/linux/linuxkey.cc | 937 |
1 files changed, 937 insertions, 0 deletions
diff --git a/rhtvision/classes/linux/linuxkey.cc b/rhtvision/classes/linux/linuxkey.cc new file mode 100644 index 0000000..253f5b9 --- /dev/null +++ b/rhtvision/classes/linux/linuxkey.cc @@ -0,0 +1,937 @@ +/***************************************************************************** + + Linux keyboard routines. + Copyright (c) 1998-2002 by Salvador E. Tropea (SET) + Some portions come from code: + Copyright (c) 1998 by Robert Hoehne. + Covered by the GPL license. + + This driver is a relatively complex one because: + 1) Deals with console mode. + 2) Translates keyboard codes. + 3) Parses escape sequences. + 4) Patches keyboard tables and restores it on console switchs. + + Todo: + * How do I setup things before they are hooked? SetKbdMapping needs to + be reworked. + * Should I move VT detection to a separated module? + * Asegurarse que atexit se llame el que restaura el teclado. + * El módulo de TScreenLinux tendría que llamar a InitOnce(), si esto le da + error no seguir. Y si el tien problemas debería llamar al Suspend() para + que restaure. + +*****************************************************************************/ +#include <tv/configtv.h> + +#define Uses_stdio +#define Uses_string +#define Uses_unistd +#define Uses_signal +#define Uses_TEvent +#define Uses_TGKey +#define Uses_FullSingleKeySymbols +#define Uses_TScreen +#define Uses_TVCodePage +#include <tv.h> + +// I delay the check to generate as much dependencies as possible +#ifdef TVOSf_Linux + +#include <termios.h> +#include <fcntl.h> +#include <sys/ioctl.h> +// For the VT and key patch stuff +#include <sys/kd.h> +#include <sys/vt.h> + +#include <tv/linux/key.h> +#include <tv/linux/screen.h> +#include <tv/linux/log.h> + +int TGKeyLinux::hIn=-1; +FILE *TGKeyLinux::fIn=NULL; +int TGKeyLinux::oldInFlags; +int TGKeyLinux::newInFlags; +struct termios TGKeyLinux::inTermiosOrig; +struct termios TGKeyLinux::inTermiosNew; +const char *TGKeyLinux::error=NULL; +int TGKeyLinux::bufferKeys[MaxLenEscapeSequence]; +int TGKeyLinux::keysInBuffer=0; +int TGKeyLinux::nextKey=-1; +int TGKeyLinux::lastKeyCode; +int TGKeyLinux::lastModifiers; +int TGKeyLinux::translatedModifiers; +char TGKeyLinux::dontPatchKeyboard=0; +int TGKeyLinux::ourVT=-1; +struct vt_mode TGKeyLinux::oldVTMode; +struct vt_mode TGKeyLinux::newVTMode; +char TGKeyLinux::vtHooked=0; +char TGKeyLinux::canPatchKeyboard=0; +char TGKeyLinux::keyPatched=0; +char TGKeyLinux::ascii; +struct kbentry TGKeyLinux::entry; + +/* Linux IOCTL values found experimentally */ +const int kblNormal=0,kblShift=1,kblAltR=2,kblCtrl=4,kblAltL=8; + +/**[txh]******************************************************************** + + Description: + Does initialization tasks performed only once. + + Return: + 0 if success, !=0 if an error ocurred. In the last case the error member +points to a descriptive error. + +***************************************************************************/ + +int TGKeyLinux::InitOnce() +{ + LOG("TGKeyLinux::InitOnce"); + hIn=fileno(stdin); + + if (!isatty(hIn)) + { + error=_("that's an interactive application, don't redirect stdin"); + return 1; + } + // We can't use stdin for all the operations, instead we must open it again + // using another file descriptor. + // Here is why: In order to get some keys, I saw it for ESC pressed alone, + // we must set the O_NONBLOCK attribute. + // We can do it, but the stdout file handle seems to be just a copy of the + // stdin file handle (dupped?). When you use duplicated file handles they + // share the "File Status Flags". It means that setting O_NONBLOCK will + // set O_NONBLOCK for output too. So what? well when we do that the one that + // doesn't block is Linux kernel, so if we send too much information to + // stdout, no matters if we use fflush, sometimes Linux could lose data. + // This effect seems to happend at exit, may be because owr process dies + // before Linux have a chance to process the remaining data and it closes + // the file handle. The fact is that using O_NONBLOCK sometimes we fail to + // restore the cursor position. + // Now a question remains: do I have to restore the mode? + char *ttyName=ttyname(hIn); + if (!ttyName) + { + error=_("failed to get the name of the current terminal used for input"); + return 3; + } + fIn=fopen(ttyName,"r+b"); + if (!fIn) + { + error=_("failed to open the input terminal"); + return 4; + } + hIn=fileno(fIn); + + if (tcgetattr(hIn,&inTermiosOrig)) + { + error=_("can't get input terminal attributes"); + return 2; + } + + memcpy(&inTermiosNew,&inTermiosOrig,sizeof(inTermiosNew)); + // Ignore breaks + inTermiosNew.c_iflag|= (IGNBRK | BRKINT); + // Disable Xon/off + inTermiosNew.c_iflag&= ~(IXOFF | IXON); + // Character oriented, no echo, no signals + inTermiosNew.c_lflag&= ~(ICANON | ECHO | ISIG); + if (tcsetattr(hIn,TCSAFLUSH,&inTermiosNew)) + { + error=_("can't set input terminal attributes"); + return 3; + } + // Don't block, needed to get some keys, even when the input is in character + // oriented mode. I saw it for ESC alone. + oldInFlags=fcntl(hIn,F_GETFL,0); + newInFlags=oldInFlags | O_NONBLOCK; + fcntl(hIn,F_SETFL,newInFlags); + + // Find if we are running in a VT and if that's the case the number + // This name should be the same but ... + ttyName=ttyname(STDOUT_FILENO); + if (ttyName) + { + if (sscanf(ttyName,"/dev/tty%2d",&ourVT)!=1) + // SET: Some Slackware systems (I think that is what Andris uses) + // define /dev/ttyNN as symlinks to /dev/vc/NN: + if (sscanf(ttyName,"/dev/vc/%2d",&ourVT)!=1) + ourVT=-1; + } + // Get the mode of the current VT + if (ourVT!=-1) + { + if (ioctl(hIn,VT_GETMODE,&oldVTMode)) + // If we fail disable it + ourVT=-1; + else + {// Tell the kernel to inform us about console changes + newVTMode=oldVTMode; + newVTMode.mode =VT_PROCESS; + newVTMode.relsig=SIGUSR1; + newVTMode.acqsig=SIGUSR2; + } + } + // Check if we can patch the keyboard + canPatchKeyboard=0; + long optPatchKeys=1; // Default is patch the keyboard + TScreen::optSearch("PatchKeys",optPatchKeys); + if (optPatchKeys && ourVT!=-1 && ioctl(hIn,KDGKBENT,&entry)==0) + { + canPatchKeyboard=1; + keyMapInit(); + } + // Now do the hook/patching + doHookAndPatch(); + // We don't need to call Resume + suspended=0; + return 0; +} + +/**[txh]******************************************************************** + + Description: + Restore the original console state. + +***************************************************************************/ + +void TGKeyLinux::Suspend() +{ + doUnHookAndUnPatch(); + // Now we use a new opened file handle for input. + // As these flags are the flags for the file handle (not the terminal itself) + // we don't need to restore the originals. + // Why don't do it anyways? because the TScreen::resume is called before and + // it needs to get the cursor position using escape sequences without waiting + // for an EOL. + //fcntl(hIn,F_SETFL,oldInFlags); + tcsetattr(hIn,TCSAFLUSH,&inTermiosOrig); + LOG("TGKeyLinux::Suspend"); +} + +/**[txh]******************************************************************** + + Description: + Memorize current console state and setup the one needed for us. + +***************************************************************************/ + +void TGKeyLinux::Resume() +{// Read current state + tcgetattr(hIn,&inTermiosOrig); + oldInFlags=fcntl(hIn,F_GETFL,0); + // Set our state + tcsetattr(hIn,TCSAFLUSH,&inTermiosNew); + // The following shouldn't be needed, I'll let it here unless I discover + // some side effect. + fcntl(hIn,F_SETFL,newInFlags); + // The user could do some action to alter the keyboard mapping tables + if (canPatchKeyboard) + keyMapInit(); + // Patch keyboard and hook the signals + doHookAndPatch(); + LOG("TGKeyLinux::Resume"); +} + +int TGKeyLinux::KbHit() +{ + if (keysInBuffer || nextKey!=-1) + return 1; // We have a key waiting for processing + nextKey=fgetc(fIn); + return nextKey!=-1; +} + +void TGKeyLinux::Clear() +{ + // Discard our buffer + keysInBuffer=0; + // Discard a key waiting + nextKey=-1; + // Flush the input + fflush(fIn); +} + +/***************************************************************************** + + Here starts the keyboard parser and translator. + It uses a static tree/hash to parse the escape sequences, may be this should +be configurable. + +*****************************************************************************/ + +// - 9 = Tab tiene conflicto con kbI+Control lo cual es natural, por otro +// -lado Ctrl+Tab no lo reporta en forma natural +// - a = Enter tiene conflicto con ^J, como ^J no lo reporta naturalmente sino +// -forzado por el keymap lo mejor es definirlo directamente. +unsigned char TGKeyLinux::kbToName[128] = +{ + 0,kbA,kbB,kbC,kbD,kbE,kbF,kbG, // 00-07 + kbH,kbTab,kbEnter,kbK,kbL,kbM,kbN,kbO, // 08-0F + kbP,kbQ,kbR,kbS,kbT,kbU,kbV,kbW, // 10-17 + kbX,kbY,kbZ,kbEsc,0,kbCloseBrace,kb6,kbMinus, // 18-1F + kbSpace,kbAdmid,kbDobleQuote,kbNumeral,kbDolar,kbPercent,kbAmper,kbQuote,// 20-27 + kbOpenPar,kbClosePar,kbAsterisk,kbPlus,kbComma,kbMinus,kbStop,kbSlash, // 28-2F + kb0,kb1,kb2,kb3,kb4,kb5,kb6,kb7, // 30-37 + kb8,kb9,kbDoubleDot,kbColon,kbLessThan,kbEqual,kbGreaterThan,kbQuestion, // 38-3F + kbA_Roba,kbA,kbB,kbC,kbD,kbE,kbF,kbG, // 40-47 + kbH,kbI,kbJ,kbK,kbL,kbM,kbN,kbO, // 48-4F + kbP,kbQ,kbR,kbS,kbT,kbU,kbV,kbW, // 50-57 + kbX,kbY,kbZ,kbOpenBrace,kbBackSlash,kbCloseBrace,kbCaret,kbUnderLine, // 58-5F + kbGrave,kbA,kbB,kbC,kbD,kbE,kbF,kbG, // 60-67 + kbH,kbI,kbJ,kbK,kbL,kbM,kbN,kbO, // 68-6F + kbP,kbQ,kbR,kbS,kbT,kbU,kbV,kbW, // 70-77 + kbX,kbY,kbZ,kbOpenCurly,kbOr,kbCloseCurly,kbTilde,kbBackSpace // 78-7F +}; + +unsigned char TGKeyLinux::kbExtraFlags[128] = +{ + 0,kblCtrl,kblCtrl,kblCtrl,kblCtrl,kblCtrl,kblCtrl,kblCtrl, // 00-07 + kblCtrl,0,0,kblCtrl,kblCtrl,kblCtrl,kblCtrl,kblCtrl, // 08-0F + kblCtrl,kblCtrl,kblCtrl,kblCtrl,kblCtrl,kblCtrl,kblCtrl,kblCtrl, // 10-17 + kblCtrl,kblCtrl,kblCtrl,0,0,kblCtrl,kblCtrl,kblCtrl, // 18-1F + 0,kblShift,kblShift,kblShift,kblShift,kblShift,kblShift,0, // 20-27 + kblShift,kblShift,kblShift,kblShift,0,0,0,0, // 28-2F + 0,0,0,0,0,0,0,0, // 30-37 + 0,0,kblShift,0,kblShift,0,kblShift,kblShift, // 38-3F + kblShift,kblShift,kblShift,kblShift,kblShift,kblShift,kblShift,kblShift, // 40-47 + kblShift,kblShift,kblShift,kblShift,kblShift,kblShift,kblShift,kblShift, // 48-4F + kblShift,kblShift,kblShift,kblShift,kblShift,kblShift,kblShift,kblShift, // 50-57 + kblShift,kblShift,kblShift,0,0,0,kblShift,kblShift, // 58-5F + 0,0,0,0,0,0,0,0, // 60-67 + 0,0,0,0,0,0,0,0, // 68-6F + 0,0,0,0,0,0,0,0, // 70-77 + 0,0,0,kblShift,kblShift,kblShift,kblShift,0, // 78-7F +}; + +/************************** Escape sequences tree **************************/ +struct node +{ + char value; + unsigned char code; + unsigned char modifiers; + node *next; +}; + +static node F6[]={{1,0,0},{'~',kbF6,0,0}}; +static node F7[]={{1,0,0},{'~',kbF7,0,0}}; +static node F8[]={{1,0,0},{'~',kbF8,0,0}}; +static node F9[]={{1,0,0},{'~',kbF9,0,0}}; +static node F10[]={{1,0,0},{'~',kbF10,0,0}}; +static node F11[]={{1,0,0},{'~',kbF11,0,0}}; +static node F12[]={{1,0,0},{'~',kbF12,0,0}}; +static node Delete[]={{1,0,0},{'~',kbDelete,0,0}}; +static node End[]={{1,0,0},{'~',kbEnd,0,0}}; +static node PgUp[]={{1,0,0},{'~',kbPgUp,0,0}}; +static node PgDn[]={{1,0,0},{'~',kbPgDn,0,0}}; + +static node Brace1[]= +{ + {4,0,0}, + {'~',kbHome,0,0}, + {'7',0,0,F6}, + {'8',0,0,F7}, + {'9',0,0,F8} +}; + +static node Brace2[]= +{ + {5,0,0}, + {'~',kbInsert,0,0}, + {'0',0,0,F9}, + {'1',0,0,F10}, + {'3',0,0,F11}, + {'4',0,0,F12} +}; + +static node BraceKeys[]= +{ + {5,0,0}, + {'A',kbF1,0,0}, + {'B',kbF2,0,0}, + {'C',kbF3,0,0}, + {'D',kbF4,0,0}, + {'E',kbF5,0,0}, +}; + +static node Sequences[]= +{ + {13,0,0}, + {'[',0,0,BraceKeys}, + {'A',kbUp,0,0}, + {'B',kbDown,0,0}, + {'C',kbRight,0,0}, + {'D',kbLeft,0,0}, + {'G',kb5,0,0}, + {'P',kbPause,0,0}, + {'1',0,0,Brace1}, + {'2',0,0,Brace2}, + {'3',0,0,Delete}, + {'4',0,0,End}, + {'5',0,0,PgUp}, + {'6',0,0,PgDn} +}; + +static node Keys[]= +{ + {1,0,0}, + {'[',0,0,Sequences} +}; +/*********************** End of escape sequences tree ***********************/ + +/**[txh]******************************************************************** + + Description: + Parse a escape sequence. + + Returns: 1 if the sequence found, 0 if not and the keys are stored in the +buffer. + +***************************************************************************/ + +int TGKeyLinux::ProcessEscape() +{ + int nextVal; + + nextVal=fgetc(fIn); + if (nextVal==EOF) // Just ESC + return 0; + + node *p=Keys; + int cant,i; + keysInBuffer=0; + while (nextVal!=EOF) + { + NextNode: + bufferKeys[keysInBuffer++]=nextVal; + cant=p->value; + for (i=1; i<=cant; i++) + { + if (p[i].value==nextVal) + { + if (p[i].next) + { + p=p[i].next; + nextVal=fgetc(fIn); + goto NextNode; + } + lastKeyCode=p[i].code; + lastModifiers=p[i].modifiers; + keysInBuffer=0; + return 1; + } + } + return 0; + } + return 0; +} + +int TGKeyLinux::GetKeyFromBuffer() +{ + int ret=bufferKeys[--keysInBuffer]; + if (keysInBuffer) + memcpy(bufferKeys,bufferKeys+1,keysInBuffer); + return ret; +} + +/**[txh]******************************************************************** + + Description: + Gets a key from the buffer, waiting value or stdin and if needed calls +the escape sequence parser. + +***************************************************************************/ + +int TGKeyLinux::GetKeyParsed() +{ + lastModifiers=0; + translatedModifiers=-1; + // If we have keys in the buffer take from there. + // They already failed the escape sequence test. + if (keysInBuffer) + return GetKeyFromBuffer(); + + // Use the value we have on hold or get a new one + int nextVal=nextKey; + nextKey=-1; + if (nextVal==-1) + nextVal=fgetc(fIn); + if (nextVal==-1) + return -1; + + // Is that an escape sequence? + if (nextVal=='\e') + { + if (ProcessEscape()) + return -2; + if (!keysInBuffer) + return '\e'; + lastKeyCode=GetKeyFromBuffer(); + lastModifiers=kblAltL; + return -3; + } + + return nextVal; +} + +/**[txh]******************************************************************** + + Description: + Gets the next key, their modifiers and ASCII. Is a postprocessor for +GetKeyParsed. + +***************************************************************************/ + +int TGKeyLinux::GetRaw() +{ + int result=GetKeyParsed(); + + if (result==-1) + return 0; // No key + if (result==-2) + { + lastModifiers|=GetLinuxShiftState(); + ascii=0; + return 1; // Key already processed + } + if (result==-3) // Forced modifier + { + result=lastKeyCode; + ascii=result>127 ? result : 0; + } + else + ascii=result; + + // Translate the key + if (result>=128) + lastKeyCode=kbUnkNown; + else + { + lastModifiers|=kbExtraFlags[result]; + lastKeyCode=kbToName[result]; + } + lastModifiers|=GetLinuxShiftState(); + return 1; +} + +/**[txh]******************************************************************** + + Description: + Gets a key from the input and converts it into the TV format. + +***************************************************************************/ + +ushort TGKeyLinux::GKey() +{ + if (GetRaw()) + { // Here I add the modifiers to the key code + if (lastModifiers & kblShift) + lastKeyCode|=kbShiftCode; + if (lastModifiers & kblCtrl) + lastKeyCode|=kbCtrlCode; + switch (AltSet) + { + case 0: // Normal thing, left is left, right is right + if (lastModifiers & kblAltL) + lastKeyCode|=kbAltLCode; + else + if (lastModifiers & kblAltR) + lastKeyCode|=kbAltRCode; + break; + case 1: // Reverse thing + if (lastModifiers & kblAltL) + lastKeyCode|=kbAltRCode; + else + if (lastModifiers & kblAltR) + lastKeyCode|=kbAltLCode; + break; + default: // Compatibility + if (lastModifiers & (kblAltL | kblAltR)) + lastKeyCode|=kbAltLCode; + } + return lastKeyCode; + } + return kbUnkNown; +} +/***************************************************************************** + End of parser and translator. +*****************************************************************************/ + +/**[txh]******************************************************************** + + Description: + Finds the value of the modifiers, if the ioctl fails the values we know +from the last key are used. + + Return: The modifiers in Linux kernel format. + +***************************************************************************/ + +unsigned TGKeyLinux::GetLinuxShiftState() +{ + int arg=6; /* TIOCLINUX function #6 */ + unsigned shift=0; + + if (ioctl(hIn,TIOCLINUX,&arg)!=-1) + shift=arg; + else + shift=lastModifiers; + return shift; +} + +/**[txh]******************************************************************** + + Description: + Finds the value of the modifiers in TV format. + +***************************************************************************/ + +unsigned TGKeyLinux::GetShiftState() +{ + lastModifiers=GetLinuxShiftState(); + if (!lastModifiers) return 0; + if (translatedModifiers==-1) + { + translatedModifiers=0; + if (lastModifiers & kblShift) + translatedModifiers|=kbLeftShiftDown | kbRightShiftDown; + if (lastModifiers & kblCtrl) + translatedModifiers|=kbLeftCtrlDown | kbRightCtrlDown | kbCtrlDown; + if (lastModifiers & kblAltR) + translatedModifiers|=kbRightAltDown | kbAltDown; + if (lastModifiers & kblAltL) + translatedModifiers|=kbLeftAltDown | kbAltDown; + } + return translatedModifiers; +} + + +/**[txh]******************************************************************** + + Description: + Fills the TV event structure for a key. + +***************************************************************************/ + +void TGKeyLinux::FillTEvent(TEvent &e) +{ + GKey(); + e.keyDown.charScan.charCode=lastModifiers & kblAltL ? 0 : ascii; + e.keyDown.charScan.scanCode=ascii; + e.keyDown.raw_scanCode=ascii; + e.keyDown.keyCode=lastKeyCode; + e.keyDown.shiftState=lastModifiers; + e.what=evKeyDown; +} + + +/***************************************************************************** + + Here starts the code to modify Linux kernel tables in order to get better +keyboard information. + Basically it makes Linux kernel map the key+modifiers to the key alone. +That's because these keys acts in special ways when a modifier is applied and +the application can't receive the key. + An example: Shift+PgUp is usually mapped to "scroll up one screen", so we +map it to just PgUp. In this way we get the key, then we get the modifiers +from the ioctl. + That's the code based in Robert's code it was readapted but the idea is +the same. + The code is complicated because we must restore the map when the user +changes to another console. So most of the code deals with the signals +related to virtual console switching. + +*****************************************************************************/ + +typedef struct +{ + uchar change_table; + uchar change_index; + uchar old_table; + uchar old_index; + ushort old_val; + ushort new_val; +} change_entry; + +#define SCAN_F1 0x3b +#define SCAN_F2 0x3c +#define SCAN_F3 0x3d +#define SCAN_F4 0x3e +#define SCAN_F5 0x3f +#define SCAN_F6 0x40 +#define SCAN_F7 0x41 +#define SCAN_F8 0x42 +#define SCAN_F9 0x43 +#define SCAN_F10 0x44 +#define SCAN_Q 0x10 +#define SCAN_S 0x1f +#define SCAN_J 0x24 +#define SCAN_M 0x32 +#define SCAN_Z 0x2C +#define SCAN_PGUP 104 +#define SCAN_PGDN 109 +#define SCAN_BKSP 14 +#define SCAN_SPAC 57 +#define SCAN_TAB 15 +// As ^[ is usually Esc we must be sure it doesn't happend +#define SCAN_LFBR 26 +// The Scroll lock key is evil for an interactive application +// As in my keyboard that's above Home and some people wrongly +// press it instead of Home I'm translating to Home. +#define SCAN_SCRL 70 +#define SCAN_HOME 102 + +static +change_entry changes[]= +{ + { kblAltL, SCAN_F1, kblNormal, SCAN_F1, 0, 0}, + { kblAltR, SCAN_F1, kblNormal, SCAN_F1, 0, 0}, + { kblCtrl, SCAN_F1, kblNormal, SCAN_F1, 0, 0}, + { kblShift, SCAN_F1, kblNormal, SCAN_F1, 0, 0}, + { kblAltL, SCAN_F2, kblNormal, SCAN_F2, 0, 0}, + { kblAltR, SCAN_F2, kblNormal, SCAN_F2, 0, 0}, + { kblCtrl, SCAN_F2, kblNormal, SCAN_F2, 0, 0}, + { kblShift, SCAN_F2, kblNormal, SCAN_F2, 0, 0}, + { kblAltL, SCAN_F3, kblNormal, SCAN_F3, 0, 0}, + { kblAltR, SCAN_F3, kblNormal, SCAN_F3, 0, 0}, + { kblCtrl, SCAN_F3, kblNormal, SCAN_F3, 0, 0}, + { kblShift, SCAN_F3, kblNormal, SCAN_F3, 0, 0}, + { kblAltL, SCAN_F4, kblNormal, SCAN_F4, 0, 0}, + { kblAltR, SCAN_F4, kblNormal, SCAN_F4, 0, 0}, + { kblCtrl, SCAN_F4, kblNormal, SCAN_F4, 0, 0}, + { kblShift, SCAN_F4, kblNormal, SCAN_F4, 0, 0}, + { kblAltL, SCAN_F5, kblNormal, SCAN_F5, 0, 0}, + { kblAltR, SCAN_F5, kblNormal, SCAN_F5, 0, 0}, + { kblCtrl, SCAN_F5, kblNormal, SCAN_F5, 0, 0}, + { kblShift, SCAN_F5, kblNormal, SCAN_F5, 0, 0}, + { kblAltL, SCAN_F6, kblNormal, SCAN_F6, 0, 0}, + { kblAltR, SCAN_F6, kblNormal, SCAN_F6, 0, 0}, + { kblCtrl, SCAN_F6, kblNormal, SCAN_F6, 0, 0}, + { kblShift, SCAN_F6, kblNormal, SCAN_F6, 0, 0}, + { kblAltL, SCAN_F7, kblNormal, SCAN_F7, 0, 0}, + { kblAltR, SCAN_F7, kblNormal, SCAN_F7, 0, 0}, + { kblCtrl, SCAN_F7, kblNormal, SCAN_F7, 0, 0}, + { kblShift, SCAN_F7, kblNormal, SCAN_F7, 0, 0}, + { kblAltL, SCAN_F8, kblNormal, SCAN_F8, 0, 0}, + { kblAltR, SCAN_F8, kblNormal, SCAN_F8, 0, 0}, + { kblCtrl, SCAN_F8, kblNormal, SCAN_F8, 0, 0}, + { kblShift, SCAN_F8, kblNormal, SCAN_F8, 0, 0}, + { kblAltL, SCAN_F9, kblNormal, SCAN_F9, 0, 0}, + { kblAltR, SCAN_F9, kblNormal, SCAN_F9, 0, 0}, + { kblCtrl, SCAN_F9, kblNormal, SCAN_F9, 0, 0}, + { kblShift, SCAN_F9, kblNormal, SCAN_F9, 0, 0}, + { kblAltL, SCAN_F10, kblNormal, SCAN_F10, 0, 0}, + { kblAltR, SCAN_F10, kblNormal, SCAN_F10, 0, 0}, + { kblCtrl, SCAN_F10, kblNormal, SCAN_F10, 0, 0}, + { kblShift, SCAN_F10, kblNormal, SCAN_F10, 0, 0}, + { kblCtrl, SCAN_Q, kblNormal, SCAN_Q, 0, 0}, + { kblCtrl, SCAN_S, kblNormal, SCAN_S, 0, 0}, + { kblCtrl, SCAN_J, kblNormal, SCAN_J, 0, 0}, + { kblCtrl, SCAN_M, kblNormal, SCAN_M, 0, 0}, + { kblCtrl, SCAN_Z, kblNormal, SCAN_Z, 0, 0}, + { kblShift, SCAN_PGUP, kblNormal, SCAN_PGUP, 0, 0}, + { kblShift, SCAN_PGDN, kblNormal, SCAN_PGDN, 0, 0}, + { kblCtrl, SCAN_BKSP, kblNormal, SCAN_BKSP, 0, 0}, + { kblCtrl, SCAN_SPAC, kblNormal, SCAN_SPAC, 0, 0}, + { kblCtrl, SCAN_LFBR, kblNormal, SCAN_LFBR, 0, 0}, + { kblNormal,SCAN_SCRL, kblNormal, SCAN_HOME, 0, 0}, + { kblAltL, SCAN_SCRL, kblNormal, SCAN_HOME, 0, 0}, + { kblAltR, SCAN_SCRL, kblNormal, SCAN_HOME, 0, 0}, + { kblCtrl, SCAN_TAB, kblNormal, SCAN_TAB, 0, 0}, + { kblCtrl | kblShift, SCAN_TAB, kblNormal, SCAN_TAB, 0, 0} +}; + +#define changeSize (sizeof(changes)/sizeof(change_entry)) + +void TGKeyLinux::unPatchKeyMap() +{ + if (!canPatchKeyboard || !keyPatched) + return; + + unsigned i; + for (i=0;i<changeSize;i++) + { + change_entry *e=&changes[i]; + entry.kb_table=e->change_table; + entry.kb_index=e->change_index; + entry.kb_value=e->old_val; + ioctl(hIn,KDSKBENT,&entry); + } + keyPatched=0; +} + +void TGKeyLinux::patchKeyMap() +{ + if (!canPatchKeyboard || keyPatched) + return; + + unsigned i; + for (i=0;i<changeSize;i++) + { + change_entry *e=&changes[i]; + entry.kb_table=e->change_table; + entry.kb_index=e->change_index; + entry.kb_value=e->new_val; + ioctl(hIn,KDSKBENT,&entry); + } + keyPatched=1; +} + +// Get the information needed to patch/unpatch +void TGKeyLinux::keyMapInit() +{ + unsigned i; + for (i=0; i<changeSize; i++) + { + change_entry *e=&changes[i]; + entry.kb_table=e->change_table; + entry.kb_index=e->change_index; + ioctl(hIn,KDGKBENT,&entry); + e->old_val=entry.kb_value; + entry.kb_table=e->old_table; + entry.kb_index=e->old_index; + ioctl(hIn,KDGKBENT,&entry); + e->new_val=entry.kb_value; + } +} + +void TGKeyLinux::hookVTSignals() +{ + if (vtHooked || dontPatchKeyboard || ourVT==-1) + return; + + // -------- Set up signal handlers to know about console switches + struct sigaction sig; + sigemptyset(&sig.sa_mask); + sigaddset(&sig.sa_mask,SIGUSR1); + sigaddset(&sig.sa_mask,SIGUSR2); + sig.sa_flags=SA_RESTART; + sigprocmask(SIG_BLOCK,&sig.sa_mask,NULL); // No switches now, we are not + // initialized yet + sig.sa_handler=releaseVTHandler; + sigaction(SIGUSR1,&sig,NULL); + sig.sa_handler=acquireVTHandler; + sigaction(SIGUSR2,&sig,NULL); + vtHooked=1; + + // -------- Tell our console to inform us about switches + if (ioctl(hIn,VT_SETMODE,&newVTMode)) + { + error=_("ioctl VT_SETMODE failed"); + return; + } + + sigprocmask(SIG_UNBLOCK,&sig.sa_mask,NULL); +} + +void TGKeyLinux::unHookVTSignals() +{ + if (!vtHooked || ourVT==-1) + return; + + // Make both signals to behave as default + struct sigaction sig; + sigemptyset(&sig.sa_mask); + sigaddset(&sig.sa_mask,SIGUSR1); + sigaddset(&sig.sa_mask,SIGUSR2); + sig.sa_flags=SA_RESTART; + sigprocmask(SIG_BLOCK,&sig.sa_mask,NULL); // No switches now, we are not + // initialized yet + sig.sa_handler=SIG_DFL; + sigaction(SIGUSR1,&sig,NULL); + sig.sa_handler=SIG_DFL; + sigaction(SIGUSR2,&sig,NULL); + + ioctl(hIn,VT_SETMODE,&oldVTMode); + + sigprocmask(SIG_UNBLOCK,&sig.sa_mask,NULL); + vtHooked=0; +} + +void TGKeyLinux::releaseVTHandler(int) +{ + unPatchKeyMap(); + TMouse::suspend(); + TScreenLinux::SuspendFont(); + ioctl(hIn,VT_RELDISP,1); +} + +void TGKeyLinux::acquireVTHandler(int) +{ + ioctl(hIn,VT_RELDISP,VT_ACKACQ); + ioctl(hIn,VT_WAITACTIVE,ourVT); + patchKeyMap(); + TMouse::resume(); + TScreenLinux::ResumeFont(); +} + +void TGKeyLinux::doHookAndPatch() +{ + if (!dontPatchKeyboard && canPatchKeyboard) + { + patchKeyMap(); + hookVTSignals(); + } +} + +void TGKeyLinux::doUnHookAndUnPatch() +{ + unPatchKeyMap(); + unHookVTSignals(); +} +/***************************************************************************** + End of keyboard patching and VT change hooking +*****************************************************************************/ + +void TGKeyLinux::SetKbdMapping(int version) +{ + if (version==linuxDisableKeyPatch) + { + dontPatchKeyboard=1; + doUnHookAndUnPatch(); + } + else if (version==linuxEnableKeyPatch) + { + dontPatchKeyboard=0; + doHookAndPatch(); + } + Mode=version; +} + +int TGKeyLinux::GetKbdMapping(int version) +{ + if (version==linuxDisableKeyPatch) + { + return dontPatchKeyboard; + } + else if (version==linuxEnableKeyPatch) + { + return !dontPatchKeyboard; + } + return 0; +} + +void TGKeyLinux::Init(int map) +{ + TGKey::Suspend =TGKeyLinux::Suspend; + TGKey::Resume =TGKeyLinux::Resume; + TGKey::kbhit =KbHit; + TGKey::clear =Clear; + TGKey::gkey =GKey; + TGKey::getShiftState =GetShiftState; + TGKey::fillTEvent =FillTEvent; + TGKey::SetKbdMapping =TGKeyLinux::SetKbdMapping; + TGKey::GetKbdMapping =TGKeyLinux::GetKbdMapping; + if (map==KOI8) + { + TGKey::SetCodePage(TVCodePage::KOI8r); + LOG("Using KOI8 keyboard table"); + } +} +#else // TVOSf_Linux + +#include <tv/linux/key.h> +#include <tv/linux/log.h> + +#endif // else TVOSf_Linux |