/* This file is part of Konsole, an X terminal. Copyright (C) 1997,1998 by Lars Doelle 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 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* The keyboard translation table allows to configure konsoles behavior on key strokes. FIXME: some bug crept in, disallowing '\0' to be emitted. */ #include "keytrans.h" #include #include #include #include #include #include #include #include /* KeyEntry instances represent the individual assignments */ KeyTrans::KeyEntry::KeyEntry(int _ref, int _key, int _bits, int _mask, int _cmd, TQString _txt) : ref(_ref), key(_key), bits(_bits), mask(_mask), cmd(_cmd), txt(_txt) { } KeyTrans::KeyEntry::~KeyEntry() { } bool KeyTrans::KeyEntry::matches(int _key, int _bits, int _mask) { int m = mask & _mask; return _key == key && (bits & m) == (_bits & m); } bool KeyTrans::KeyEntry::metaspecified(void) { return ((mask & (1 << BITS_Alt)) && (bits & (1 << BITS_Alt))) || ((mask & (1 << BITS_AnyMod)) && (bits & (1 << BITS_AnyMod))); } bool KeyTrans::KeyEntry::anymodspecified(void) { return (mask & (1 << BITS_AnyMod)) && (bits & (1 << BITS_AnyMod)); } TQString KeyTrans::KeyEntry::text() { return txt; } /* KeyTrans combines the individual assignments to a proper map Takes part in a collection themself. */ KeyTrans::KeyTrans(const TQString& path) :m_path(path) ,m_numb(0) ,m_fileRead(false) { tableX.setAutoDelete(true); if (m_path=="[buildin]") { m_id = "default"; } else { m_id = m_path; int i = m_id.tqfindRev('/'); if (i > -1) m_id = m_id.mid(i+1); i = m_id.tqfindRev('.'); if (i > -1) m_id = m_id.left(i); } } KeyTrans::KeyTrans() { /* table.setAutoDelete(true); path = ""; numb = 0;*/ } KeyTrans::~KeyTrans() { } KeyTrans::KeyEntry* KeyTrans::addEntry(int ref, int key, int bits, int mask, int cmd, TQString txt) // returns conflicting entry { for (TQPtrListIterator it(tableX); it.current(); ++it) { if (it.current()->matches(key,bits,mask)) { return it.current(); } } tableX.append(new KeyEntry(ref,key,bits,mask,cmd,txt)); return (KeyEntry*)NULL; } bool KeyTrans::findEntry(int key, int bits, int* cmd, const char** txt, int* len, bool* metaspecified) { if (!m_fileRead) readConfig(); if (bits & ((1< it(tableX); it.current(); ++it) if (it.current()->matches(key,bits,0xffff)) { *cmd = it.current()->cmd; *len = it.current()->txt.length(); if ((*cmd==CMD_send) && it.current()->anymodspecified() && (*len < 16)) { static char buf[16]; char *c; char mask = '1' + BITS(0, bits&(1<txt.ascii()); c = (char*)strchr(buf, '*'); if (c) *c = mask; *txt = buf; } else *txt = it.current()->txt.ascii(); *metaspecified = it.current()->metaspecified(); return true; } return false; } /* ------------------------------------------------------------------------- */ /* */ /* Scanner for keyboard configuration */ /* */ /* ------------------------------------------------------------------------- */ // regular tokenizer /* Tokens - Spaces - Name (A-Za-z0-9)+ - String - Opr on of +-: */ #define SYMName 0 #define SYMString 1 #define SYMEol 2 #define SYMEof 3 #define SYMOpr 4 #define SYMError 5 #define inRange(L,X,H) ((L <= X) && (X <= H)) #define isNibble(X) (inRange('A',X,'F')||inRange('a',X,'f')||inRange('0',X,'9')) #define convNibble(X) (inRange('0',X,'9')?X-'0':X+10-(inRange('A',X,'F')?'A':'a')) class KeytabReader { public: KeytabReader(TQString p, TQIODevice &d); public: void getCc(); void getSymbol(); void parseTo(KeyTrans* kt); void ReportError(const char* msg); void ReportToken(); // diagnostic private: int sym; TQString res; int len; int slinno; int scolno; private: int cc; int linno; int colno; TQIODevice* buf; TQString path; }; KeytabReader::KeytabReader(TQString p, TQIODevice &d) { path = p; buf = &d; cc = 0; colno = 0; } void KeytabReader::getCc() { if (cc == '\n') { linno += 1; colno = 0; } if (cc < 0) return; cc = buf->getch(); colno += 1; } void KeytabReader::getSymbol() { res = ""; len = 0; sym = SYMError; while (cc == ' ') getCc(); // skip spaces if (cc == '#') // skip comment { while (cc != '\n' && cc > 0) getCc(); } slinno = linno; scolno = colno; if (cc <= 0) { sym = SYMEof; // eos } else if (cc == '\n') { getCc(); sym = SYMEol; // eol } else if (inRange('A',cc,'Z')||inRange('a',cc,'z')||inRange('0',cc,'9')||(cc=='_')) { while (inRange('A',cc,'Z') || inRange('a',cc,'z') || inRange('0',cc,'9') || (cc=='_')) { res = res + (char)cc; getCc(); } sym = SYMName; } else if (strchr("+-:",cc)) { res = ""; res = res + (char)cc; getCc(); sym = SYMOpr; } else if (cc == '"') { getCc(); while (cc >= ' ' && cc != '"') { int sc; if (cc == '\\') // handle quotation { getCc(); switch (cc) { case 'E' : sc = 27; getCc(); break; case 'b' : sc = 8; getCc(); break; case 'f' : sc = 12; getCc(); break; case 't' : sc = 9; getCc(); break; case 'r' : sc = 13; getCc(); break; case 'n' : sc = 10; getCc(); break; case '\\' : // fall thru case '"' : sc = cc; getCc(); break; case 'x' : getCc(); sc = 0; if (!isNibble(cc)) return; sc = 16*sc + convNibble(cc); getCc(); if (!isNibble(cc)) return; sc = 16*sc + convNibble(cc); getCc(); break; default : return; } } else { // regular char sc = cc; getCc(); } res = res + (char)sc; len = len + 1; } if (cc != '"') return; getCc(); sym = SYMString; } else { // cc was an invalid char. Get next cc or we will loop forever. getCc(); } } void KeytabReader::ReportToken() // diagnostic { printf("sym(%d): ",slinno); switch(sym) { case SYMEol : printf("End of line"); break; case SYMEof : printf("End of file"); break; case SYMName : printf("Name: %s",res.latin1()); break; case SYMOpr : printf("Opr : %s",res.latin1()); break; case SYMString : printf("String len %d,%d ",res.length(),len); for (unsigned i = 0; i < res.length(); i++) printf(" %02x(%c)",res.latin1()[i],res.latin1()[i]>=' '?res.latin1()[i]:'?'); break; } printf("\n"); } void KeytabReader::ReportError(const char* msg) // diagnostic { fprintf(stderr,"%s(%d,%d):error: %s.\n",path.ascii(),slinno,scolno,msg); } // local symbol tables --------------------------------------------------------------------- class KeyTransSymbols { public: KeyTransSymbols(); protected: void defOprSyms(); void defModSyms(); void defKeySyms(); void defKeySym(const char* key, int val); void defOprSym(const char* key, int val); void defModSym(const char* key, int val); public: TQDict keysyms; TQDict modsyms; TQDict oprsyms; }; static KeyTransSymbols * syms = 0L; // parser ---------------------------------------------------------------------------------- /* Syntax - Line :: [KeyName { ("+" | "-") ModeName } ":" (String|CommandName)] "\n" - Comment :: '#' (any but \n)* */ void KeyTrans::readConfig() { if (m_fileRead) return; m_fileRead=true; TQIODevice* buf(0); if (m_path=="[buildin]") { TQCString txt = #include "default.keytab.h" ; buf=TQT_TQIODEVICE(new TQBuffer(txt)); } else { buf=TQT_TQIODEVICE(new TQFile(m_path)); }; KeytabReader ktr(m_path,*buf); ktr.parseTo(this); delete buf; } #define assertSyntax(Cond,Message) if (!(Cond)) { ReportError(Message); goto ERROR; } void KeytabReader::parseTo(KeyTrans* kt) { // Opening sequence buf->open(IO_ReadOnly); getCc(); linno = 1; colno = 1; getSymbol(); Loop: // syntax: ["key" KeyName { ("+" | "-") ModeName } ":" String/CommandName] ["#" Comment] if (sym == SYMName && !strcmp(res.latin1(),"keyboard")) { getSymbol(); assertSyntax(sym == SYMString, "Header expected") kt->m_hdr = i18n(res.latin1()); getSymbol(); assertSyntax(sym == SYMEol, "Text unexpected") getSymbol(); // eoln goto Loop; } if (sym == SYMName && !strcmp(res.latin1(),"key")) { //printf("line %3d: ",startofsym); getSymbol(); assertSyntax(sym == SYMName, "Name expected") assertSyntax(syms->keysyms[res], "Unknown key name") ptrdiff_t key = (ptrdiff_t)(syms->keysyms[res]) - 1; //printf(" key %s (%04x)",res.latin1(),(int)syms->keysyms[res]-1); getSymbol(); // + - : int mode = 0; int mask = 0; while (sym == SYMOpr && (!strcmp(res.latin1(),"+") || !strcmp(res.latin1(),"-"))) { bool on = !strcmp(res.latin1(),"+"); getSymbol(); // mode name assertSyntax(sym == SYMName, "Name expected") assertSyntax(syms->modsyms[res], "Unknown mode name") ptrdiff_t bits = (ptrdiff_t)(syms->modsyms[res]) - 1; if (mask & (1 << bits)) { fprintf(stderr,"%s(%d,%d): mode name used multiple times.\n",path.ascii(),slinno,scolno); } else { mode |= (on << bits); mask |= (1 << bits); } //printf(", mode %s(%d) %s",res.latin1(),(int)syms->modsyms[res]-1,on?"on":"off"); getSymbol(); } assertSyntax(sym == SYMOpr && !strcmp(res.latin1(),":"), "':' expected") getSymbol(); // string or command assertSyntax(sym == SYMName || sym == SYMString,"Command or string expected") ptrdiff_t cmd = 0; if (sym == SYMName) { assertSyntax(syms->oprsyms[res], "Unknown operator name") cmd = (ptrdiff_t)(syms->oprsyms[res]) - 1; //printf(": do %s(%d)",res.latin1(),(int)syms->oprsyms[res]-1); } if (sym == SYMString) { cmd = CMD_send; //printf(": send"); //for (unsigned i = 0; i < res.length(); i++) //printf(" %02x(%c)",res.latin1()[i],res.latin1()[i]>=' '?res.latin1()[i]:'?'); } //printf(". summary %04x,%02x,%02x,%d\n",key,mode,mask,cmd); KeyTrans::KeyEntry* ke = kt->addEntry(slinno,key,mode,mask,cmd,res); if (ke) { fprintf(stderr,"%s(%d): keystroke already assigned in line %d.\n",path.ascii(),slinno,ke->ref); } getSymbol(); assertSyntax(sym == SYMEol, "Unexpected text") goto Loop; } if (sym == SYMEol) { getSymbol(); goto Loop; } assertSyntax(sym == SYMEof, "Undecodable Line") buf->close(); return; ERROR: while (sym != SYMEol && sym != SYMEof) getSymbol(); // eoln goto Loop; } // local symbol tables --------------------------------------------------------------------- // material needed for parsing the config file. // This is incomplete work. void KeyTransSymbols::defKeySym(const char* key, int val) { keysyms.insert(key,(TQObject*)(val+1)); } void KeyTransSymbols::defOprSym(const char* key, int val) { oprsyms.insert(key,(TQObject*)(val+1)); } void KeyTransSymbols::defModSym(const char* key, int val) { modsyms.insert(key,(TQObject*)(val+1)); } void KeyTransSymbols::defOprSyms() { // Modifier defOprSym("scrollLineUp", CMD_scrollLineUp ); defOprSym("scrollLineDown",CMD_scrollLineDown); defOprSym("scrollPageUp", CMD_scrollPageUp ); defOprSym("scrollPageDown",CMD_scrollPageDown); defOprSym("scrollLock", CMD_scrollLock); } void KeyTransSymbols::defModSyms() { // Modifier defModSym("Shift", BITS_Shift ); defModSym("Control", BITS_Control ); defModSym("Alt", BITS_Alt ); // Modes defModSym("BsHack", BITS_BsHack ); // deprecated defModSym("Ansi", BITS_Ansi ); defModSym("NewLine", BITS_NewLine ); defModSym("AppCuKeys", BITS_AppCuKeys ); defModSym("AppScreen", BITS_AppScreen ); // Special (Any Modifier) defModSym("AnyMod", BITS_AnyMod ); } void KeyTransSymbols::defKeySyms() { // Grey keys defKeySym("Escape", Qt::Key_Escape ); defKeySym("Tab", Qt::Key_Tab ); defKeySym("Backtab", Qt::Key_Backtab ); defKeySym("Backspace", Qt::Key_Backspace ); defKeySym("Return", Qt::Key_Return ); defKeySym("Enter", Qt::Key_Enter ); defKeySym("Insert", Qt::Key_Insert ); defKeySym("Delete", Qt::Key_Delete ); defKeySym("Pause", Qt::Key_Pause ); defKeySym("Print", Qt::Key_Print ); defKeySym("SysReq", Qt::Key_SysReq ); defKeySym("Home", Qt::Key_Home ); defKeySym("End", Qt::Key_End ); defKeySym("Left", Qt::Key_Left ); defKeySym("Up", Qt::Key_Up ); defKeySym("Right", Qt::Key_Right ); defKeySym("Down", Qt::Key_Down ); defKeySym("Prior", TQt::Key_Prior ); defKeySym("Next", TQt::Key_Next ); defKeySym("Shift", Qt::Key_Shift ); defKeySym("Control", Qt::Key_Control ); defKeySym("Meta", Qt::Key_Meta ); defKeySym("Alt", Qt::Key_Alt ); defKeySym("CapsLock", Qt::Key_CapsLock ); defKeySym("NumLock", Qt::Key_NumLock ); defKeySym("ScrollLock", Qt::Key_ScrollLock ); defKeySym("F1", Qt::Key_F1 ); defKeySym("F2", Qt::Key_F2 ); defKeySym("F3", Qt::Key_F3 ); defKeySym("F4", Qt::Key_F4 ); defKeySym("F5", Qt::Key_F5 ); defKeySym("F6", Qt::Key_F6 ); defKeySym("F7", Qt::Key_F7 ); defKeySym("F8", Qt::Key_F8 ); defKeySym("F9", Qt::Key_F9 ); defKeySym("F10", Qt::Key_F10 ); defKeySym("F11", Qt::Key_F11 ); defKeySym("F12", Qt::Key_F12 ); defKeySym("F13", Qt::Key_F13 ); defKeySym("F14", Qt::Key_F14 ); defKeySym("F15", Qt::Key_F15 ); defKeySym("F16", Qt::Key_F16 ); defKeySym("F17", Qt::Key_F17 ); defKeySym("F18", Qt::Key_F18 ); defKeySym("F19", Qt::Key_F19 ); defKeySym("F20", Qt::Key_F20 ); defKeySym("F21", Qt::Key_F21 ); defKeySym("F22", Qt::Key_F22 ); defKeySym("F23", Qt::Key_F23 ); defKeySym("F24", Qt::Key_F24 ); defKeySym("F25", Qt::Key_F25 ); defKeySym("F26", Qt::Key_F26 ); defKeySym("F27", Qt::Key_F27 ); defKeySym("F28", Qt::Key_F28 ); defKeySym("F29", Qt::Key_F29 ); defKeySym("F30", Qt::Key_F30 ); defKeySym("F31", Qt::Key_F31 ); defKeySym("F32", Qt::Key_F32 ); defKeySym("F33", Qt::Key_F33 ); defKeySym("F34", Qt::Key_F34 ); defKeySym("F35", Qt::Key_F35 ); defKeySym("Super_L", Qt::Key_Super_L ); defKeySym("Super_R", Qt::Key_Super_R ); defKeySym("Menu", Qt::Key_Menu ); defKeySym("Hyper_L", Qt::Key_Hyper_L ); defKeySym("Hyper_R", Qt::Key_Hyper_R ); // Regular keys defKeySym("Space", Qt::Key_Space ); defKeySym("Exclam", Qt::Key_Exclam ); defKeySym("QuoteDbl", Qt::Key_QuoteDbl ); defKeySym("NumberSign", Qt::Key_NumberSign ); defKeySym("Dollar", Qt::Key_Dollar ); defKeySym("Percent", Qt::Key_Percent ); defKeySym("Ampersand", Qt::Key_Ampersand ); defKeySym("Apostrophe", Qt::Key_Apostrophe ); defKeySym("ParenLeft", Qt::Key_ParenLeft ); defKeySym("ParenRight", Qt::Key_ParenRight ); defKeySym("Asterisk", Qt::Key_Asterisk ); defKeySym("Plus", Qt::Key_Plus ); defKeySym("Comma", Qt::Key_Comma ); defKeySym("Minus", Qt::Key_Minus ); defKeySym("Period", Qt::Key_Period ); defKeySym("Slash", Qt::Key_Slash ); defKeySym("0", Qt::Key_0 ); defKeySym("1", Qt::Key_1 ); defKeySym("2", Qt::Key_2 ); defKeySym("3", Qt::Key_3 ); defKeySym("4", Qt::Key_4 ); defKeySym("5", Qt::Key_5 ); defKeySym("6", Qt::Key_6 ); defKeySym("7", Qt::Key_7 ); defKeySym("8", Qt::Key_8 ); defKeySym("9", Qt::Key_9 ); defKeySym("Colon", Qt::Key_Colon ); defKeySym("Semicolon", Qt::Key_Semicolon ); defKeySym("Less", Qt::Key_Less ); defKeySym("Equal", Qt::Key_Equal ); defKeySym("Greater", Qt::Key_Greater ); defKeySym("Question", Qt::Key_Question ); defKeySym("At", Qt::Key_At ); defKeySym("A", Qt::Key_A ); defKeySym("B", Qt::Key_B ); defKeySym("C", Qt::Key_C ); defKeySym("D", Qt::Key_D ); defKeySym("E", Qt::Key_E ); defKeySym("F", Qt::Key_F ); defKeySym("G", Qt::Key_G ); defKeySym("H", Qt::Key_H ); defKeySym("I", Qt::Key_I ); defKeySym("J", Qt::Key_J ); defKeySym("K", Qt::Key_K ); defKeySym("L", Qt::Key_L ); defKeySym("M", Qt::Key_M ); defKeySym("N", Qt::Key_N ); defKeySym("O", Qt::Key_O ); defKeySym("P", Qt::Key_P ); defKeySym("Q", Qt::Key_Q ); defKeySym("R", Qt::Key_R ); defKeySym("S", Qt::Key_S ); defKeySym("T", Qt::Key_T ); defKeySym("U", Qt::Key_U ); defKeySym("V", Qt::Key_V ); defKeySym("W", Qt::Key_W ); defKeySym("X", Qt::Key_X ); defKeySym("Y", Qt::Key_Y ); defKeySym("Z", Qt::Key_Z ); defKeySym("BracketLeft", Qt::Key_BracketLeft ); defKeySym("Backslash", Qt::Key_Backslash ); defKeySym("BracketRight", Qt::Key_BracketRight); defKeySym("AsciiCircum", Qt::Key_AsciiCircum ); defKeySym("Underscore", Qt::Key_Underscore ); defKeySym("QuoteLeft", Qt::Key_QuoteLeft ); defKeySym("BraceLeft", Qt::Key_BraceLeft ); defKeySym("Bar", Qt::Key_Bar ); defKeySym("BraceRight", Qt::Key_BraceRight ); defKeySym("AsciiTilde", Qt::Key_AsciiTilde ); } KeyTransSymbols::KeyTransSymbols() { defModSyms(); defOprSyms(); defKeySyms(); } // Global material ----------------------------------------------------------- static int keytab_serial = 0; //FIXME: remove,localize static TQIntDict * numb2keymap = 0L; KeyTrans* KeyTrans::find(int numb) { KeyTrans* res = numb2keymap->tqfind(numb); return res ? res : numb2keymap->tqfind(0); } KeyTrans* KeyTrans::find(const TQString &id) { TQIntDictIterator it(*numb2keymap); while(it.current()) { if (it.current()->id() == id) return it.current(); ++it; } return numb2keymap->tqfind(0); } int KeyTrans::count() { return numb2keymap->count(); } void KeyTrans::addKeyTrans() { m_numb = keytab_serial ++; numb2keymap->insert(m_numb,this); } void KeyTrans::loadAll() { if (!numb2keymap) numb2keymap = new TQIntDict; else { // Needed for konsole_part. numb2keymap->clear(); keytab_serial = 0; } if (!syms) syms = new KeyTransSymbols; //defaultKeyTrans()->addKeyTrans(); KeyTrans* sc = new KeyTrans("[buildin]"); sc->addKeyTrans(); TQStringList lst = KGlobal::dirs()->findAllResources("data", "konsole/*.keytab"); for(TQStringList::Iterator it = lst.begin(); it != lst.end(); ++it ) { //TQFile file(TQFile::encodeName(*it)); sc = new KeyTrans(TQFile::encodeName(*it)); //KeyTrans* sc = KeyTrans::fromDevice(TQFile::encodeName(*it),file); if (sc) sc->addKeyTrans(); } } // Debugging material ----------------------------------------------------------- /* void TestTokenizer(TQBuffer &buf) { // opening sequence buf.open(IO_ReadOnly); cc = buf.getch(); lineno = 1; // Test tokenizer while (getSymbol(buf)) ReportToken(); buf.close(); } void test() { // Opening sequence TQCString txt = #include "default.keytab.h" ; TQBuffer buf(txt); if (0) TestTokenizer(buf); if (1) { KeyTrans kt; kt.scanTable(buf); } } */