[Go language] Comment obtenir l'entrée du terminal en temps réel

introduction

Dans cet article, je vais vous montrer comment utiliser le langage Go pour obtenir une entrée de terminal en temps réel. Cette fois, je voudrais vous présenter uniquement le système d'exploitation UNIX. Dans le cas de Windows, il semble que cela puisse être réalisé avec l'API fournie par Microsoft ... (recherche insuffisante)

table des matières

Conclusion

En mettant le terminal en mode non canonique, vous pouvez obtenir une entrée instantanément. Pour passer en mode non canonique, vous devez modifier les valeurs dans les champs de la structure termios. Dans ce qui suit, chaque mode est expliqué avec un exemple de code et un manuel Linux.

Environnement d'exploitation

Qu'est-ce que le mode canonique?

Avant de discuter du mode non canonique, parlons d'abord du mode canonique. Pour faire simple, c'est l'état par défaut. L'entrée est acceptée jusqu'à ce que vous appuyiez sur Entrée.

Citation manuel de traduction en japonais pour Linux

--L'entrée se fait ligne par ligne. La ligne d'entrée devient disponible lorsque le caractère de saut de ligne est tapé. Les délimiteurs de ligne sont NL, EOL, EOL2 et EOF au début d'une ligne. Pour les non-EOF, le tampon renvoyé par read (2) comprend également des séparateurs de ligne

L'édition de ligne est activée (ERASE, KILL ont un effet; WERASE, REPRINT, LNEXT ont également un effet si l'indicateur IEXTEN est activé). read (2) renvoie au plus une ligne d'entrée. Si read (2) demande moins d'octets que la ligne d'entrée courante, seul le même nombre d'octets demandés sera lu et les caractères restants seront lus lors de la lecture suivante (2).

Qu'est-ce que le mode non canonique?

Mode non canonique, ce mode peut accepter des entrées en temps réel. Vous pouvez définir le nombre de caractères acceptés avec MIN (c_cc [VMIN]). Par exemple, si c_cc [VMIN] = 3, l'entrée sera terminée et le programme sera chargé lorsque 3 caractères seront saisis. Vous pouvez définir le délai d'expiration avec TIME (c_cc [VTIME]) et le mettre à 0 si vous n'avez pas besoin de délai.

Citation manuel de traduction en japonais pour Linux

--En mode non canonique, l'entrée est immédiatement disponible (l'utilisateur n'a pas besoin de taper des sauts de ligne), aucun traitement d'entrée n'est effectué et l'édition de ligne est désactivée. Les paramètres de MIN (c_cc [VMIN]) et TIME (c_cc [VTIME]) déterminent les conditions dans lesquelles read (2) se termine.

Exemple de code

En langage C, utilisez les fonctions de "termios.h" pour accéder à la structure termios. J'emprunterai le package pkg / term créé par des volontaires afin qu'il puisse également être utilisé dans le langage Go. Lorsque vous modifiez les paramètres de la structure termios, utilisez Tcgetattr () pour obtenir les paramètres actuels, puis effectuez les réglages nécessaires pour les opérations sur les bits. Pour les paramètres qui peuvent être définis, reportez-vous au Manuel de traduction en japonais pour Linux. À propos, les indicateurs qui peuvent être utilisés en langage C pourraient également être utilisés dans le package pkg / term. Une fois que vous avez terminé les paramètres requis, vous pouvez les refléter avec Tcsetattr ().

sample.go


package main

import (
	"fmt"
	"syscall"

	"github.com/pkg/term/termios"
)

func main() {
	var ttystate syscall.Termios

	//Obtenir les paramètres du terminal
	termios.Tcgetattr(uintptr(syscall.Stdin), &ttystate)

    //Modifier les paramètres du terminal
    setNonCanonicalMode(&ttystate)

    //Obtenez une entrée standard
	bufCh := make(chan []byte, 1)
	go readBuffer(bufCh)

	for {
		fmt.Printf("Ce que vous avez tapé: %c\n", <-bufCh)
	}
}

//Définir en mode non canonique
func setNonCanonicalMode(attr *syscall.Termios) {

	//Désactiver le mode canonique (&^ AND NOT)
	attr.Lflag &^= syscall.ICANON

	//Nombre minimum de caractères lors de la lecture=Un personnage
	attr.Cc[syscall.VMIN] = 1

	//Délai d'expiration lors de la lecture non canonique= 0
	attr.Cc[syscall.VTIME] = 0

	//Refléter les paramètres modifiés
	termios.Tcsetattr(uintptr(syscall.Stdin), termios.TCSANOW, attr)
}

//Obtenez la valeur du tampon
func readBuffer(bufCh chan []byte) {
	buf := make([]byte, 1024)

	for {
		if n, err := syscall.Read(syscall.Stdin, buf); err == nil {
			bufCh <- buf[:n]
		}
	}
}

Lorsque le code ci-dessus est exécuté, le comportement est le suivant. Printf est exécuté après l'affichage de la clé saisie à l'écran. En passant, si vous augmentez la valeur de VMIN, vous pouvez obtenir plusieurs valeurs.

Demo01

Qu'est-ce que le mode Raw?

Outre les fonctionnalités du mode non canonique, les touches que vous saisissez n'apparaissent pas à l'écran. De plus, comme le traitement spécial ne fonctionne pas, vous ne pouvez pas forcer l'arrêt avec Ctrl + C.

Citation manuel de traduction en japonais pour Linux

-L'entrée est possible caractère par caractère, l'écho est désactivé et tout traitement spécial pour les caractères d'entrée / sortie du terminal est désactivé.

Exemple de code

Il s'agit d'un modèle défini en mode Raw comme indiqué dans Manuel de traduction en japonais pour Linux. (* J'ai commenté les parties que j'ai jugées inutiles.) Vous pouvez le régler selon le manuel sans penser comme le code ci-dessous, mais je pense qu'il est préférable de sélectionner le paramètre de réglage selon les besoins.

sample.go


package main

import (
	"fmt"
	"syscall"

	"github.com/pkg/term/termios"
)

func main() {
	var ttystate syscall.Termios

	//Obtenir les paramètres du terminal
	termios.Tcgetattr(uintptr(syscall.Stdin), &ttystate)

	//Modifier les paramètres du terminal * Modifier ici
	//setNonCanonicalMode(&ttystate)
	setRawMode(&ttystate)

	bufCh := make(chan []byte, 1)
	go readBuffer(bufCh)

	for {
		fmt.Printf("Ce que vous avez tapé: %c\n", <-bufCh)
	}
}

//Mettre en mode brut
func setRawMode(attr *syscall.Termios) {

	//La valeur définie est[https://linuxjm.osdn.jp/html/LDP_man-pages/man3/termios.3.html]Cité de
	//Si vous effacez OPOST, la sortie sera étrange dans votre environnement, alors commentez
	//Ctrl lorsque ISIG est désactivé+Commentez car il est difficile de déboguer car C ne peut pas être pressé
	//Dans le manuel, la taille des caractères est spécifiée comme CS8, mais elle est jugée inutile dans cette vérification et commentée.

    attr.Iflag &^= syscall.BRKINT | syscall.ICRNL | syscall.INPCK | syscall.ISTRIP | syscall.IXON
  
	//attr.Oflag &^= syscall.OPOST
  
    attr.Cflag &^= syscall.CSIZE | syscall.PARENB
  
    //attr.Cflag |= syscall.CS8
  
    attr.Lflag &^= syscall.ECHO | syscall.ICANON | syscall.IEXTEN //| syscall.ISIG
  
    attr.Cc[syscall.VMIN] = 1
	attr.Cc[syscall.VTIME] = 0

	//Refléter les paramètres modifiés
	termios.Tcsetattr(uintptr(syscall.Stdin), termios.TCSANOW, attr)
}

//Obtenez la valeur du tampon
func readBuffer(bufCh chan []byte) {
	//réduction
}

Demo02 Contrairement au mode non canonique ci-dessus, la touche enfoncée n'est pas affichée à l'écran.

Site de référence

https://linuxjm.osdn.jp/html/LDP_man-pages/man3/termios.3.html https://www.slideshare.net/c-bata/golang-ui https://grimoire.f5.si/archives/125

en conclusion

Il y a beaucoup d'informations sur les termios gérés en langage C, mais il y a eu peu de cas où cela a été fait en langage Go. J'ai commencé à chercher à créer une commande blague, mais cela a pris plus de temps que prévu. J'ai rédigé un article pour moi dans le futur et pour ceux qui sont également bloqués. C'était intéressant de pouvoir toucher les couches basses auxquelles je ne pense généralement pas.

Recommended Posts

[Go language] Comment obtenir l'entrée du terminal en temps réel
[Question] Comment obtenir les données des données textarea en temps réel à l'aide du flacon du framework Web Python
Comment écrire en temps réel hors ligne Résolution des problèmes E04 avec Python
Comment obtenir stacktrace en python
Comment obtenir la différence de date et d'heure en secondes avec Python
Comment obtenir les résultats de l'identifiant dans Celery
Méthode de contrôle exclusive multi-processus en langage C
Comment obtenir de l'aide dans un shell interactif
Comment réparer lorsque l'entrée du terminal devient anormale
Comment obtenir les fichiers dans le dossier [Python]
Comment lire les données de séries chronologiques dans PyTorch
Comment masquer l'entrée utilisateur dans l'interface graphique PySimple
Comment obtenir le nom de la variable lui-même en python
Comment obtenir plusieurs objets de modèle au hasard dans Django
Comment obtenir le nombre de chiffres en Python
Comment mesurer le temps de traitement avec Python ou Java
Obtenez une sortie standard en temps réel avec le sous-processus Python
Comment faire un clic droit en utilisant la saisie au clavier dans RPA?
Comment quitter lors de l'utilisation de Python dans Terminal (Mac)
Aller à la langue pour voir et se souvenir du langage Partie 7 C en langage GO
Comment obtenir des histogrammes RVB et HSV avec OpenCV
Comment se débarrasser des pictogrammes personnalisés du serveur dans message.content
Comment implémenter provisoirement une barre de progression dans un langage de script
[Django] Comment donner des valeurs d'entrée à l'avance avec ModelForm
J'ai essayé "Comment obtenir une méthode décorée en Python"
J'ai essayé d'illustrer le temps et le temps du langage C
Obtenir la taille du terminal en Python
Comment pirater un terminal
Comment obtenir le pointeur d'instruction (= compteur de programme) dans le noyau Linux
Comment obtenir la dernière (dernière) valeur d'une liste en Python
Comment définir les variables Go
Hello World en langue GO
Comment obtenir toutes les clés et valeurs du dictionnaire
[Aller] Comment utiliser "... (3 périodes)"
Comment obtenir une liste d'exceptions intégrées pour python
Obtenez des champs de chat en direct YouTube en temps réel avec l'API
Comment développer en Python
Comment obtenir un aperçu de vos données dans Pandas
[Python] Comment utiliser input ()
[Shell] Comment obtenir la branche distante par défaut avec Git
Partie 1 J'ai écrit la réponse au problème de référence de l'écriture hors ligne en temps réel en Python
J'ai essayé de décrire le trafic en temps réel avec WebSocket
Le programmeur Java a essayé de toucher le langage Go (pour le moment)
Comment obtenir toutes les valeurs possibles dans une expression régulière
Repeated @ app.callback dans Dash Comment écrire correctement l'entrée et l'état
Comment déterminer qu'une clé croisée a été entrée dans Python3
Comment obtenir une chaîne à partir d'un argument de ligne de commande en python
<Pandas> Comment gérer les données de séries chronologiques dans le tableau croisé dynamique
Comment obtenir les coordonnées de sommet d'une entité dans ArcPy
Créez une fonction pour obtenir le contenu de la base de données dans Go
Comment écrire hors ligne en temps réel Résolution des problèmes F01 avec Python
Comment calculer "xx time" en un seul coup avec Python Timedelta
[Python] Comment faire PCA avec Python