In this article, I'll show you how to get terminal input in real time using Go language. This time, I would like to introduce only UNIX-like OS. In the case of Windows, it seems that it can be realized with the API provided by Microsoft ... (insufficient research)
-Introduction -[Table of Contents](#Table of Contents) -Conclusion -[Operating environment](#Operating environment) -[What is Canonical Mode](#What is Canonical Mode) -[What is non-canonical mode](#What is non-canonical mode) -[Sample code](#Sample code) -[What is Raw mode](What is #raw mode) -[Sample code](#Sample code-1) -[Reference site](#Reference site) -Conclusion
Putting the terminal in non-canonical mode, you can get input instantly. To enter non-canonical mode, you must change the values in the fields of the termios structure. The following describes each mode with sample code and a Linux manual.
Before discussing non-canonical mode, let's first talk about canonical mode. To put it simply, it's the default state. Input is accepted until Enter is pressed.
Quote Japanese translation manual for Linux
--Input is done line by line. The input line becomes available when the line delimiter is typed. The line delimiters are NL, EOL, EOL2 and EOF at the beginning of the line. For non-EOF, the buffer returned by read (2) also includes line delimiters
--Line editing is enabled (ERASE, KILL have an effect; WERASE, REPRINT, LNEXT also have an effect if the IEXTEN flag is set). read (2) returns at most one line of input. If read (2) requests less bytes than the current input line, only the same number of bytes as requested is read, and the remaining characters are read in the next read (2).
Non-canonical mode, this mode can accept input in real time. You can set the number of accepted characters with MIN (c_cc [VMIN]). For example, if c_cc [VMIN] = 3, the input is completed and the program is loaded when 3 characters are input. You can set the timeout period with TIME (c_cc [VTIME]) and set it to 0 if you do not need the timeout.
Quote Japanese translation manual for Linux
--In non-canonical mode, input is immediately available (user does not have to type in line delimiters), no input processing is performed, and line editing is disabled. The settings of MIN (c_cc [VMIN]) and TIME (c_cc [VTIME]) determine the conditions under which read (2) completes.
In C language, when accessing the termios structure, use the functions of "termios.h". I will borrow the pkg / term package created by volunteers so that this can be used in the same way in Go language. When changing the parameters of the termios structure, use Tcgetattr () to get the current settings, and then make the necessary settings for bit operations. For the parameters that can be set, refer to Japanese Translation Manual for Linux. By the way, the flags that can be used in C language could basically be used in the pkg / term package as well. Once you have completed the required settings, you can reflect them with Tcsetattr ().
sample.go
package main
import (
"fmt"
"syscall"
"github.com/pkg/term/termios"
)
func main() {
var ttystate syscall.Termios
//Get terminal settings
termios.Tcgetattr(uintptr(syscall.Stdin), &ttystate)
//Change terminal settings
setNonCanonicalMode(&ttystate)
//Get standard input
bufCh := make(chan []byte, 1)
go readBuffer(bufCh)
for {
fmt.Printf("What you typed: %c\n", <-bufCh)
}
}
//Set to non-canonical mode
func setNonCanonicalMode(attr *syscall.Termios) {
//Disable canonical mode (canonical mode disabled)&^ AND NOT)
attr.Lflag &^= syscall.ICANON
//Minimum number of characters when reading=One character
attr.Cc[syscall.VMIN] = 1
//Timeout time when reading non-canonical= 0
attr.Cc[syscall.VTIME] = 0
//Reflect the changed settings
termios.Tcsetattr(uintptr(syscall.Stdin), termios.TCSANOW, attr)
}
//Get the value of the buffer
func readBuffer(bufCh chan []byte) {
buf := make([]byte, 1024)
for {
if n, err := syscall.Read(syscall.Stdin, buf); err == nil {
bufCh <- buf[:n]
}
}
}
When the above code is executed, the behavior is as follows. Printf is executed after the entered key is displayed on the screen. By the way, if you increase the value of VMIN, you can get multiple values.
In addition to the features of non-canonical mode, the keys you type do not appear on the screen. Also, since special processing does not work, you cannot force termination with Ctrl + C.
Quote Japanese translation manual for Linux
-Input is possible on a character-by-character basis, echo is disabled, and all special processing for terminal I / O characters is disabled.
It is a pattern set to Raw mode as shown in Japanese translation manual for Linux. (* Comment out the parts that are deemed unnecessary.) You can set it according to the manual without thinking like the code below, but I feel that it is better to select the setting parameter as needed.
sample.go
package main
import (
"fmt"
"syscall"
"github.com/pkg/term/termios"
)
func main() {
var ttystate syscall.Termios
//Get terminal settings
termios.Tcgetattr(uintptr(syscall.Stdin), &ttystate)
//Change terminal settings * Change here
//setNonCanonicalMode(&ttystate)
setRawMode(&ttystate)
bufCh := make(chan []byte, 1)
go readBuffer(bufCh)
for {
fmt.Printf("What you typed: %c\n", <-bufCh)
}
}
//Set to Raw mode
func setRawMode(attr *syscall.Termios) {
//The set value is[https://linuxjm.osdn.jp/html/LDP_man-pages/man3/termios.3.html]Quoted from
//If you bit clear OPOST, the output will be strange in your environment, so comment out
//Ctrl when ISIG is disabled+Comment out because it is difficult to debug because C cannot be pressed
//In the manual, the character size is specified as CS8, but it is judged unnecessary in this verification and commented out.
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
//Reflect the changed settings
termios.Tcsetattr(uintptr(syscall.Stdin), termios.TCSANOW, attr)
}
//Get the value of the buffer
func readBuffer(bufCh chan []byte) {
//abridgement
}
Unlike the above non-canonical mode, the pressed key is not displayed on the screen.
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
There is a lot of information about termios handled in C language, but there were few cases where it was done in Go language. I started looking to create a joke command, but it took longer than I expected. I've put together an article for myself in the future, and for those who are similarly jammed. It was interesting to be able to touch the low layers that I don't usually think about.
Recommended Posts