diff --git a/examples/go.mod b/examples/go.mod index eb5c306e..8dea10c7 100644 --- a/examples/go.mod +++ b/examples/go.mod @@ -3,29 +3,28 @@ module examples go 1.18 require ( - github.com/charmbracelet/bubbletea/v2 v2.0.0-alpha.1.0.20241105155825-ead55032fd81 - github.com/charmbracelet/lipgloss/v2 v2.0.0-20241105145349-c8e32d1b422c - github.com/charmbracelet/x/ansi v0.4.4 - github.com/charmbracelet/x/cellbuf v0.0.5 - github.com/lucasb-eyer/go-colorful v1.2.0 + github.com/charmbracelet/x/ansi v0.4.5 + github.com/charmbracelet/x/cellbuf v0.0.6-0.20241106170917-eb0997d7d743 + github.com/charmbracelet/x/input v0.2.0 + github.com/charmbracelet/x/vt v0.0.0-00010101000000-000000000000 + github.com/creack/pty v1.1.24 + golang.org/x/image v0.22.0 ) require ( - github.com/charmbracelet/colorprofile v0.1.6 // indirect - github.com/charmbracelet/x/input v0.2.0 // indirect + github.com/charmbracelet/colorprofile v0.1.7 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect ) require ( - github.com/charmbracelet/x/term v0.2.0 // indirect + github.com/charmbracelet/x/term v0.2.0 github.com/charmbracelet/x/wcwidth v0.0.0-20241011142426-46044092ad91 // indirect - github.com/charmbracelet/x/windows v0.2.0 // indirect github.com/muesli/cancelreader v0.2.2 // indirect - github.com/rivo/uniseg v0.4.7 + github.com/rivo/uniseg v0.4.7 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect - golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.26.0 // indirect - golang.org/x/text v0.19.0 // indirect + golang.org/x/sys v0.27.0 // indirect + golang.org/x/text v0.20.0 // indirect ) replace github.com/charmbracelet/x/ansi => ../ansi @@ -36,6 +35,8 @@ replace github.com/charmbracelet/x/term => ../term replace github.com/charmbracelet/x/input => ../input +replace github.com/charmbracelet/x/vt => ../vt + replace github.com/charmbracelet/x/windows => ../windows replace github.com/charmbracelet/x/exp => ../exp diff --git a/examples/go.sum b/examples/go.sum index 846524ca..2289fdee 100644 --- a/examples/go.sum +++ b/examples/go.sum @@ -1,11 +1,9 @@ -github.com/charmbracelet/bubbletea/v2 v2.0.0-alpha.1.0.20241105155825-ead55032fd81 h1:J40lkNAii38iaDD8B/Eh2+mHL2U7DzS48RqXe0ap5OQ= -github.com/charmbracelet/bubbletea/v2 v2.0.0-alpha.1.0.20241105155825-ead55032fd81/go.mod h1:24niqT9RbtXhWg8zLRU/v/xTixlo1+DUsHQZ3+kez5Y= -github.com/charmbracelet/colorprofile v0.1.6 h1:nMMqCns0c0DfCwNGdagBh6SxutFqkltSxxKk5S9kt+Y= -github.com/charmbracelet/colorprofile v0.1.6/go.mod h1:3EMXDxwRDJl0c17eJ1jX99MhtlP9OxE/9Qw0C5lvyUg= -github.com/charmbracelet/lipgloss/v2 v2.0.0-20241105145349-c8e32d1b422c h1:rzZvGgEkGHwO34rdQuxR6yR2O/EGWw6PGV4g/w3FyPU= -github.com/charmbracelet/lipgloss/v2 v2.0.0-20241105145349-c8e32d1b422c/go.mod h1:M8oXIuQtauwIZFuFREK6cYi+2fu0YNiBFnmCP7N8c2c= +github.com/charmbracelet/colorprofile v0.1.7 h1:q7PtMQrRBBnLNE2EbtbNUtouu979EivKcDGGaimhyO8= +github.com/charmbracelet/colorprofile v0.1.7/go.mod h1:d3UYToTrNmsD2p9/lbiya16H1WahndM0miDlJWXWf4U= github.com/charmbracelet/x/wcwidth v0.0.0-20241011142426-46044092ad91 h1:D5OO0lVavz7A+Swdhp62F9gbkibxmz9B2hZ/jVdMPf0= github.com/charmbracelet/x/wcwidth v0.0.0-20241011142426-46044092ad91/go.mod h1:Ey8PFmYwH+/td9bpiEx07Fdx9ZVkxfIjWXxBluxF4Nw= +github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= +github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= @@ -17,10 +15,10 @@ github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/image v0.22.0 h1:UtK5yLUzilVrkjMAZAZ34DXGpASN8i8pj8g+O+yd10g= +golang.org/x/image v0.22.0/go.mod h1:9hPFhljd4zZ1GNSIZJ49sqbp45GKK9t6w+iXvGqZUz4= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= diff --git a/examples/htop/main.go b/examples/htop/main.go new file mode 100644 index 00000000..e01b9355 --- /dev/null +++ b/examples/htop/main.go @@ -0,0 +1,124 @@ +package main + +import ( + "fmt" + "image" + "image/color" + "image/draw" + "image/png" + "io" + "log" + "os" + "os/exec" + "sync/atomic" + "time" + + "github.com/charmbracelet/x/termios" + "github.com/charmbracelet/x/vt" + "github.com/creack/pty" + "golang.org/x/image/font" + "golang.org/x/image/font/basicfont" + "golang.org/x/image/math/fixed" + "golang.org/x/sys/unix" +) + +const ( + fontWidth = 7 + fontHeight = 13 +) + +func drawChar(img *image.RGBA, x, y int, fg color.Color, text string) { + point := fixed.Point26_6{fixed.Int26_6(x * 64), fixed.Int26_6(y * 64)} + if fg == nil { + fg = color.White + } + d := &font.Drawer{ + Dst: img, + Src: image.NewUniform(fg), + Face: basicfont.Face7x13, + Dot: point, + } + d.DrawString(text) +} + +var counter int32 = 1 + +func putImage(vt *vt.Terminal) { + rows, cols := vt.Height(), vt.Width() + img := image.NewRGBA(image.Rect(0, 0, cols*fontWidth, rows*fontHeight)) + draw.Draw(img, img.Bounds(), &image.Uniform{color.Black}, image.ZP, draw.Src) + + for row := 0; row < rows; row++ { + for col := 0; col < cols; col++ { + cell, ok := vt.At(col, row) + if !ok { + log.Printf("failed to get at %d %d", row, col) + } + txt := cell.Content + if len(txt) > 0 && txt[0] != 0 { + fg, bg := cell.Style.Fg, cell.Style.Bg + if bg == nil { + bg = color.Black + } + + // Draw background + x0, y0 := (col+1)*fontWidth, ((row+1)*fontHeight)-11 + x1, y1 := x0+fontWidth, y0+fontHeight + draw.Draw(img, image.Rect(x0, y0, x1, y1), &image.Uniform{bg}, image.ZP, draw.Over) + + drawChar(img, (col+1)*fontWidth, (row+1)*fontHeight, fg, txt) + } + } + } + + f, err := os.Create(fmt.Sprintf("output%d.png", counter)) + if err != nil { + log.Fatal(err) + } + + defer f.Close() + err = png.Encode(f, img) + if err != nil { + log.Fatal(err) + } + + atomic.AddInt32(&counter, 1) +} + +const ( + width = 165 + height = 25 +) + +func main() { + vt := vt.NewTerminal(width, height) + cmd := exec.Command("htop") + + go func() { + for { + time.Sleep(1 * time.Second) + putImage(vt) + } + }() + + go func() { + time.Sleep(5 * time.Second) + cmd.Process.Kill() + }() + + ptm, err := pty.Start(cmd) + if err != nil { + log.Fatal(err) + } + + if err := termios.SetWinsize(int(ptm.Fd()), &unix.Winsize{Row: uint16(height), Col: uint16(width)}); err != nil { + log.Fatal(err) + } + + go io.Copy(ptm, vt) + go io.Copy(vt, ptm) + + if err := cmd.Wait(); err != nil { + log.Fatal(err) + } +}