Skip to content

Commit

Permalink
Update main.go to clean up console mode
Browse files Browse the repository at this point in the history
Something in gum causes console mode to change causing issues (for me Ctrl-C not quitting certain PowerShell commands). We cannot rely on stdin stream as you cannot set console mode on a pipe (causes error, isn't the same thing). Finally found a way to open the stream for console input using magic string `"CONIN$"` thanks to some long google searching. Links in source code.

For some reason the console handle changes between start and end of the program so have to reopen the file to get current handle. What the change is doing is opening console at start of mode and saving the mode. It also checks if virtual terminal stuff is available. Then on main exiting it reopens the console and sets the mode to what was saved, including forcing virtual terminal enabled back on.

With this change `Ctrl-C` now works after gum has run.

This change is in no way cross platform compatible. I will leave that up to someone else to figure out.
  • Loading branch information
immersivegamer committed Aug 16, 2022
1 parent 5f4cc48 commit 6577304
Showing 1 changed file with 56 additions and 0 deletions.
56 changes: 56 additions & 0 deletions main.go
Expand Up @@ -11,6 +11,8 @@ import (
"github.com/muesli/termenv"

"github.com/charmbracelet/gum/internal/exit"

"golang.org/x/sys/windows"
)

const shaLen = 7
Expand All @@ -23,11 +25,60 @@ var (
// CommitSHA contains the SHA of the commit that this application was built
// against. It's set via ldflags when building.
CommitSHA = ""

vtInputSupported bool
consoleInMode uint32
consoleHandle windows.Handle
)

var bubbleGumPink = lipgloss.NewStyle().Foreground(lipgloss.Color("212"))

func saveConsoleModeForPowerShell() {
// get handle for console ... this took way to long to find how to get the console file stream ...
// from: https://github.com/FiloSottile/age/pull/274/files/03f6e59d5ccf931734067c5e1f06ac3baa1187cc#r643446247
console, _ := os.OpenFile("CONIN$", os.O_RDWR, 0777)
consoleHandle = windows.Handle(console.Fd())

// consoleInMode is global var so we can revert after any gum stuff that changes it
// from: https://github.com/containerd/console/blob/main/console_windows.go#L34
if err := windows.GetConsoleMode(consoleHandle, &consoleInMode); err == nil {
// Validate that windows.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it.
if err = windows.SetConsoleMode(consoleHandle, consoleInMode|windows.ENABLE_VIRTUAL_TERMINAL_INPUT); err == nil {
vtInputSupported = true
}
// Unconditionally set the console mode back even on failure because SetConsoleMode
// remembers invalid bits on input handles.
windows.SetConsoleMode(consoleHandle, consoleInMode)
}
}

func revertConsoleModeForPowerShell() {
console, err := os.OpenFile("CONIN$", os.O_RDWR, 0777)

if err == nil {
consoleHandle = windows.Handle(console.Fd())
// use saved mode when starting the program and ensure
// ENABLE_VIRTUAL_TERMINAL_INPUT is enabled too
if vtInputSupported {
consoleInMode = consoleInMode | windows.ENABLE_VIRTUAL_TERMINAL_INPUT
}
windows.SetConsoleMode(consoleHandle, consoleInMode)
// this what always worked for me in powershell, but saving the mode and
// reverting seems more sane
// windows.SetConsoleMode(consoleHandle, 503)

// close for extra measure?
console.Close()
}
}

func main() {
// read and save the console mode
saveConsoleModeForPowerShell()

// always revert console mode after running
defer revertConsoleModeForPowerShell()

lipgloss.SetColorProfile(termenv.ANSI256)

if Version == "" {
Expand Down Expand Up @@ -60,11 +111,16 @@ func main() {
"defaultUnderline": "false",
},
)

if err := ctx.Run(); err != nil {
if errors.Is(err, exit.ErrAborted) {
// read somewhere that `os.Exit()` means defers don't run, so
// calling here as well
revertConsoleModeForPowerShell()
os.Exit(exit.StatusAborted)
}
fmt.Println(err)
revertConsoleModeForPowerShell()
os.Exit(1)
}
}

0 comments on commit 6577304

Please sign in to comment.