Go

termbox-go の基本的な使い方

Go でTUI(Text User Interface) を作るための termbox-go の基本的な使い方のメモ

基本的な考え方

  1. メインループを作る
  2. イベントハンドリングを定義する
  3. 描画関数を定義する
  4. メインループ内で描画関数を呼び出す
package main

import (
    "math/rand"

    log "github.com/Sirupsen/logrus"
    "github.com/nsf/termbox-go"
)

const coldef = termbox.ColorDefault

// (3) 描画関数の定義
// (x, y)座標に四角を表示するだけの関数
func drawBox(x, y int) {
    termbox.Clear(coldef, coldef)
    termbox.SetCell(x, y, '┏', coldef, coldef)
    termbox.SetCell(x+1, y, '┓', coldef, coldef)
    termbox.SetCell(x, y+1, '┗', coldef, coldef)
    termbox.SetCell(x+1, y+1, '┛', coldef, coldef)
    termbox.Flush() // Flushを呼ばないと描画されない
}

func main() {
    if err := termbox.Init(); err != nil {
        log.Fatal(err)
    }
    defer termbox.Close()

    drawBox(0, 0)
    // (1) メインループ
MAINLOOP:
    for {
        w, h := termbox.Size()
        // (2) イベントハンドリング
        switch ev := termbox.PollEvent(); ev.Type {
        case termbox.EventKey:
            switch ev.Key {
            case termbox.KeyEsc:
                break MAINLOOP
            }
        }
        drawBox(rand.Intn(w), rand.Intn(h)) // (4) メインループ内で描画関数を呼び出す
    }
}
  • 描画の基本は termbox.SetCell(x座標, y座標, '表示したいrune', 前景色, 背景色) 関数
    • 指定した座標に1文字表示する関数
    • 極端な話、1座標ずつこれを呼び出せば好きな画面を構築できる

バッファを直接変更して画面に表示する方法

termbox-go はターミナル画面に表示される領域を termbox.Cell 構造体の配列として保持している。
back buffer とも呼ぶもよう。
この配列を直接指定して値を設定しても、同様に画面を操作可能。ただし、1次元配列なので画面幅と高さを取得してオフセット計算を行う必要がある。

package main

import (
    log "github.com/Sirupsen/logrus"
    "github.com/nsf/termbox-go"
)

const coldef = termbox.ColorDefault

func drawBox() {
    w, _ := termbox.Size()
    termbox.Clear(coldef, coldef)

    buf := termbox.CellBuffer()

    buf[0].Ch = '┏'
    buf[0].Bg = coldef
    buf[0].Fg = termbox.ColorRed

    buf[1].Ch = '┓'
    buf[1].Bg = termbox.ColorRed
    buf[1].Fg = coldef

    buf[w].Ch = '┗'
    buf[w].Bg = coldef
    buf[w].Fg = coldef

    buf[w+1].Ch = '┛'
    buf[w+1].Bg = coldef
    buf[w+1].Fg = coldef

    termbox.Flush() // Flushを呼ばないと描画されない
}

func main() {
    if err := termbox.Init(); err != nil {
        log.Fatal(err)
    }
    defer termbox.Close()

    drawBox()
MAINLOOP:
    for {
        switch ev := termbox.PollEvent(); ev.Type {
        case termbox.EventKey:
            switch ev.Key {
            case termbox.KeyEsc:
                break MAINLOOP
            }
        default:
            drawBox()
        }
    }
}

参考文献