【问题标题】:Unbuffered input manager in Go?Go 中的无缓冲输入管理器?
【发布时间】:2018-02-16 17:10:57
【问题描述】:

我正在用 Go 创建一个简单的基于控制台的游戏。我想要某种方式来接受无缓冲的输入(例如,您输入一个键并立即返回)。我从这段代码开始:

func InitInput() {
  exec.Command("stty", "-f", "/dev/tty", "cbreak", "min", "1").Run()
  exec.Command("stty", "-f", "/dev/tty", "-echo").Run()
}
func StopInput() {
  exec.Command("stty", "-f", "/dev/tty", "echo").Run()
}
func GetInput() string {
  var b []byte = make([]byte, 1)
  for {
    os.Stdin.Read(b)
    return string(b)
  }
}

这太棒了,但它只适用于基于 *nix 的操作系统,并且需要 3 个函数。接下来,有人为我推荐了这段代码:

/*
// Works also for 64 bits
#ifdef _WIN32

// Lib for console management in windows
#include "conio.h"

#else

// Libs terminal management in Unix, Linux...
#include <stdio.h>
#include <unistd.h>
#include <termios.h>

// Implement reading a key pressed in terminal
char getch(){
    char ch = 0;
    struct termios old = {0};
    fflush(stdout);
    if( tcgetattr(0, &old) < 0 ) perror("tcsetattr()");
    old.c_lflag &= ~ICANON;
    old.c_lflag &= ~ECHO;
    old.c_cc[VMIN] = 1;
    old.c_cc[VTIME] = 0;
    if( tcsetattr(0, TCSANOW, &old) < 0 ) perror("tcsetattr ICANON");
    if( read(0, &ch,1) < 0 ) perror("read()");
    old.c_lflag |= ICANON;
    old.c_lflag |= ECHO;
    if(tcsetattr(0, TCSADRAIN, &old) < 0) perror("tcsetattr ~ICANON");
    return ch;
}
#endif
*/
import "C"

然后你只需要 1 个函数:

func GetInput() string {
    return string(byte(C.getch()))
}

这很好用,除了因为 cgo 的工作方式,它非常慢,这对于游戏来说并不理想。此外,用于测试换行符的原始代码 if extras.GetInput() == "\n" {} 不再起作用。有没有办法让单字符无缓冲输入管理器在 Go 中工作而无需使用又大又厚的外部库?

【问题讨论】:

  • 你的意思是像github.com/nsf/termbox-go
  • @icza 这正是我想要的一个例子。这是一个庞大而臃肿的库,我只需要一个轻量级的输入管理器。
  • go 编译器不包含很多你从导入包中不使用的东西,所以我不认为这是一个问题。
  • 而且 termbox-go 相当轻量级:“Termbox 是一个提供简约 API 的库……”
  • @KavehShahbazian 我再次想要termbox-go。阅读this

标签: go input cgo


【解决方案1】:

我从头开始编写了一些控制台处理,在 C、Perl 和 PHP,正准备用 Python 做一个。

然后我发现了 Go,我也在问同样的问题。 我同意该线程的作者,我们必须做最少的事情。 我还没有尝试过你的代码(包含 termio 代码的那个)。

可能会慢的原因有很多:

1 / 每次按下键时都会调用 C: 您可以更改此拓扑: 首先,将控制台置于非缓冲模式并将保存的参数返回到 Golang。

2/ 你按照这段代码敲键:

package main
import (
    "fmt"
    "os"
    "os/exec"
)

func main() {
    // disable input buffering
    exec.Command("stty", "-F", "/dev/tty", "cbreak", "min", "1").Run()
    // do not display entered characters on the screen
    exec.Command("stty", "-F", "/dev/tty", "-echo").Run()
    // restore the echoing state when exiting
    defer exec.Command("stty", "-F", "/dev/tty", "echo").Run()

    var b []byte = make([]byte, 1)
    LOOP:
    for {
        os.Stdin.Read(b)
        var n uint8 = b[0]
        switch n {
        case 27, 113, 81 : fmt.Println("ESC, or 'q' or 'Q' was hitted!")
            break LOOP
        default:
            fmt.Printf("You typed : %d\n", n)
        }
    }
}

最后你恢复控制台设置。

在这段代码中:我使用 stty 但我想我可以用 2 个 C 例程调用来替换它。 好吧,我需要尝试实现这个......

还有一个问题:我们应该返回一个字节、一个字符还是一个宽字符?这肯定会更复杂。

请注意,您在键盘上也有扩展键,并且必须向前读取一个或多个字节才能获得整个序列,但这不是问题。

【讨论】:

    猜你喜欢
    • 2018-10-02
    • 2021-12-11
    • 2013-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-16
    相关资源
    最近更新 更多