package main import ( "io" "log" "os/exec" "time" "github.com/eiannone/keyboard" "github.com/gdamore/tcell" "github.com/rivo/tview" ) const ( CMD_FIM = "/usr/local/bin/pf-fim.sh" SLIDESHOW_INTERVAL = 300 * time.Second ) func main() { // fim placeholder so we can operate on it when a exit slideshow is received var fim *exec.Cmd = nil // Run slideshow fim = exec.Command(CMD_FIM) // Setup stdin for fim to control slideshow stdin, err := fim.StdinPipe() if err != nil { log.Fatalf("Error getting fim stdin : %s", err) } // Advance slideshow every interval as defined in const() ticker := time.NewTicker(SLIDESHOW_INTERVAL) stop_ticker := make(chan struct{}) go func() { for { select { case <-ticker.C: _, err = io.WriteString(stdin, "n") if err != nil { log.Fatalf("Error advancing slides : %s", err) } case <-stop_ticker: ticker.Stop() stdin.Close() return } } }() // Start watching for key strokes and echo them back to stdout keysEvents, err := keyboard.GetKeys(10) if err != nil { panic(err) } defer func() { _ = keyboard.Close() }() // Goroutine for tracking which keys are pressed and controlling fim if appropriate go func() { for { event := <-keysEvents if event.Err != nil { panic(event.Err) } log.Printf("You pressed: key %X\n", event.Key) // Keys for fim event management (previous/next in particular) fimKey := "" if event.Key == keyboard.KeyArrowLeft || event.Key == keyboard.KeyArrowDown { fimKey = "p" } if event.Key == keyboard.KeyArrowRight || event.Key == keyboard.KeyArrowUp { fimKey = "n" } // Exit fim and move to the config UI if event.Key == keyboard.KeyEsc || event.Key == keyboard.KeyEnter || event.Key == keyboard.KeySpace { if fim != nil { // Just in case someone lays on exit key or similar during startup if err := fim.Process.Kill(); err != nil { log.Fatalf("failed to kill fim : %s", err) } } break } // Control fim if we received a valid key for next/previous slide if fimKey != "" { log.Printf("Sending key to fim : %s", fimKey) _, err = io.WriteString(stdin, fimKey) if err != nil { log.Fatalf("Error controlling fim : %s", err) } ticker.Reset(SLIDESHOW_INTERVAL) } } }() // Run fim if err := fim.Run(); err != nil { // Unwrap the error a bit so we can find out if a signal killed fim or something else // An exit code of -1 means the program didn't exit in time or was terminated by a signal (per the docs) if exitError, ok := err.(*exec.ExitError); ok && exitError.ExitCode() != -1 { log.Fatalf("Error running fim : %s", err) } } // Stop fim slideshow advancing go routine close(stop_ticker) // Run config UI when slideshow stops app := tview.NewApplication() frame := tview.NewFrame(tview.NewBox().SetBackgroundColor(tcell.ColorBlack)). SetBorders(2, 2, 2, 2, 4, 4). AddText("PiFrame", true, tview.AlignCenter, tcell.ColorWhite). AddText("Configuration Utility", true, tview.AlignCenter, tcell.ColorRed) if err := app.SetRoot(frame, true).EnableMouse(true).Run(); err != nil { log.Fatalf("Error running UI : %s", err) } }