Using 256 colors in Go - windows

How Can I used 256 colors in terminal with Golang.
As Libraries like faith/color only have limited colors support.
This python library here
use some kind of default code and a color code to print colored text in terminal.
I try to use color code but instead of color it printing color code in go program but in python program it prints colored text.
How can I print color use color code as above library doing...
Do I need to initialize the terminal ? If yes How?
Thanks!
I am expecting 256 colors to print in terminal.
*go version: 1.18.7

Windows can be weird. In some cases you need to set the console mode. If you are using Windows, specify it as part of your question.
colors.go:
package main
import (
"fmt"
"strconv"
)
func main() {
setConsoleColors()
for i := 0; i < 16; i++ {
for j := 0; j < 16; j++ {
code := strconv.Itoa(i*16 + j)
color := "\u001b[38;5;" + code + "m"
fmt.Printf("%s %-4s", color, code)
}
fmt.Println()
}
fmt.Print("\u001b[0m")
}
colors_windows.go:
//go:build windows
package main
import "golang.org/x/sys/windows"
func setConsoleColors() error {
console := windows.Stdout
var consoleMode uint32
windows.GetConsoleMode(console, &consoleMode)
consoleMode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
return windows.SetConsoleMode(console, consoleMode)
}
colors_other.go:
//go:build !windows
package main
func setConsoleColors() error {
return nil
}

Related

Is it possible to detect if a writer is tty or not?

Bash has a 'magical behavior', if you type 'ls', usually you will get colorful output, but if you redirect the output to a file, the color codes are gone. How to achive this effect using Go. e.g. With the following statement:
fmt.Println("\033[1;34mHello World!\033[0m")
I can see the text in color, but if I pipe the output to a file, the color is preserved, which is NOT what I want.
BTW, this question is mostly not related to Go, I just want to achive the effect in my go program.
Bash has a 'magical behavior', if you type 'ls', usually you will
get colorful output, but if you redirect the output to a file, the
color codes are gone.
It's not Bash feature, it's ls feature. It calls
isatty()
to check if stdout file descriptor refers to a terminal. In musl libc
isatty is implemented like that:
int isatty(int fd)
{
struct winsize wsz;
unsigned long r = syscall(SYS_ioctl, fd, TIOCGWINSZ, &wsz);
if (r == 0) return 1;
if (errno != EBADF) errno = ENOTTY;
return 0;
}
You can use the same method in Go:
package main
import (
"fmt"
"os"
"golang.org/x/sys/unix"
)
func main() {
_, err := unix.IoctlGetWinsize(int(os.Stdout.Fd()), unix.TIOCGWINSZ)
if err != nil {
fmt.Println("Hello World")
} else {
fmt.Println("\033[1;34mHello World!\033[0m")
}
}

Print multiple rows in parallel Golang

I am attempting to write a horse race simulator with multiple rows.
Each row will represent one horse location calculated by a goroutine.
For some reason the code, when run on the Go Playground, does not output the numbers randomly as happens on my machine.
package main
import (
"math/rand"
"os"
"strconv"
"time"
)
var counter = 0
func main() {
i := 1
horses := 9
for i <= horses {
go run(i)
i++
}
time.Sleep(5000 * time.Millisecond)
print("\ncounter: " + strconv.Itoa(counter))
print("\nEnd of main()")
}
func run(number int) {
var i = 1
var steps = 5
for i <= steps {
print("[" + strconv.Itoa(number) + "]")
rand.Seed(time.Now().UnixNano())
sleep := rand.Intn(10)
time.Sleep(time.Duration(sleep) * time.Millisecond)
i++
counter++
}
if i == steps {
println(strconv.Itoa(number) + " wins")
os.Exit(1)
}
}
Playground: https://play.golang.org/p/pycZ4EdH7SQ
My output unordered is:
[1][5][8][2][3][4][7][9][6][7][9][9][4][3]...
But my question is how would I go about to print the numbers like:
[1][1]
[2][2][2][2][2][2][2][2]
[3][3][3]
...
[N][N][N][N][N]
you may want to check out this stackoverflow answer which uses goterm to move the terminal cursor and allow you to overwrite part of it.
The idea is that once you get to the terminal bit you want to be "dynamic" (much like a videogame screen clear+redraw), you always reposition the cursor and "draw" your "horses" position.
Note that with this you will need to store their positions somewhere, to then "draw" their positions at each "frame".
With this exercise you are getting close to how video games work, and for this you may want to set up a goroutine with a given refresh rate to clear your terminal and render what you want.

Go Profiling - Wrong file

I'm doing profiling in Go using github.com/pkg/profile and it's creating the file when I run my code, but the return comes from the example page code, how would it be to run through my code?
thanks in advance
Code:
package main
import (
"fmt"
"github.com/pkg/profile"
"time"
)
func main() {
defer profile.Start(profile.MemProfile).Stop()
var inicio = time.Now().UnixNano()
var text = "Olá Mundo!"
fmt.Println(text)
var fim = time.Now().UnixNano()
fmt.Println(fim - inicio)
}
Return:
You can change your profile output path to to your current working directory,
profile.ProfilePath(path)
If you are unable to make retrieve any samples, it either means your MemProfileRate is not small enough to actually capture small changes.
If you are allocation less amount of memory, then set the MemProfileRate to lesser value, If you are allocating large amount of memory, just keep to default. If you think you capturing minor memory changes, then increase the MemProfileRate.
profile.MemProfileRate(100)
and one thing you shouldn't forget when you are using profile package is your call should be deferred.
defer profile.Start(xxx).Stop()
Here is the complete program.
package main
import (
"os"
"github.com/pkg/profile"
)
func main() {
dir, _ := os.Getwd()
defer profile.Start(profile.MemProfile, profile.MemProfileRate(100), profile.ProfilePath(dir)).Stop()
//decrease mem profile rate for capturing more samples
for i := 0; i < 10000; i++ {
tmp := make([]byte, 100000)
tmp[0] = tmp[1] << 0 //fake workload
}
}
you can also set profile path for having the profile output in your current workign directory.

Unbuffered input manager in Go?

I am creating a simple console-based game in Go. I want some way to accept unbuffered input (as in, you type in one key and it is immediately returned). I started out with this code:
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)
}
}
This was amazing, but it only works on a *nix-based os, and requires 3 functions. Next, someone recommended this code for me:
/*
// 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"
And then you only need 1 function:
func GetInput() string {
return string(byte(C.getch()))
}
This works perfectly, except that because of the way that cgo works, it is very slow which is not ideal for a game. Also, the original code for testing for a newline, if extras.GetInput() == "\n" {} doesn't work anymore. Is there a way to get a single-character unbuffered input manager to work in Go without using a big, thick, external library?
I wrote some console handling from scratch sources, in
C, Perl and PHP and was about to do one in Python.
Then I discovered Go and I'm asking the same questions.
I agree with the author of the thread, we have to do the minimum.
I haven't tried your code yet (The one which includes termio code).
There are many reasons why it may be slow:
1 / You make a C call every time you hit a key:
You may change this topology:
first, you place the console in non-buffering mode and return the saved parameters to
Golang.
2/ You hit keys according to this code:
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)
}
}
}
Then at last you restore console settings.
In this code : I use stty but I suppose I could replace it with 2 C routine calls.
Well, I need to try to implement this...
Also, a question: should we return a byte, a char a widechar? This can be more complicated for sure.
Notice that you have also extended keys on keyboard and must read forward one or more bytes to get the whole sequence, but this is not a problem.

Different execution under Windows and Linux with Go

I created a very small game of number guessing in Go. The thing is, it executes differently under Windows and under Linux. By executing it on Ubuntu for example, everything works just fine. But when I try to start it under Windows, it compiles just fine but during execution when I enter something (for example 5) it prints me twice "Smaller than random num"or "Bigger than random num". I have no idea why it happens.
package main
import (
"fmt"
"math/rand"
"time"
)
func main () {
var number int //declaration
var tries int = 0
random_number := random(1, 9) //gets random number
for ; ;tries++ {
//fmt.Printf("Enter your prediction: ")
fmt.Scanf("%v",&number)
if number == random_number {
break;
} else if number<random_number{
fmt.Printf("Smaller than random num\n")
} else {
fmt.Printf("Bigger than random num\n")
}
}
fmt.Printf("You guessed correctly in %v tries",tries)
}
func random(min, max int) int {
rand.Seed(time.Now().Unix())
return rand.Intn(max - min) + min
}
Newlines are different in Linux and Windows, and thus your program behaves differently.
See this issue: https://github.com/golang/go/issues/5391
To fix this, you can replace your Scanf with this (note the "\n" at the end):
fmt.Scanf("%v\n",&number)

Resources