/***************************************************************************** Gestionnaire de terminal Unix: keyboard.c (c) Pierre Adriaans 1994 Prevu pour fonctionner soit par sigaction() soit par signal(). Deux implementations des lectures de touches speciales, choix a la compilation par #define FULL_NON_BLOCK: si defini: dans ReadKbd(), pour la lecture des touches speciale, le clavier est passe en mode non-bloquant des reception d'un ESC: le reste est lu comme un KeyPressed(). si non defini: ce qui suit Esc est lu dans une boucle de lectures bloquantes jusqu'a obtenir une sequence connue, le tout protege par un reveil *****************************************************************************/ #define _KEYBOARD_SOURCE_CODE_ #include "screen.h" #include "keyboard.h" #define FULL_NON_BLOCK /* Pour le fichier de mapping */ #define _UNSUPPORTED_ "Unsupported Key" #define _TEXT_TAIL_ ':' /* Memorisation de l'etat de depart du terminal */ struct termios _InitialState_; int _fcntl_flags_; jmp_buf _uk_buf_, /* Reveil de lecture des touches speciales */ _nb_buf_; /* Pour le test de la methode d'I/O non bloquante a l'initialisation de l'unite */ int _BackSpace_, /* Code renvoye par la touche Backspace */ _KbType_; /* Type de clavier */ /* Structure contenant les entrees claviers possibles: a adapter suivant les terminaux et le programme appelant */ #define _NBRE_KEYS_ 40 /* Table des sequences des touches speciales */ KbEntry_t _KbCodes_[_NBRE_KEYS_]; /* Macro d'initialisation de cette table */ #define INIT_KEY(Code,Text,Tcap) \ { \ strcpy(_KbCodes_[TableIndex].Texte,Text); \ _KbCodes_[TableIndex].SymbolicCode = Code; \ strcpy(_KbCodes_[TableIndex].TCapSeq,Tcap); \ TableIndex++; \ } /* Pointeur de tete de la file de touches */ KeyQueue_t *_KeyQueue_ = (KeyQueue_t *)NULL; /* Pour le mapping de /etc/termcap */ char *pszTermcapBuffer; struct TermcapTemp_s { char Buffer[255]; struct TermcapTemp_s *Next; }; /* Temoin d'appel free() */ int _InFree_; char *getenv(const char *); /***************************************************************************** _terminate_() ------------------------------------------------------------------------------ Input: le signal Process: fct de detournement d'un kill pdt une entree: on retablit le terminal a partir de l'etat sauve a l'initialisation de l'unite. Output: / *****************************************************************************/ void _terminate_(int Num) { RestoreKeyboard(); if(GetScreenType() == VT_SCREEN) SWITCH_TEXT; SetAtt(PackAtt(NORMAL)); MoveCurs(24,1); if(Num == SIGME) { printf("\nMemory allocation error: the malloc() system call failed. Exiting.\n"); fflush(stdout); exit(SIGME); } printf("\nSignal "); switch(Num) { case SIGINT: printf("SIGINT"); break; case SIGTERM: printf("SIGTERM"); break; case SIGQUIT: printf("SIGQUIT"); break; case SIGILL: printf("SIGILL"); break; case SIGTRAP: printf("SIGTRAP"); break; case SIGABRT: printf("SIGABRT"); break; case SIGFPE: printf("SIGFPE"); break; case SIGBUS: printf("SIGBUS"); break; case SIGSEGV: printf("SIGSEGV"); break; case SIGPIPE: printf("SIGPIPE"); break; case SIGCHLD: printf("SIGCHLD"); break; } if(_InFree_) printf(" (%d) received on a free() system call. Exiting.\n",Num); else printf(" (%d) received. Exiting.\n",Num); fflush(stdout); exit(Num); } /***************************************************************************** GetKeyQueueAdress() ------------------------------------------------------------------------------ Input: / Output: l'adresse du pointeur de tete de la file de touches. *****************************************************************************/ KeyQueue_t *GetKeyQueueAdress(void) { return(_KeyQueue_); } /***************************************************************************** KeyInQueue() ------------------------------------------------------------------------------ Input: un code de touche (symbolique ou ASCII) Process: enfilage en queue de la touche Output: / *****************************************************************************/ int KeyInQueue(int Code) { KeyQueue_t *Cour,*Prec,*Temp = (KeyQueue_t *)NULL; Temp = (KeyQueue_t *)malloc(sizeof(KeyQueue_t)); if(Temp == (KeyQueue_t *)NULL) raise(SIGME); Temp->Code = Code; Temp->Suivant = (KeyQueue_t *)NULL; if(_KeyQueue_ == (KeyQueue_t *)NULL) _KeyQueue_ = Temp; else { Cour = _KeyQueue_; Prec = (KeyQueue_t *)NULL; while(Cour != (KeyQueue_t *)NULL) { Prec = Cour; Cour = Cour->Suivant; } Prec->Suivant = Temp; } return(0); } /***************************************************************************** OldestKey() ------------------------------------------------------------------------------ Input: / Process: renvoie le code de la plus ancienne touche enfilee dans la queue, et libere cette entree. *****************************************************************************/ int OldestKey(void) { int Code; KeyQueue_t *Temp; if(_KeyQueue_ != (KeyQueue_t *)NULL) { Code = _KeyQueue_->Code; Temp = _KeyQueue_; _KeyQueue_ = _KeyQueue_->Suivant; free(Temp); return(Code); } else return(NO_KEY_AVAIL); } /***************************************************************************** FreeKeyQueue() ------------------------------------------------------------------------------ Input,Output: / Process: liberation de la totalite de la file de touches. *****************************************************************************/ void FreeKeyQueue(void) { KeyQueue_t *Temp; Temp = _KeyQueue_; while(Temp != (KeyQueue_t *)NULL) { _KeyQueue_ = _KeyQueue_->Suivant; free(Temp); Temp = _KeyQueue_; } } /***************************************************************************** GetNbreKeys() ------------------------------------------------------------------------------ Renvoie le #define _NBRE_KEYS_ *****************************************************************************/ int GetNbreKeys(void) { return(_NBRE_KEYS_); } /***************************************************************************** GetKbCodesAdress() ------------------------------------------------------------------------------ Renvoie l'adresse de la structure contenant les entrees clavier connues. *****************************************************************************/ struct KbEntry_s *GetKbCodesAdress(void) { return(_KbCodes_); } /***************************************************************************** GetKey() ------------------------------------------------------------------------------ Input: un buffer pour recevoir un code de touche Process: attente de la frappe d'une touche de controle au clavier, recuperation du code et stockage dans le buffer. Le code peut etre de MAX_KEY caracteres maximum. *****************************************************************************/ void GetKey(char *Buf) { int i; struct termios Avant,Apres; #ifdef SIGACT struct sigaction IntNew,IntOld, TermNew,TermOld, QuitNew,QuitOld; IntNew.sa_handler = _terminate_; sigemptyset(&IntNew.sa_mask); IntNew.sa_flags = 0; sigaction(SIGINT,&IntNew,&IntOld); TermNew.sa_handler = _terminate_; sigemptyset(&TermNew.sa_mask); TermNew.sa_flags = 0; sigaction(SIGTERM,&TermNew,&TermOld); QuitNew.sa_handler = _terminate_; sigemptyset(&QuitNew.sa_mask); QuitNew.sa_flags = 0; sigaction(SIGQUIT,&QuitNew,&QuitOld); #else void (*IntFct)(int), (*TermFct)(int), (*QuitFct)(int); IntFct = signal(SIGINT,_terminate_); TermFct = signal(SIGTERM,_terminate_); QuitFct = signal(SIGQUIT,_terminate_); #endif tcgetattr(0, &Avant); memcpy(&Apres,&Avant,sizeof(struct termios)); Apres.c_iflag &= ~(INLCR | ICRNL | ISTRIP | BRKINT | IXON); Apres.c_oflag &= ~OPOST; Apres.c_lflag &= ~ICANON; Apres.c_cc[VMIN] = MAX_KEY; Apres.c_cc[VTIME] = 1; tcsetattr(0, TCSANOW, &Apres); for(i=0;i CR */ ICRNL | /* Pas de conversion CR -> LF */ ISTRIP | /* Pas de conversion 8 bits -> 7 bits */ BRKINT | /* Pas de caractere de controle */ IXON); /* Pas de controle de flux par Ctrl-S */ Apres.c_oflag &= ~OPOST; /* Pas de traitement en sortie ecran */ Apres.c_lflag &= ~ICANON; /* Pas de return attendu */ Apres.c_cc[VMIN] = 1; /* Lire un seul caractere */ Apres.c_cc[VSTOP] = 0; /* Pour empecher Ctrl-S d'envoyer un Stop */ Apres.c_cc[VSTART] = 0; /* Si Ctrl-S est deconnecte, Ctrl-Q ne */ /* sert plus a rien: on le vire */ tcsetattr(0, TCSANOW, &Apres); SetNonBlock(); CodeRead = read(0,Temp,1); rc = TestRead(CodeRead); if(rc == 1) /* Il y a qque chose dans le clavier */ { if(Temp[0] != ESC) { /* Touche classique: */ if(Temp[0] == (char)_BackSpace_) Code = BACKSP; else Code = (int)Temp[0]; } else { /* Touche de controle ou ESC sur PC: passage en non bloquant et lecture de la suite si il y a. */ offset = 1; while(Code == -1) { CodeRead = read(0,Temp+offset,1); rc = TestRead(CodeRead); if(rc == 1) offset++; else { /* Plus rien a lire: recherche dans la table des sequences */ for(i=0;i<_NBRE_KEYS_ && Code == -1;i++) if(strcmp(Temp,_KbCodes_[i].Sequence) == 0) Code = _KbCodes_[i].SymbolicCode; if(Code == -1) Code = K_UNKNOWN; } } } KeyInQueue(Code); rc = 1; } SetBlock(); tcsetattr(0, TCSANOW, &Avant); #ifdef SIGACT sigaction(SIGINT,&IntOld,&IntNew); sigaction(SIGTERM,&TermOld,&TermNew); sigaction(SIGQUIT,&QuitOld,&QuitNew); #else signal(SIGINT,IntFct); signal(SIGTERM,TermFct); signal(SIGQUIT,QuitFct); #endif return(rc); } /***************************************************************************** ReadKbd() ------------------------------------------------------------------------------ Input: / Process: lecture synchrone du clavier. Output: si la queue contient qque chose: le code de la touche la plus ancienne dans la queue, sinon, la touche entree *****************************************************************************/ int ReadKbd(void) { #ifdef FULL_NON_BLOCK /*---------------------------------------------------------------------------- Premiere version de la fonction: premiere touche en lecture bloquante, puis, passage du clavier en non bloquant pour la suite si ESC est le premier code recu. Fonctionne sur Linux et SCO. ----------------------------------------------------------------------------*/ char Temp[MAX_KEY]; int i,offset,rc,Code = -1,Fini = 0,CodeRead; unsigned int Remains; struct termios Avant,Apres; #ifdef SIGACT struct sigaction IntNew,IntOld, TermNew,TermOld, QuitNew,QuitOld; #else void (*IntFct)(int), (*TermFct)(int), (*QuitFct)(int); #endif if(_KeyQueue_ != (KeyQueue_t *)NULL) return(OldestKey()); else { #ifdef SIGACT IntNew.sa_handler = _terminate_; sigemptyset(&IntNew.sa_mask); IntNew.sa_flags = 0; sigaction(SIGINT,&IntNew,&IntOld); TermNew.sa_handler = _terminate_; sigemptyset(&TermNew.sa_mask); TermNew.sa_flags = 0; sigaction(SIGTERM,&TermNew,&TermOld); QuitNew.sa_handler = _terminate_; sigemptyset(&QuitNew.sa_mask); QuitNew.sa_flags = 0; sigaction(SIGQUIT,&QuitNew,&QuitOld); #else IntFct = signal(SIGINT,_terminate_); TermFct = signal(SIGTERM,_terminate_); QuitFct = signal(SIGQUIT,_terminate_); #endif tcgetattr(0, &Avant); for(i=0;i CR */ ICRNL | /* Pas de conversion CR -> LF */ ISTRIP | /* Pas de conversion 8 bits -> 7 bits */ BRKINT | /* Pas de caractere de controle */ IXON); /* Pas de controle de flux par Ctrl-S */ Apres.c_oflag &= ~OPOST; /* Pas de traitement en sortie ecran */ Apres.c_lflag &= ~ICANON; /* Pas de return attendu */ Apres.c_cc[VMIN] = 1; /* Lire un seul caractere */ Apres.c_cc[VSTOP] = 0; /* Pour empecher Ctrl-S d'envoyer un Stop */ Apres.c_cc[VSTART] = 0; /* Si Ctrl-S est deconnecte, Ctrl-Q ne */ /* sert plus a rien: on le vire */ tcsetattr(0, TCSANOW, &Apres); while((read(0,Temp,1) == -1) && (errno == EINTR)); if(Temp[0] != ESC) { /* Touche classique: */ if(Temp[0] == (char)_BackSpace_) Code = BACKSP; else Code = (int)Temp[0]; } else { /* Touche de controle ou ESC sur PC: passage en non bloquant et lecture de la suite si il y a. */ SetNonBlock(); offset = 1; while(Code == -1) { CodeRead = read(0,Temp+offset,1); rc = TestRead(CodeRead); if(rc == 1) offset++; else { for(i=0;i<_NBRE_KEYS_ && Code == -1;i++) if(strcmp(Temp,_KbCodes_[i].Sequence) == 0) Code = _KbCodes_[i].SymbolicCode; if(Code == -1) Code = K_UNKNOWN; } } SetBlock(); } tcsetattr(0, TCSANOW, &Avant); #ifdef SIGACT sigaction(SIGINT,&IntOld,&IntNew); sigaction(SIGTERM,&TermOld,&TermNew); sigaction(SIGQUIT,&QuitOld,&QuitNew); #else signal(SIGINT,IntFct); signal(SIGTERM,TermFct); signal(SIGQUIT,QuitFct); #endif return(Code); } /*--------------------------------------------------------------------------*/ #else /*---------------------------------------------------------------------------- Seconde version: tout en lecture bloquante avec reveil. Fonctionne sur ULTRIX, OSF/1, SCO et Solaris. ----------------------------------------------------------------------------*/ char Temp[MAX_KEY]; int i,offset,rc,Code = -1,Fini = 0,CodeRead; unsigned int Remains; struct termios Avant,Apres; #ifdef SIGACT struct sigaction IntNew,IntOld, TermNew,TermOld, QuitNew,QuitOld, AlrmNew,AlrmOld; #else void (*IntFct)(int), (*TermFct)(int), (*QuitFct)(int), (*AlrmFct)(int); #endif if(_KeyQueue_ != (KeyQueue_t *)NULL) return(OldestKey()); else { #ifdef SIGACT IntNew.sa_handler = _terminate_; sigemptyset(&IntNew.sa_mask); IntNew.sa_flags = 0; sigaction(SIGINT,&IntNew,&IntOld); TermNew.sa_handler = _terminate_; sigemptyset(&TermNew.sa_mask); TermNew.sa_flags = 0; sigaction(SIGTERM,&TermNew,&TermOld); QuitNew.sa_handler = _terminate_; sigemptyset(&QuitNew.sa_mask); QuitNew.sa_flags = 0; sigaction(SIGQUIT,&QuitNew,&QuitOld); #else IntFct = signal(SIGINT,_terminate_); TermFct = signal(SIGTERM,_terminate_); QuitFct = signal(SIGQUIT,_terminate_); #endif tcgetattr(0, &Avant); for(i=0;i CR */ ICRNL | /* Pas de conversion CR -> LF */ ISTRIP | /* Pas de conversion 8 bits -> 7 bits */ BRKINT | /* Pas de caractere de controle */ IXON); /* Pas de controle de flux par Ctrl-S */ Apres.c_oflag &= ~OPOST; /* Pas de traitement en sortie ecran */ Apres.c_lflag &= ~ICANON; /* Pas de return attendu */ Apres.c_cc[VMIN] = 1; /* Lire un seul caractere */ Apres.c_cc[VSTOP] = 0; /* Pour empecher Ctrl-S d'envoyer un Stop */ Apres.c_cc[VSTART] = 0; /* Si Ctrl-S est deconnecte, Ctrl-Q ne */ /* sert plus a rien: on le vire */ tcsetattr(0, TCSANOW, &Apres); while((read(0,Temp,1) == -1) && (errno == EINTR)); if(Temp[0] != ESC) { /* Touche classique: */ if(Temp[0] == (char)_BackSpace_) Code = BACKSP; else Code = (int)Temp[0]; } else { /* Touche de controle ou ESC sur PC: boucle de lecture caractere par caractere jusqu'a obtenir une sequence connue, le tout protege par un reveil d'une seconde */ #ifdef SIGACT AlrmNew.sa_handler = UnknownKey; sigemptyset(&AlrmNew.sa_mask); AlrmNew.sa_flags = 0; sigaction(SIGALRM,&AlrmNew,&AlrmOld); #else AlrmFct = signal(SIGALRM,UnknownKey); #endif rc = setjmp(_uk_buf_); if(rc == 0) { Remains = alarm(1); offset = 1; while(Code == -1) { read(0,Temp+offset,1); /* Si c'est un deuxieme ESC, ca veut dire qu'on massacre cette touche sur un PC: renvoyer directement ESC */ if(Temp[1] == ESC) Code = ESC; offset++; for(i=0;i<_NBRE_KEYS_ && Code == -1;i++) if(strcmp(Temp,_KbCodes_[i].Sequence) == 0) Code = _KbCodes_[i].SymbolicCode; if(Code != -1) /* Touche reconnue: annuler l'alarm et retablir l'ancienne */ { #ifdef SIGACT sigaction(SIGALRM,&AlrmOld,&AlrmNew); if(AlrmOld.sa_handler != SIG_IGN) alarm(Remains); #else signal(SIGALRM,AlrmFct); if(AlrmFct != SIG_IGN) alarm(Remains); #endif } } } else { #ifdef SIGACT sigaction(SIGALRM,&AlrmOld,&AlrmNew); if(AlrmOld.sa_handler != SIG_IGN) alarm(Remains); #else signal(SIGALRM,AlrmFct); if(AlrmFct != SIG_IGN) alarm(Remains); #endif if(Temp[1] == 0) Code = ESC; /* ESC sur PC SCO */ else Code = K_UNKNOWN; /* Touche non geree */ } } tcsetattr(0, TCSANOW, &Avant); #ifdef SIGACT sigaction(SIGINT,&IntOld,&IntNew); sigaction(SIGTERM,&TermOld,&TermNew); sigaction(SIGQUIT,&QuitOld,&QuitNew); #else signal(SIGINT,IntFct); signal(SIGTERM,TermFct); signal(SIGQUIT,QuitFct); #endif return(Code); } #endif } /***************************************************************************** Getch() ------------------------------------------------------------------------------ Input: / Process: entree brute d'un caractere sans return ni echo Output: le code ASCII du caractere *****************************************************************************/ int Getch(void) { char Temp; struct termios Avant,Apres; #ifdef SIGACT struct sigaction IntNew,IntOld, TermNew,TermOld, QuitNew,QuitOld; IntNew.sa_handler = _terminate_; sigemptyset(&IntNew.sa_mask); IntNew.sa_flags = 0; sigaction(SIGINT,&IntNew,&IntOld); TermNew.sa_handler = _terminate_; sigemptyset(&TermNew.sa_mask); TermNew.sa_flags = 0; sigaction(SIGTERM,&TermNew,&TermOld); QuitNew.sa_handler = _terminate_; sigemptyset(&QuitNew.sa_mask); QuitNew.sa_flags = 0; sigaction(SIGQUIT,&QuitNew,&QuitOld); #else void (*IntFct)(int), (*TermFct)(int), (*QuitFct)(int); IntFct = signal(SIGINT,_terminate_); TermFct = signal(SIGTERM,_terminate_); QuitFct = signal(SIGQUIT,_terminate_); #endif tcgetattr(0, &Avant); memcpy(&Apres,&Avant,sizeof(struct termios)); Apres.c_iflag &= ~(INLCR | ICRNL | ISTRIP | IXON | BRKINT); Apres.c_oflag &= ~OPOST; Apres.c_lflag &= ~ICANON; Apres.c_cc[VMIN] = 1; Apres.c_cc[VSTOP] = 0; Apres.c_cc[VSTART] = 0; tcsetattr(0, TCSANOW, &Apres); while((read(0,&Temp,1) == -1) && (errno == EINTR)); tcsetattr(0, TCSANOW, &Avant); #ifdef SIGACT sigaction(SIGINT,&IntOld,&IntNew); sigaction(SIGTERM,&TermOld,&TermNew); sigaction(SIGQUIT,&QuitOld,&QuitNew); #else signal(SIGINT,IntFct); signal(SIGTERM,TermFct); signal(SIGQUIT,QuitFct); #endif return((int)Temp); } /***************************************************************************** Initialisation des codes de touches depuis un fichier *****************************************************************************/ /***************************************************************************** SetOneKey() ------------------------------------------------------------------------------ Input: la ligne lue dans le fichier de mapping Process: si la touche est supportee, transfert de la sequence dans le buffer de la touche *****************************************************************************/ void SetOneKey(char *FileBuf) { char KeyName[30]; int i,j; /* Isoler le nom de la touche */ for(i=0;*FileBuf != _TEXT_TAIL_;FileBuf++,i++) KeyName[i] = *FileBuf; KeyName[i] = 0; /* Se positionner sur la sequence */ FileBuf++; while(*FileBuf == ' ') FileBuf++; if(strcmp(FileBuf,_UNSUPPORTED_) != 0) { /* Chercher la cible dans la structure */ for(j=0;(strcmp(_KbCodes_[j].Texte,KeyName) != 0);j++); /* Transferrer la sequence */ i = 0; while(*FileBuf) { _KbCodes_[j].Sequence[i] = *FileBuf; i++; FileBuf++; } } } /***************************************************************************** InitKbFromFile() ------------------------------------------------------------------------------ Input: le nom du fichier de mapping a lire ou NULL. Si NULL, la fonction construit le nom du fichier a partir de la variable d'environnent TERM Process: lecture du fichier et initialisation de la structure _KbCodes_ Output: -1 si fichier innexistant ou erreur de lecture 0 sinon *****************************************************************************/ int InitKbFromFile(char *KeyFile) { FILE *hf; char Buffer[80],*Term,*Home,File[80]; int i,j; Home = getenv("HOME"); Term = getenv("TERM"); /* Si pas de fichier de cles precise, le creer a partir de la variable d'environnement TERM */ if(KeyFile == (char *)NULL) { strcpy(File,Home); if(File[strlen(File) - 1] != '/') strcat(File,"/"); strcat(File,Term); strcat(File,".keys"); } else strcpy(File,KeyFile); /* Mise a 0 des sequences */ for(i=0;i<_NBRE_KEYS_;i++) for(j=0;j"); else if(isdisplay(Buffer[i])) printf("%c",Buffer[i]); else printf("%d",Buffer[i]); else printf(_UNSUPPORTED_); printf("\n"); } /***************************************************************************** InitKbFromKeyboard() ------------------------------------------------------------------------------ Input: le nom du fichier de mapping a creer ou NULL. Si NULL, le nom du fichier est construit par la fonction Process: demande des codes au clavier et creation du fichier de mapping Output: / *****************************************************************************/ void InitKbFromKeyboard(char* KeyFile) { FILE *hf; char *Term,*Home,File[80]; int i,j; char Tail[2],D[15]; Home = getenv("HOME"); Term = getenv("TERM"); /* Si pas de fichier de cles precise, le creer a partir de la variable d'environnement TERM */ if(KeyFile == (char *)NULL) { strcpy(File,Home); if(File[strlen(File) - 1] != '/') strcat(File,"/"); strcat(File,Term); strcat(File,".keys"); } else strcpy(File,KeyFile); Tail[0] = _TEXT_TAIL_; Tail[1] = 0; /* Mise a 0 des sequences */ for(i=0;i<_NBRE_KEYS_;i++) for(j=0;j key to begin..."); fflush(stdout); fgets(D, sizeof(D), stdin); printf("\n\n"); printf("Please press ONCE the requested key\n"); printf("or press if the key doesn't exist on this terminal...\n"); printf("(Ctrl-C to cancel)\n\n"); /* Traitement particulier du backspace */ printf("BackSpace: "); fflush(stdout); _BackSpace_ = Getch(); printf(" %d\n",_BackSpace_); fprintf(hf,"Backspace"); fprintf(hf,"%s",Tail); fprintf(hf," %c\n",_BackSpace_); /* Traitement des autres touches */ for(i=0;i<_NBRE_KEYS_;i++) { GetOneKey(_KbCodes_[i].Texte,_KbCodes_[i].Sequence); fprintf(hf,"%s%s",_KbCodes_[i].Texte,Tail); for(j=0;j<(25-strlen(_KbCodes_[i].Texte));j++) fprintf(hf," "); if(_KbCodes_[i].Sequence[0] == CR) { fprintf(hf,_UNSUPPORTED_); fprintf(hf,"\n"); } else fprintf(hf,"%s\n",(_KbCodes_[i].Sequence)/* +1 */); } fclose(hf); SetKeyboard(); printf("\nDone.\n"); printf("To remap this terminal's special keys, \nerase the file %s", File); printf(" and restart the program.\n\n"); printf("[Press Enter to go on...]"); fflush(stdout); fgets(D, sizeof(D), stdin); } /***************************************************************************** SetKeyboard() ------------------------------------------------------------------------------ Fonction de configuration du terminal pour utiliser les fonctions de la librairie: couper echo et caracteres de controle pour eviter les interferences. Le reglage se fait a partir des valeurs recuperees a l'initialisation de l'unite, ce qui est plus sur que de les relire a chaque appel. *****************************************************************************/ void SetKeyboard(void) { struct termios Tmp; /* Retablir les valeurs de depart */ RestoreKeyboard(); memcpy(&Tmp,&_InitialState_,sizeof(struct termios)); /* Modifier ce qu'il faut */ Tmp.c_lflag &= ~(ISIG | ECHO); tcsetattr(0, TCSANOW, &Tmp); } /***************************************************************************** RestoreKeyboard() ------------------------------------------------------------------------------ Remettre le terminal a son etat de depart *****************************************************************************/ void RestoreKeyboard(void) { fcntl(0,F_SETFL,_fcntl_flags_); tcsetattr(0, TCSANOW, &_InitialState_); } /***************************************************************************** Initialisation de l'unite: Input: - le nom du fichier de mapping a consulter ou a creer si aucun n'existe. Si NULL est passe, le nom du fichier sera construit a partir de la variable d'environnement $TERM. - le type de gestion des I/O non bloquantes (voir keyboard.h) Process: - Recuperer l'etat du terminal au niveau fcntl() pour permettre le passage en mode non bloquant et le retablissement du mode bloquant dans KeyPressed(). - Memoriser l'etat du terminal au lancement - annuler ISIG et ECHO - tester si les I/O non bloquantes fonctionnent La recuperation de ces configurations permet de ne pas planter l'unite au retour d'un sous-shell qui laisserait le terminal dans un etat douteux: la fonction SetKeyboard() repart des valeurs sauvees a l'initialisation au lieur de les relire a chaque appel. *****************************************************************************/ void _SetSig_(void) { #ifdef SIGACT struct sigaction New,Old; New.sa_handler = SIG_IGN; sigemptyset(&New.sa_mask); New.sa_flags = 0; sigaction(SIGHUP,&New,&Old); sigaction(SIGUSR1,&New,&Old); sigaction(SIGUSR2,&New,&Old); sigaction(SIGCHLD,&New,&Old); sigemptyset(&New.sa_mask); sigaction(SIGALRM,&New,&Old); sigemptyset(&New.sa_mask); New.sa_handler = _terminate_; sigaction(SIGINT,&New,&Old); sigaction(SIGTERM,&New,&Old); sigaction(SIGQUIT,&New,&Old); sigaction(SIGILL,&New,&Old); sigaction(SIGTRAP,&New,&Old); sigaction(SIGIOT,&New,&Old); sigaction(SIGABRT,&New,&Old); sigaction(SIGFPE,&New,&Old); sigaction(SIGBUS,&New,&Old); sigaction(SIGSEGV,&New,&Old); sigaction(SIGPIPE,&New,&Old); #else signal(SIGHUP,SIG_IGN); signal(SIGUSR1,SIG_IGN); signal(SIGUSR2,SIG_IGN); signal(SIGCLD,SIG_IGN); signal(SIGALRM,SIG_IGN); signal(SIGINT,_terminate_); signal(SIGTERM,_terminate_); signal(SIGQUIT,_terminate_); signal(SIGILL,_terminate_); signal(SIGTRAP,_terminate_); signal(SIGIOT,_terminate_); signal(SIGABRT,_terminate_); signal(SIGFPE,_terminate_); signal(SIGBUS,_terminate_); signal(SIGSEGV,_terminate_); signal(SIGPIPE,_terminate_); #endif } void NonBlockErr(int sig) { longjmp(_nb_buf_,1); } /***************************************************************************** Initialisation depuis /etc/termcap *****************************************************************************/ /**************************************************************************** InitTermcapBuffer() ----------------------------------------------------------------------------- Input: la variable d'environnement TERM Process: mapping de l'entree correspondante dans /etc/termcap Output: 0 si ok -1 si probleme ****************************************************************************/ int InitTermcapBuffer(char *Term) { FILE *hf; char szBuffer[255]; struct TermcapTemp_s *Head = (struct TermcapTemp_s *)NULL, *New = (struct TermcapTemp_s *)NULL, *Prec,*Cur; int Size; fprintf(stderr,"Looking for terminal %s\n",Term); pszTermcapBuffer = getenv("TERMCAP"); if(pszTermcapBuffer != (char *)NULL) { if(*pszTermcapBuffer == 0) pszTermcapBuffer = (char *)NULL; else { while(*pszTermcapBuffer != ':') pszTermcapBuffer++; return(0); } } fprintf(stderr,"TERMCAP environment variable not defined: using /etc/termcap\n"); /* Open termcap database */ hf = fopen("/etc/termcap","r"); /* Cannot open */ if(hf == (FILE *)NULL) return(-1); /* Look for the terminal */ while(fgets(szBuffer,255,hf) != (char *)NULL) { szBuffer[strlen(szBuffer) -1] = 0; if(strlen(szBuffer) == 0) continue; if(szBuffer[0] == '#') continue; if(strstr(szBuffer,Term) != (char *)NULL) break; } fprintf(stderr,"Found\n"); /* Chain the strings into the list */ while(fgets(szBuffer,255,hf) != (char *)NULL) { if(strlen(szBuffer) > 0) szBuffer[strlen(szBuffer) -1] = 0; if(strlen(szBuffer) == 0) break; if(szBuffer[0] == '#') continue; New = (struct TermcapTemp_s *)malloc(sizeof(struct TermcapTemp_s)); if(New == (struct TermcapTemp_s *)NULL) raise(SIGME); strcpy(New->Buffer,szBuffer); New->Next = (struct TermcapTemp_s *)NULL; if(Head == (struct TermcapTemp_s *)NULL) Head = New; else { Cur = Head; Prec = (struct TermcapTemp_s *)NULL; while(Cur != (struct TermcapTemp_s *)NULL) { Prec = Cur; Cur = Cur->Next; } Prec->Next = New; } } fclose(hf); /* Compute the size of the search buffer */ Size = 0; New = Head; while(New != (struct TermcapTemp_s *)NULL) { Size += strlen(New->Buffer); New = New->Next; } /* Build the search buffer */ pszTermcapBuffer = (char *)malloc(Size + 1); if(pszTermcapBuffer == (char *)NULL) raise(SIGME); New = Head; strcpy(pszTermcapBuffer,""); while(New != (struct TermcapTemp_s *)NULL) { strcat(pszTermcapBuffer,New->Buffer); New = New->Next; } /* Free the list */ while(Head != (struct TermcapTemp_s *)NULL) { New = Head; Head = Head->Next; free(New); } return(0); } /**************************************************************************** GetSequence() ----------------------------------------------------------------------------- Input: Code: la sequence a rechercher dans le mapping Sequence: la sequence de touche generee Process: recherche du code dans le mapping et initialisation Output: / ****************************************************************************/ void GetSequence(char *Code,char *Sequence) { char *szWander; int i; /* Petite entourloupe pour court-circuiter les fleches depuis termcap */ char KU[4],KD[4],KL[4],KR[4]; strcpy(KU," [A"); KU[0] = 27; strcpy(KD," [B"); KD[0] = 27; strcpy(KR," [C"); KR[0] = 27; strcpy(KL," [D"); KL[0] = 27; if(strcmp(Code,":ku=") == 0) { strcpy(Sequence,KU); return; } if(strcmp(Code,":kd=") == 0) { strcpy(Sequence,KD); return; } if(strcmp(Code,":kr=") == 0) { strcpy(Sequence,KR); return; } if(strcmp(Code,":kl=") == 0) { strcpy(Sequence,KL); return; } szWander = strstr(pszTermcapBuffer,Code); if(szWander == (char *)NULL) return; szWander += strlen(Code); i = 0; while(*szWander != ':') { switch(*szWander) { case '\\': szWander++; switch(*szWander) { case 'E': Sequence[i] = 27; break; } break; default: Sequence[i] = *szWander; break; } szWander++; i++; } Sequence[i] = 0; } /**************************************************************************** ReleaseTermcapBuffer ----------------------------------------------------------------------------- Liberation du buffer de mapping ****************************************************************************/ void ReleaseTermcapBuffer(void) { /*free(pszTermcapBuffer);*/ } /**************************************************************************** InitKeyboard() ----------------------------------------------------------------------------- Initialiation de l'unite ****************************************************************************/ void InitKeyboard(char *KeyFile,int KbType,int MappingType) { unsigned int Remains; int i,j,rc,TableIndex; #ifdef SIGACT struct sigaction New,Old; #else void (*AlrmFct)(int); #endif /* Changer les valeurs de Termcap si besoin est */ TableIndex = 0; INIT_KEY(ESC, "Escape" ,""); INIT_KEY(DELETE, "Delete" ,":kD="); INIT_KEY(K_UP, "Up Arrow" ,":ku="); INIT_KEY(K_DOWN, "Down Arrow" ,":kd="); INIT_KEY(K_LEFT, "Left Arrow" ,":kl="); INIT_KEY(K_RIGHT, "Right Arrow" ,":kr="); INIT_KEY(K_PGUP, "Page Up" ,":kP="); INIT_KEY(K_PGDN, "Page Down" ,":kN="); INIT_KEY(K_INS, "Insert" ,":kI="); INIT_KEY(K_HOME, "Home" ,":kh="); INIT_KEY(K_END, "End" ,":kH="); INIT_KEY(K_F1, "Function Key #1" ,":k1="); INIT_KEY(K_F2, "Function Key #2" ,":k2="); INIT_KEY(K_F3, "Function Key #3" ,":k3="); INIT_KEY(K_F4, "Function Key #4" ,":k4="); INIT_KEY(K_F5, "Function Key #5" ,":k5="); INIT_KEY(K_F6, "Function Key #6" ,":k6="); INIT_KEY(K_F7, "Function Key #7" ,":k7="); INIT_KEY(K_F8, "Function Key #8" ,":k8="); INIT_KEY(K_F9, "Function Key #9" ,":k9="); INIT_KEY(K_F10, "Function Key #10" ,":k0="); INIT_KEY(K_F11, "Function Key #11" ,""); INIT_KEY(K_F12, "Function Key #12" ,""); INIT_KEY(K_F13, "Function Key #13" ,""); INIT_KEY(K_F14, "Function Key #14" ,""); INIT_KEY(K_F15, "Function Key #15" ,""); INIT_KEY(K_F16, "Function Key #16" ,""); INIT_KEY(K_F17, "Function Key #17" ,""); INIT_KEY(K_F18, "Function Key #18" ,""); INIT_KEY(K_F19, "Function Key #19" ,""); INIT_KEY(K_F20, "Function Key #20" ,""); INIT_KEY(K_HELP, "Help Key" ,""); INIT_KEY(K_DO, "Do Key" ,""); INIT_KEY(K_FIND, "Find Key" ,""); INIT_KEY(K_SELECT, "Select Key" ,""); INIT_KEY(K_REMOVE, "Remove Key" ,""); INIT_KEY(K_PF1, "PF1 Key" ,""); INIT_KEY(K_PF2, "PF2 Key" ,""); INIT_KEY(K_PF3, "PF3 Key" ,""); INIT_KEY(K_PF4, "PF4 Key" ,""); fprintf(stderr,"\nInitializing keyboard handling library ("); switch(KbType) { case POSIX_KBD: fprintf(stderr,"POSIX"); break; case SYSTEM_V_KBD: fprintf(stderr,"System V"); break; case BSD_KBD: fprintf(stderr,"BSD"); break; case USR_GROUP_KBD: fprintf(stderr,"/usr/group"); break; } fprintf(stderr," non-blocking I/O)...\n"); _KbType_ = KbType; _InFree_ = 0; fprintf(stderr,"Getting terminal configuration through fcntl().\n"); _fcntl_flags_ = fcntl(0,F_GETFL,0); if(_fcntl_flags_ == -1) { fprintf(stderr,"Unable to get the terminal configuration through fcntl.\n"); exit(1); } fprintf(stderr,"Getting terminal configuration through ioctl().\n"); tcgetattr(0, &_InitialState_); fprintf(stderr,"Setting terminal state.\n"); SetKeyboard(); _SetSig_(); _KeyQueue_ = (KeyQueue_t *)NULL; /* Tester si les I/O clavier non bloquantes sont possibles */ fprintf(stderr,"Testing non-blocking keyboard I/O...\n"); #ifdef SIGACT New.sa_handler = NonBlockErr; sigemptyset(&New.sa_mask); New.sa_flags = 0; sigaction(SIGALRM,&New,&Old); #else AlrmFct = signal(SIGALRM,NonBlockErr); #endif rc = setjmp(_nb_buf_); if(rc == 0) { Remains = alarm(2); for(i=0;i<10;i++) KeyPressed(); #ifdef SIGACT sigaction(SIGALRM,&Old,&New); if(Old.sa_handler != SIG_IGN) alarm(Remains); #else signal(SIGALRM,AlrmFct); if(AlrmFct != SIG_IGN) alarm(Remains); #endif FreeKeyQueue(); fprintf(stderr,"Non-blocking I/O seem to be handled correctly.\n"); } else { fprintf(stderr,"Non-blocking I/O unhandled:\n\r"); fprintf(stderr,"the "); switch(_KbType_) { case POSIX_KBD: fprintf(stderr,"POSIX"); break; case BSD_KBD: fprintf(stderr,"BSD"); break; case SYSTEM_V_KBD: fprintf(stderr,"System V"); break; case USR_GROUP_KBD: fprintf(stderr,"/usr/group"); break; } fprintf(stderr," keyboard handling method doesn't work.\n\r"); fprintf(stderr,"Change the handler initialisation. Raising SIGQUIT...\n\r"); raise(SIGQUIT); } SetKeyboard(); switch(MappingType) { case INTERACTIVE_MAP: fprintf(stderr,"Searching for a keyboard mapping file...\n"); if(InitKbFromFile(KeyFile) == -1) { fprintf(stderr,"No valid mapping file for this terminal: switching to interractive mapping...\n\n"); InitKbFromKeyboard(KeyFile); } else fprintf(stderr,"Found. Keyboard mapped.\n"); break; case TERMCAP_MAP: /* Mise a 0 des sequences */ for(i=0;i<_NBRE_KEYS_;i++) for(j=0;j