| package termbox |
| |
| import ( |
| "syscall" |
| ) |
| |
| // 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 = 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 = 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) |
| <-cancel_done_comm |
| |
| set_console_cursor_info(out, &orig_cursor_info) |
| set_console_cursor_position(out, coord{}) |
| set_console_screen_buffer_size(out, orig_size) |
| 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 { |
| r := small_rect{ |
| left: 0, |
| top: diff.pos, |
| right: term_size.x - 1, |
| bottom: diff.pos + diff.lines - 1, |
| } |
| write_console_output(out, diff.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 click 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 |
| } |