IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C Discussion :

[Linux][C] tasse de café -- phénomène bien connu


Sujet :

C

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre expérimenté

    Homme Profil pro
    Enseignant
    Inscrit en
    Septembre 2012
    Messages
    330
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Septembre 2012
    Messages : 330
    Par défaut [Linux][C] tasse de café -- phénomène bien connu
    Salutations !!

    Je suis en train de développer une application avec mes élèves basée sur le langage C. Il s'agit de simuler une partie de "blind test": un programme "moniteur" joue des morceaux et les joueurs armés d'un arduino et d'un bouton poussoir
    peuvent interrompre la lecture du morceau et introduire au clavier le titre l'artiste ou l'année d'édition du morceau en question.

    Le soucis c'est que la saisie peut provoquer l'effet dit "la tasse de café": si le joueur décide de ne jamais répondre, le jeu est bloqué... en effet la lecture du morceau va reprendre uniquement si le joueur a mal répondu, dans le cas contraire
    le "moniteur" va sélectionner au hasard un autre morceau et le jouer (tout ça grâce à SDL).

    Je n'ai pas encore vraiment planché sur la problématique mais je me pose la question suivante:

    Comment en Langage C, dans l'environnement Linux, serait-il possible de ne permettre la saisie au clavier que pendant un certain laps de temps (disons 30 secondes) ?
    Comment faire en sorte d'interrompre une fonction comme fgets() ?

    Piste:

    J'ai une fonction non bloquante de saisie au clavier que je pourrais utiliser, je vais soumettre d'ici peu une première approche mais je ne suis pas certain de mon coup.

  2. #2
    Membre Expert
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Par défaut
    Tu peux envoyer un signal au processus pour débloquer la saisie. À ta place cela dit je me ferais pas suer et j'utiliserais les évènements clavier SDL avec lesquels il est très simple de gérer tout ça.

  3. #3
    Membre expérimenté

    Homme Profil pro
    Enseignant
    Inscrit en
    Septembre 2012
    Messages
    330
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Septembre 2012
    Messages : 330
    Par défaut
    Voilà la fonction sur laquelle je pense m'appuyer pour permettre dans la boucle/fonction de saisie de notre programme de faire la saisie au clavier pendant un certain temps.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
     
    #ifdef LINUX
    int nbgetch(void)
    {
    	char							Buffer;
    	struct termios		oldt;
    	struct termios		newt;
     
    	tcgetattr(STDIN_FILENO, &oldt);
      newt = oldt;
     
    	//newt.c_lflag &= ~(ICANON | ECHO); // | ECHONL | ECHOCTL); 			// affiche le caractère alors que je dis de ne pas faire l'écho...
    	//tcsetattr(STDIN_FILENO, TCSANOW, &newt);
     
    	//newt.c_lflag=0;
    	// tcsetattr(STDIN_FILENO, TCSADRAIN, &newt);
     
    	// mode non bloquant...
     
    	newt.c_cc[VMIN] = 0;
    	newt.c_cc[VTIME] = 0;
    	tcsetattr(STDIN_FILENO, TCSANOW, &newt);
     
    	Buffer=getchar();	// retourne EOF (-1) si il y a rien dans le buffer
     
    	// on restitue "l'ancienne configuration"
    	tcsetattr(STDIN_FILENO, TCSANOW, &oldt); 
    	return Buffer;
    }
    #endif
    Voici l'idée de mon approche...
    Comme cette fonction ne saisit qu'un seul caractère, et encore je ne prends pas en compte les caractères UTF8, (faudra voir cela plus tard...), il faudra remplir un tableau/liste/pile FIFO avec chaque lettre/caractère saisi.
    En soi ce n'est pas la mort...

    Je vais utiliser cette fonction dans une fonction fct_getReponse() qui contient une boucle dans laquelle je vais saisir un caractère jusqu'à ce que la taille maximum du champs soit atteinte ou que le code ASCII 10 soit saisi.
    Incrémenter un compteur et sortir de la boucle dès que celui-ci aura atteint une certaine valeur me semble peu idéal, car, en fonction de la puissance du processeur, la valeur maximum peut être atteinte plus vite ou plus lentement...
    Je vais donc récupérer AVANT la boucle l'EPOCH à la seconde près dans une variable, et comparer dans la boucle si cette variable est égale à EPOCH+30... si tel est le cas j'interromperais la boucle.

    Qu'en pensez-vous ??

  4. #4
    Membre expérimenté

    Homme Profil pro
    Enseignant
    Inscrit en
    Septembre 2012
    Messages
    330
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Septembre 2012
    Messages : 330
    Par défaut
    @Matt --> Oui j'y ai pensé aussi... une sorte de SIGUSR1, ou un timer système...
    Je vais essayer un peu tout ce que me propose. D'autant que je n'ai pas encore exploré SDL2 à fond, je me doute bien qu'il doit y avoir de quoi me sortir de cette petite galère.
    Merci.

  5. #5
    Membre Expert
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Par défaut
    C'est vraiment pas sorcier : tu défiles les évènements comme d'habitude, en testant les keycodes pour les caractères imprimables qui t'intéressent que tu empiles dans un buffer jusqu'à ce que tu tombes sur la touche Entrée. C'est pas super UTF-8-compliant mais bon, je pense que ça fera l'affaire dans ton cas non ?

    Comme c'est au sein de la boucle de jeu, tu mets tout ça dans un état dédié à la saisie et lorsque l'utilisateur a validé la saisie ou que le compte à rebours est terminé tu passes à l'état suivant qui va bien.

  6. #6
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 495
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 495
    Par défaut
    Bonjour,

    Citation Envoyé par hurukan Voir le message
    Comment en Langage C, dans l'environnement Linux, serait-il possible de ne permettre la saisie au clavier que pendant un certain laps de temps (disons 30 secondes) ?
    Comment faire en sorte d'interrompre une fonction comme fgets() ?
    Matt Houston t'a donné de très bonnes indications mais puisque tu en es déjà à envisager termios, tu peux explorer ces pistes :

    • Soit tu t'appuies entièrement sur la SDL ;
    • Soit tu utilises alarm() (le timer système) pour définir ton laps de temps et tu fais un appel bloquant normal, comme fgetc(). À l'échéance, alarm() va envoyer SIGALRM à ton processus, et le fait en soi de recevoir un signal va provoquer l'échec de tout appel bloquant, qui va rendre la main avec un code spécial. Dans le cas de fgetc(), ce sera toujours EOF. À toi également de vérifier si ce EOF correspond bien à une fin de fichier (ou à la fermeture de l'entrée standard avec Ctrl+D). Il faudra aussi penser à mettre en place le handler du signal et à le réarmer à chaque fois, sinon le signal va tuer ton processus ;
    • Soit encore, tu utilises select() même s'il n'y a qu'un seul descripteur à surveiller et tu spécifies la valeur de timeout dans cet appel. C'est la manière la plus propre de procéder, à mon avis. L'avantage est que tout tient dans un seul appel et que le même code te permet éventuellement de surveiller plusieurs entrées à la fois.


    Bon courage.

  7. #7
    Membre expérimenté

    Homme Profil pro
    Enseignant
    Inscrit en
    Septembre 2012
    Messages
    330
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Septembre 2012
    Messages : 330
    Par défaut
    Ceci fonctionne...

    J'ai perdu trois heures à essayer de comprendre pourquoi quand je fais un "back space" je n'obtiens pas un retour en arrière normal... je vais m'y pencher plus tard... sinon ceci fonctionne.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
     
    char* getTimedUTF8String(short maxlen,short maxtime)
    {
    	int		cpt=0;
    	char*	tmp;
    	char	cCaractere=-1;
    	char	*pCaractere;
    	time_t start=time(NULL);
    	time_t current;
     
    	LinkedList *ll_Chaine;
     
    	maxlen++;	// pour ajouter le '\0'
    	ll_Chaine=lc_init();
     
    	// 1. Effectuer la boucle 
     
    	while(cCaractere!=10 && cpt<maxlen)
    	{
    		cCaractere=nbgetch();
    		current=time(NULL);
     
    		if(cCaractere!=-1) 
    		{
    			if(cCaractere==27) continue;
    			if(cCaractere>127) continue;
    			if(cCaractere<32 && cCaractere>0) continue;
    			cpt++;
    			pCaractere=malloc(1);
    			*pCaractere=cCaractere;
    			if(cCaractere!=10)
    				lc_add((void*)pCaractere,ll_Chaine,cssmbyte,sizeof(char));
    		}
     
    		int debug=current-start;
     
    		if(debug>maxtime) break;
    	}
     
    	cpt=0;
     
    	// 2. constituer la chaîne de caractères...
     
    	if(!ll_Chaine->NbElem) return NULL;
     
    	tmp=malloc(sizeof(char)*ll_Chaine->NbElem);
    	memset(tmp,0,ll_Chaine->NbElem);
     
    	while(ll_Chaine->NbElem>0)
    	{
    		lc_Datas *tmpDatas=lc_pop(ll_Chaine);
    		char	*ctmp;
    		ctmp=(char*)tmpDatas->value;
    		*tmp=*ctmp;
    		tmp++;
    		cpt++;
    	}
    	*(tmp)='\0';
     
    	lc_empty(ll_Chaine);
     
    	return tmp-cpt;
    }
    Le code permettant la saisie sur lequel je me basais était bloquant... il faut le modifier...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    #ifdef LINUX
    int nbgetch(void)
    {
    	char							Buffer;
    	struct termios		oldt;
    	struct termios		newt;
    		
    	tcgetattr(STDIN_FILENO, &oldt);
      newt = oldt;
    	
    	// mode non bloquant...
    		
    	newt.c_cc[VMIN] = 0;
    	newt.c_cc[VTIME] = 0;
    	newt.c_lflag &= ~ICANON;																					// n'a pas besoin d'appuyer sur "entrée" (caractère non bloquant de la procédure)
    	tcsetattr(STDIN_FILENO, TCSANOW, &newt);
    	
    	Buffer=getchar();	// retourne EOF (-1) si il y a rien dans le buffer
    	
    	// on restitue "l'ancienne configuration"
    	tcsetattr(STDIN_FILENO, TCSANOW, &oldt); 
    	return Buffer;
    }
    #endif

  8. #8
    Membre expérimenté

    Homme Profil pro
    Enseignant
    Inscrit en
    Septembre 2012
    Messages
    330
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Septembre 2012
    Messages : 330
    Par défaut
    J'ai déjà, je pense, partagé la librairie linkedlist sur ce forum, c'est une sorte d'essai sur la manière de gérer des listes chaînées pouvant prendre en compte n'importe quoi comme type de données.

    Je vous la file de nouveau si vous n'arriviez pas à mettre la main dessus...

    Dans mes essais j'ai remarqué que

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    newt.c_lflag &= ~(ICANON|ECHOCTL);
    Ne m'affiche plus les saloperies de ^H ou ^? quand j'appuie sur backspace en mode terminal...
    Je sens que j'approche de la vérité...

    Oui !!! En fait je suis bon ^^
    Ca marche !!
    Reste maintenant à enlever le dernier élément inséré dans la liste dès que l'on appuie sur back space...

  9. #9
    Membre Expert
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Par défaut
    Pourquoi faire simple, hein ?

    Je n'ai pas bien compris l'intérêt de l'étape avec la liste chaînée. Pourquoi ne stockes-tu pas directement la chaîne dans un tableau de char déclaré sur la pile ? C'est un blind test, le gars ne va pas saisir une biographie de l'interprète en trois volumes.

  10. #10
    Membre expérimenté

    Homme Profil pro
    Enseignant
    Inscrit en
    Septembre 2012
    Messages
    330
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Septembre 2012
    Messages : 330
    Par défaut
    Pour faire le malin, probablement...

    ...il y a "plus simple" effectivement, vu que je connais la taille maximum du tableau... un super char tmp[maxlen] et hop nous voilà parti... je vais d'ailleurs revoir ma copie plus c'est simple mieux c'est ^^

    Et autre chose aussi: il faut faire attention avec les paramètres c_cc[VMIN] et c_cc[VTIME]... il faut éviter de mettre à 0 c_cc[VTIME] sinon ça bouffe des ressources processeur (un de mes coeurs montait jusque 100%).
    J'ai mis une valeur de 50 et là tout va bien.

Discussions similaires

  1. Changer la tasse de café de Java
    Par midoscofield dans le forum Graphisme
    Réponses: 9
    Dernier message: 07/08/2013, 10h08
  2. [Associé] Développeur PHP MySQL connaissant bien Linux
    Par Mindiell dans le forum Autres
    Réponses: 0
    Dernier message: 14/12/2009, 23h15
  3. Réponses: 2
    Dernier message: 14/01/2008, 12h37

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo