package termbox import ( "syscall" "github.com/mattn/go-runewidth" ) // public API // Initializes termbox library. This function should be called before any other functions. // After successful initialization, the library must be finalized using 'Close' function. // // Example usage: // err := termbox.Init() // if err != nil { // panic(err) // } // defer termbox.Close() func Init() error { var err error interrupt, err = create_event() if err != nil { return err } in, err = syscall.Open("CONIN$", syscall.O_RDWR, 0) if err != nil { return err } out, err = syscall.Open("CONOUT$", syscall.O_RDWR, 0) if err != nil { return err } err = get_console_mode(in, &orig_mode) if err != nil { return err } err = set_console_mode(in, enable_window_input) if err != nil { return err } orig_size, orig_window = get_term_size(out) win_size := get_win_size(out) err = set_console_screen_buffer_size(out, win_size) if err != nil { return err } err = fix_win_size(out, win_size) if err != nil { return err } err = get_console_cursor_info(out, &orig_cursor_info) if err != nil { return err } show_cursor(false) term_size, _ = get_term_size(out) back_buffer.init(int(term_size.x), int(term_size.y)) front_buffer.init(int(term_size.x), int(term_size.y)) back_buffer.clear() front_buffer.clear() clear() diffbuf = make([]diff_msg, 0, 32) go input_event_producer() IsInit = true return nil } // Finalizes termbox library, should be called after successful initialization // when termbox's functionality isn't required anymore. func Close() { // we ignore errors here, because we can't really do anything about them Clear(0, 0) Flush() // stop event producer cancel_comm <- true set_event(interrupt) select { case <-input_comm: default: } <-cancel_done_comm set_console_screen_buffer_size(out, orig_size) set_console_window_info(out, &orig_window) set_console_cursor_info(out, &orig_cursor_info) set_console_cursor_position(out, coord{}) set_console_mode(in, orig_mode) syscall.Close(in) syscall.Close(out) syscall.Close(interrupt) IsInit = false } // Interrupt an in-progress call to PollEvent by causing it to return // EventInterrupt. Note that this function will block until the PollEvent // function has successfully been interrupted. func Interrupt() { interrupt_comm <- struct{}{} } // Synchronizes the internal back buffer with the terminal. func Flush() error { update_size_maybe() prepare_diff_messages() for _, diff := range diffbuf { chars := []char_info{} for _, char := range diff.chars { chars = append(chars, char) if runewidth.RuneWidth(rune(char.char)) > 1 { chars = append(chars, char_info{ char: ' ', attr: char.attr, }) } } r := small_rect{ left: 0, top: diff.pos, right: term_size.x - 1, bottom: diff.pos + diff.lines - 1, } write_console_output(out, chars, r) } if !is_cursor_hidden(cursor_x, cursor_y) { move_cursor(cursor_x, cursor_y) } return nil } // Sets the position of the cursor. See also HideCursor(). func SetCursor(x, y int) { if is_cursor_hidden(cursor_x, cursor_y) && !is_cursor_hidden(x, y) { show_cursor(true) } if !is_cursor_hidden(cursor_x, cursor_y) && is_cursor_hidden(x, y) { show_cursor(false) } cursor_x, cursor_y = x, y if !is_cursor_hidden(cursor_x, cursor_y) { move_cursor(cursor_x, cursor_y) } } // The shortcut for SetCursor(-1, -1). func HideCursor() { SetCursor(cursor_hidden, cursor_hidden) } // Changes cell's parameters in the internal back buffer at the specified // position. func SetCell(x, y int, ch rune, fg, bg Attribute) { if x < 0 || x >= back_buffer.width { return } if y < 0 || y >= back_buffer.height { return } back_buffer.cells[y*back_buffer.width+x] = Cell{ch, fg, bg} } // Returns a slice into the termbox's back buffer. You can get its dimensions // using 'Size' function. The slice remains valid as long as no 'Clear' or // 'Flush' function calls were made after call to this function. func CellBuffer() []Cell { return back_buffer.cells } // Wait for an event and return it. This is a blocking function call. func PollEvent() Event { select { case ev := <-input_comm: return ev case <-interrupt_comm: return Event{Type: EventInterrupt} } } // Returns the size of the internal back buffer (which is mostly the same as // console's window size in characters). But it doesn't always match the size // of the console window, after the console size has changed, the internal back // buffer will get in sync only after Clear or Flush function calls. func Size() (int, int) { return int(term_size.x), int(term_size.y) } // Clears the internal back buffer. func Clear(fg, bg Attribute) error { foreground, background = fg, bg update_size_maybe() back_buffer.clear() return nil } // Sets termbox input mode. Termbox has two input modes: // // 1. Esc input mode. When ESC sequence is in the buffer and it doesn't match // any known sequence. ESC means KeyEsc. This is the default input mode. // // 2. Alt input mode. When ESC sequence is in the buffer and it doesn't match // any known sequence. ESC enables ModAlt modifier for the next keyboard event. // // Both input modes can be OR'ed with Mouse mode. Setting Mouse mode bit up will // enable mouse button press/release and drag events. // // If 'mode' is InputCurrent, returns the current input mode. See also Input* // constants. func SetInputMode(mode InputMode) InputMode { if mode == InputCurrent { return input_mode } if mode&InputMouse != 0 { err := set_console_mode(in, enable_window_input|enable_mouse_input|enable_extended_flags) if err != nil { panic(err) } } else { err := set_console_mode(in, enable_window_input) if err != nil { panic(err) } } input_mode = mode return input_mode } // Sets the termbox output mode. // // Windows console does not support extra colour modes, // so this will always set and return OutputNormal. func SetOutputMode(mode OutputMode) OutputMode { return OutputNormal } // Sync comes handy when something causes desync between termbox's understanding // of a terminal buffer and the reality. Such as a third party process. Sync // forces a complete resync between the termbox and a terminal, it may not be // visually pretty though. At the moment on Windows it does nothing. func Sync() error { return nil }