package printer import ( "encoding/hex" "fmt" "io" "log" "os" "strings" "golang.org/x/sys/unix" ) type Printer interface { io.Reader io.Writer io.Closer } type usbPrinter struct { *os.File } const ( FlagNone = 0 FlagDebug = 1 << 0 ) func OpenUSBPrinter(path string, flags int) (Printer, error) { file, err := os.OpenFile(path, os.O_RDWR, 0) if err != nil { return nil, err } var printer Printer = usbPrinter{file} if flags&FlagDebug != 0 { printer = withDebugger(printer) } return printer, nil } func (usb usbPrinter) Write(p []byte) (int, error) { // The printer often doens't like it if we use multiple write syscalls to // transmit the data. usb.File.Write() doesn't seem to guarantee that it // will only use a single syscall, so we manually call write instead. return unix.Write(int(usb.Fd()), p) } type debugger struct { Printer lastError error } func withDebugger(printer Printer) Printer { return &debugger{ Printer: printer, lastError: nil, } } func (d *debugger) Read(p []byte) (n int, err error) { n, err = d.Printer.Read(p) if err == io.EOF { return } // don't spam the terminal with the same error again and again if err != nil && d.lastError != nil && err.Error() == d.lastError.Error() { return } d.lastError = err var builder strings.Builder fmt.Fprint(&builder, " \033[31m") for i, b := range p[:n] { if i > 0 { fmt.Fprint(&builder, " ") } fmt.Fprintf(&builder, "%08b", b) } if err != nil { if n > 0 { fmt.Fprint(&builder, " ") } fmt.Fprintf(&builder, "read error: %v", err) } fmt.Fprintf(&builder, "\033[0m\n") fmt.Fprint(os.Stderr, builder.String()) return } func (d *debugger) Write(p []byte) (n int, err error) { n, err = d.Printer.Write(p) printSize := len(p) max := 20 * 8 * 2 if printSize > max { printSize = max } fmt.Fprintf(os.Stderr, "\033[36m%s\033[0m", hex.Dump(p[:printSize])) if len(p) > printSize { fmt.Fprintf(os.Stderr, " \033[36m[%d bytes omitted]\033[0m\n", len(p)-printSize) } if err == nil && n == len(p) { log.Printf("wrote %d bytes to printer\n", n) } else { log.Printf("only wrote %d/%d bytes to printer: %v\n", n, len(p), err) } return }