Skip to content

Instantly share code, notes, and snippets.

@uobikiemukot
Last active December 3, 2018 22:48
Show Gist options
  • Save uobikiemukot/c769f4ce1d3683bc16893b0bbb455a21 to your computer and use it in GitHub Desktop.
Save uobikiemukot/c769f4ce1d3683bc16893b0bbb455a21 to your computer and use it in GitHub Desktop.

% Goと端末制御シーケンス % haru % 2018/12/03

自己紹介

  • @uobikiemukot
  • 業務
    • Ruby (サーバーサイド)
  • 趣味の開発
    • 端末エミュレータ 「yaft」

yaft

(元々は) Linux framebuffer用 端末エミュレータ

話す内容

課題3-2 「分割ダウンロードを行う」

$ ./rget https://blog.golang.org/gopher/header.jpg > header.jpg
$ echo $?
0

…地味 🤔

ダウンロードの進捗を表示したい!

くるくる回るアスキーアートを描画する(1)

var aa = []string{"-", "\\", "|", "/"}
...
for {
	select {
	case <-tick:
		if counter > 0 {
			fmt.Printf("\x08")
		}
		fmt.Printf("%s", aa[counter%len(aa)])
		counter++
		...
	}
}

くるくる回るアスキーアートを描画する(2)

https://gist.github.com/uobikiemukot/c769f4ce1d3683bc16893b0bbb455a21#file-lt1-gif

くるくる回るアスキーアートを描画する(3)

var aa = []string{"-", "\\", "|", "/"}
...
for {
	select {
	case <-tick:
		if counter > 0 {
			fmt.Printf("\x08") // Backspaceで一文字消している
		}
		fmt.Printf("%s", aa[counter%len(aa)])
		counter++
		...
	}
}

制御文字

man ascii

  • Bell: \x07
    • ビープを鳴らす
  • BackSpace: \x08
    • 直前の1文字を削除
  • Tab: \x09
    • タブ
  • Newline: \x0A
    • 改行
  • ESC: \x1B
    • より複雑な端末の制御 (後述)

プログレスバーを描画する(1)

if counter > 0 {
	fmt.Printf("\x1b[G")
	fmt.Printf("\x1b[K")
}
for i := 0; i < 10; i++ {
	if i <= counter {
		fmt.Printf("=")
	} else {
		fmt.Printf("-")
	}
}
fmt.Printf(" %d%%", (counter + 1) * 10)
counter++

プログレスバーを描画する(2)

https://gist.github.com/uobikiemukot/c769f4ce1d3683bc16893b0bbb455a21#file-lt2-gif

プログレスバーを描画する(3)

if counter > 0 {
	fmt.Printf("\x1b[G") // 行の先頭へ移動
	fmt.Printf("\x1b[K") // 1行削除
}
for i := 0; i < 10; i++ {
	if i <= counter {
		fmt.Printf("=")
	} else {
		fmt.Printf("-")
	}
}
fmt.Printf(" %d%%", (counter + 1) * 10)
counter++

端末制御シーケンス

http://uobikiemukot.github.io/yaft/escape.html

  • \x1b [ G
    • カーソルを行の先頭へ移動
  • \x1b [ Pl ; Pc H
    • カーソルを (Pl 行, Pc 列) へ移動
  • \x1b [ K
    • 行を削除
  • \x1b [ J
    • 画面を削除

色を付ける (1)

...
fmt.Printf("\x1b[3%dm", counter%8)
for i := 0; i < 10; i++ {
	if i <= counter {
		fmt.Printf("=")
	} else {
		fmt.Printf("-")
	}
}
fmt.Printf(" %d%%", (counter + 1) * 10)
fmt.Printf("\x1b[0m")
counter++

色を付ける (2)

https://gist.github.com/uobikiemukot/c769f4ce1d3683bc16893b0bbb455a21#file-lt3-gif

色を付ける (3)

...
fmt.Printf("\x1b[3%dm", counter%8) // 文字色を指定
for i := 0; i < 10; i++ {
	if i <= counter {
		fmt.Printf("=")
	} else {
		fmt.Printf("-")
	}
}
fmt.Printf(" %d%%", (counter + 1) * 10)
fmt.Printf("\x1b[0m") // 色を解除
counter++

SGR (select graphic rendition)

  • \x1b [ Ps m
    • Ps = 0
      • 全ての属性を解除
    • Ps = 21
      • ボールド
    • Ps = 31
      • 文字色を赤に
    • Ps = 41
      • 背景色を赤に
    • Ps = 38;5;Ps
      • xterm 256色拡張

参考(1): 端末制御ライブラリ

参考(2): xterm 256色拡張

http://web.archive.org/web/20130125000058/http://www.frexx.de/xterm-256-notes/

https://gist.github.com/uobikiemukot/c769f4ce1d3683bc16893b0bbb455a21#file-xterm256-gif

参考(3): sixel

https://github.com/mattn/go-sixel

https://gist.github.com/uobikiemukot/c769f4ce1d3683bc16893b0bbb455a21#file-gosr-gif

おしまい

ありがとうございました 🙇‍♀️

https://gist.github.com/uobikiemukot/c769f4ce1d3683bc16893b0bbb455a21#file-lt4-gif

package main
import (
"fmt"
"os"
"time"
)
func removeChar(n int) {
for i := 0; i < n; i++ {
fmt.Printf("\x08")
}
}
func moveColumn(n int) {
fmt.Printf("\x1b[%dG", n)
}
func eraseLine() {
fmt.Printf("\x1b[K")
}
func main() {
var aa = []string{"-", "\\", "|", "/"}
tick := time.Tick(500 * time.Millisecond)
after := time.After(5 * time.Second)
i := 0
fmt.Printf("downloading...")
for {
select {
case <-tick:
if i > 0 {
removeChar(1)
}
fmt.Printf("%s", aa[i%len(aa)])
i++
case <-after:
fmt.Println()
os.Exit(0)
}
}
}
package main
import (
"fmt"
"os"
"time"
)
func removeChar(n int) {
for i := 0; i < n; i++ {
fmt.Printf("\x08")
}
}
func moveColumn(n int) {
fmt.Printf("\x1b[%dG", n)
}
func eraseLine() {
fmt.Printf("\x1b[K")
}
func main() {
tick := time.Tick(500 * time.Millisecond)
after := time.After(5 * time.Second)
counter := 0
for {
select {
case <-tick:
if counter > 0 {
moveColumn(0)
eraseLine()
}
for i := 0; i < 10; i++ {
if i <= counter {
fmt.Printf("=")
} else {
fmt.Printf("-")
}
}
fmt.Printf(" %d%%", (counter + 1) * 10)
counter++
case <-after:
fmt.Println()
os.Exit(0)
}
}
}
package main
import (
"fmt"
"os"
"time"
)
func removeChar(n int) {
for i := 0; i < n; i++ {
fmt.Printf("\x08")
}
}
func moveColumn(n int) {
fmt.Printf("\x1b[%dG", n)
}
func eraseLine() {
fmt.Printf("\x1b[K")
}
func main() {
tick := time.Tick(500 * time.Millisecond)
after := time.After(5 * time.Second)
counter := 0
for {
select {
case <-tick:
if counter > 0 {
moveColumn(0)
eraseLine()
}
fmt.Printf("\x1b[3%dm", counter%8)
for i := 0; i < 10; i++ {
if i <= counter {
fmt.Printf("=")
} else {
fmt.Printf("-")
}
}
fmt.Printf(" %d%%", (counter + 1) * 10)
fmt.Printf("\x1b[0m")
counter++
case <-after:
fmt.Println()
os.Exit(0)
}
}
}
package main
import (
"fmt"
"os"
"time"
)
func removeChar(n int) {
for i := 0; i < n; i++ {
fmt.Printf("\x08")
}
}
func moveColumn(n int) {
fmt.Printf("\x1b[%dG", n)
}
func eraseLine() {
fmt.Printf("\x1b[K")
}
func main() {
tick := time.Tick(500 * time.Millisecond)
after := time.After(5 * time.Second)
counter := 0
for {
select {
case <-tick:
if counter > 0 {
moveColumn(0)
eraseLine()
}
//fmt.Printf("\x1b[3%dm", counter%8)
for i := 0; i < 10; i++ {
if i == counter {
fmt.Printf(" ʕ◔ϖ◔ʔ ")
} else if i < counter {
fmt.Printf("ε=")
}
}
fmt.Printf(" %d%%", (counter + 1) * 10)
//fmt.Printf("\x1b[0m")
counter++
case <-after:
fmt.Println()
os.Exit(0)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment