Skip to content

Interrupt Handlers

maxlandon edited this page May 29, 2023 · 5 revisions

Continuing with our example application with two different menus (main and client), here we show how you can bind special interrupt handlers to each menu. In our current example, and as seen in various cases, sometimes one needs to exit a current context (here a menu) without exiting the entire application, with CtrlC/CtrlD.

Those handlers, currently, can only be bound to errors that are returned by the underlying readline shell. The latter returns:

  • io.EOF error when receiving a Ctrl-D keystroke.
  • console.ErrCtrlC when receiving a Ctrl-C keystroke.

In addition, the console will return handle the following os.Signals even when a command is being executed (they are being wrapped into an error for matching):

errors.New(syscall.SIGINT.String())
errors.New(syscall.SIGTERM.String())
errors.New(syscall.SIGQUIT.String())

Writing the handler

The following shows two different handlers.

The first is used in the main menu, to exit the application.

// exitCtrlD is a custom interrupt handler to use when the shell
// readline receives an io.EOF error, which is returned with CtrlD.
func exitCtrlD(c *console.Console) {
    reader := bufio.NewReader(os.Stdin)
    fmt.Print("Confirm exit (Y/y): ")
    text, _ := reader.ReadString('\n')
    answer := strings.TrimSpace(text)

    if (answer == "Y") || (answer == "y") {
        os.Exit(0)
    }
}

The second is used in our client menu, and which will switch back to the main menu.

func switchMenu(c *console.Console) {
    fmt.Println("Switching to main menu")
    c.SwitchMenu("")
}

Binding to the menu

We can bind both of our handlers like this:

import (
    "io"
    "github.com/reeflective/console"
)

func main() {
    app := console.New()
    
    // Main menu interrupts
    menu := app.CurrentMenu()
    menu.AddInterrupt(io.EOF, exitCtrlD)
    
    // Client menu interrupts
    clientMenu := console.Menu("client")
    clientMenu.AddInterrupt(console.ErrCtrlC, switchMenu)
}