[Go language] How to get terminal input in real time

Introduction

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)

table of contents

-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

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.

Operating environment

What is canonical mode?

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).

What is non-canonical mode?

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.

Sample code

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.

Demo01

What is Raw mode?

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.

Sample code

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
}

Demo02 Unlike the above non-canonical mode, the pressed key is not displayed on the screen.

Reference site

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

in conclusion

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

[Go language] How to get terminal input in real time
Post to slack in Go language
[Question] How to get data of textarea data in real time using Python web framework bottle
How to write offline real time Solve E04 problems in Python
How to get a stacktrace in python
How to get the date and time difference in seconds with python
How to make a request to bitFlyer Lightning's Private API in Go language
How to get results from id in Celery
How to multi-process exclusive control in C language
How to get help in an interactive shell
How to fix when Terminal input becomes abnormal
How to get the files in the [Python] folder
How to read time series data in PyTorch
How to hide user input in PySimple GUI
How to get the variable name itself in python
How to get multiple model objects randomly in Django
How to get the number of digits in Python
How to measure processing time in Python or Java
Get standard output in real time with Python subprocess
How to right click using keyboard input in RPA?
How to exit when using Python in Terminal (Mac)
Go language to see and remember Part 7 C language in GO language
How to get RGB and HSV histograms in OpenCV
How to use linux commands in Visual Studio Code terminal
How to get rid of server custom emoji in message.content
How to temporarily implement a progress bar in a scripting language
How to generate exponential pulse time series data in python
[Django] How to give input values in advance with ModelForm
I tried "How to get a method decorated in Python"
I tried to illustrate the time and time in C language
Get Terminal size in Python
How to hack a terminal
How to get Instruction Pointer (= program counter) in Linux kernel
How to get the last (last) value in a list in Python
How to define Go variables
Hello World in GO language
How to get all the keys and values in the dictionary
[Go] How to use "... (3 periods)"
How to get a list of built-in exceptions in python
Get YouTube Live chat field in real time with API
How to develop in Python
How to get an overview of your data in Pandas
[Python] How to use input ()
[Shell] How to get the remote default branch in Git
Try HeloWorld in your own language (with How to & code)
How to get a quadratic array of squares in a spiral!
Part 1 I wrote the answer to the reference problem of how to write offline in real time in Python
I tried to describe the traffic in real time with WebSocket
Created a package to support AWS Lambda development in Go language
Java programmer tried to touch Go language (for the time being)
How to get all the possible values in a regular expression
Repeated @ app.callback in Dash How to write Input and State neatly
How to judge that the cross key is input in Python3
How to get a string from a command line argument in python
<Pandas> How to handle time series data in a pivot table
How to get the vertex coordinates of a feature in ArcPy
The easiest way to get started in Slack socket mode (Go)
Create a function to get the contents of the database in Go
How to write offline real time Solve F01 problems with Python
How to calculate "xx time" in one shot with Python timedelta
[Python] How to do PCA in Python