1package termbox 2 3import ( 4 "syscall" 5 6 "github.com/mattn/go-runewidth" 7) 8 9// public API 10 11// Initializes termbox library. This function should be called before any other functions. 12// After successful initialization, the library must be finalized using 'Close' function. 13// 14// Example usage: 15// err := termbox.Init() 16// if err != nil { 17// panic(err) 18// } 19// defer termbox.Close() 20func Init() error { 21 var err error 22 23 interrupt, err = create_event() 24 if err != nil { 25 return err 26 } 27 28 in, err = syscall.Open("CONIN$", syscall.O_RDWR, 0) 29 if err != nil { 30 return err 31 } 32 out, err = syscall.Open("CONOUT$", syscall.O_RDWR, 0) 33 if err != nil { 34 return err 35 } 36 37 err = get_console_mode(in, &orig_mode) 38 if err != nil { 39 return err 40 } 41 42 err = set_console_mode(in, enable_window_input) 43 if err != nil { 44 return err 45 } 46 47 orig_size, orig_window = get_term_size(out) 48 win_size := get_win_size(out) 49 50 err = set_console_screen_buffer_size(out, win_size) 51 if err != nil { 52 return err 53 } 54 55 err = fix_win_size(out, win_size) 56 if err != nil { 57 return err 58 } 59 60 err = get_console_cursor_info(out, &orig_cursor_info) 61 if err != nil { 62 return err 63 } 64 65 show_cursor(false) 66 term_size, _ = get_term_size(out) 67 back_buffer.init(int(term_size.x), int(term_size.y)) 68 front_buffer.init(int(term_size.x), int(term_size.y)) 69 back_buffer.clear() 70 front_buffer.clear() 71 clear() 72 73 diffbuf = make([]diff_msg, 0, 32) 74 75 go input_event_producer() 76 IsInit = true 77 return nil 78} 79 80// Finalizes termbox library, should be called after successful initialization 81// when termbox's functionality isn't required anymore. 82func Close() { 83 // we ignore errors here, because we can't really do anything about them 84 Clear(0, 0) 85 Flush() 86 87 // stop event producer 88 cancel_comm <- true 89 set_event(interrupt) 90 select { 91 case <-input_comm: 92 default: 93 } 94 <-cancel_done_comm 95 96 set_console_screen_buffer_size(out, orig_size) 97 set_console_window_info(out, &orig_window) 98 set_console_cursor_info(out, &orig_cursor_info) 99 set_console_cursor_position(out, coord{}) 100 set_console_mode(in, orig_mode) 101 syscall.Close(in) 102 syscall.Close(out) 103 syscall.Close(interrupt) 104 IsInit = false 105} 106 107// Interrupt an in-progress call to PollEvent by causing it to return 108// EventInterrupt. Note that this function will block until the PollEvent 109// function has successfully been interrupted. 110func Interrupt() { 111 interrupt_comm <- struct{}{} 112} 113 114// Synchronizes the internal back buffer with the terminal. 115func Flush() error { 116 update_size_maybe() 117 prepare_diff_messages() 118 for _, diff := range diffbuf { 119 chars := []char_info{} 120 for _, char := range diff.chars { 121 chars = append(chars, char) 122 if runewidth.RuneWidth(rune(char.char)) > 1 { 123 chars = append(chars, char_info{ 124 char: ' ', 125 attr: char.attr, 126 }) 127 } 128 } 129 r := small_rect{ 130 left: 0, 131 top: diff.pos, 132 right: term_size.x - 1, 133 bottom: diff.pos + diff.lines - 1, 134 } 135 write_console_output(out, chars, r) 136 } 137 if !is_cursor_hidden(cursor_x, cursor_y) { 138 move_cursor(cursor_x, cursor_y) 139 } 140 return nil 141} 142 143// Sets the position of the cursor. See also HideCursor(). 144func SetCursor(x, y int) { 145 if is_cursor_hidden(cursor_x, cursor_y) && !is_cursor_hidden(x, y) { 146 show_cursor(true) 147 } 148 149 if !is_cursor_hidden(cursor_x, cursor_y) && is_cursor_hidden(x, y) { 150 show_cursor(false) 151 } 152 153 cursor_x, cursor_y = x, y 154 if !is_cursor_hidden(cursor_x, cursor_y) { 155 move_cursor(cursor_x, cursor_y) 156 } 157} 158 159// The shortcut for SetCursor(-1, -1). 160func HideCursor() { 161 SetCursor(cursor_hidden, cursor_hidden) 162} 163 164// Changes cell's parameters in the internal back buffer at the specified 165// position. 166func SetCell(x, y int, ch rune, fg, bg Attribute) { 167 if x < 0 || x >= back_buffer.width { 168 return 169 } 170 if y < 0 || y >= back_buffer.height { 171 return 172 } 173 174 back_buffer.cells[y*back_buffer.width+x] = Cell{ch, fg, bg} 175} 176 177// Returns a slice into the termbox's back buffer. You can get its dimensions 178// using 'Size' function. The slice remains valid as long as no 'Clear' or 179// 'Flush' function calls were made after call to this function. 180func CellBuffer() []Cell { 181 return back_buffer.cells 182} 183 184// Wait for an event and return it. This is a blocking function call. 185func PollEvent() Event { 186 select { 187 case ev := <-input_comm: 188 return ev 189 case <-interrupt_comm: 190 return Event{Type: EventInterrupt} 191 } 192} 193 194// Returns the size of the internal back buffer (which is mostly the same as 195// console's window size in characters). But it doesn't always match the size 196// of the console window, after the console size has changed, the internal back 197// buffer will get in sync only after Clear or Flush function calls. 198func Size() (int, int) { 199 return int(term_size.x), int(term_size.y) 200} 201 202// Clears the internal back buffer. 203func Clear(fg, bg Attribute) error { 204 foreground, background = fg, bg 205 update_size_maybe() 206 back_buffer.clear() 207 return nil 208} 209 210// Sets termbox input mode. Termbox has two input modes: 211// 212// 1. Esc input mode. When ESC sequence is in the buffer and it doesn't match 213// any known sequence. ESC means KeyEsc. This is the default input mode. 214// 215// 2. Alt input mode. When ESC sequence is in the buffer and it doesn't match 216// any known sequence. ESC enables ModAlt modifier for the next keyboard event. 217// 218// Both input modes can be OR'ed with Mouse mode. Setting Mouse mode bit up will 219// enable mouse button press/release and drag events. 220// 221// If 'mode' is InputCurrent, returns the current input mode. See also Input* 222// constants. 223func SetInputMode(mode InputMode) InputMode { 224 if mode == InputCurrent { 225 return input_mode 226 } 227 if mode&InputMouse != 0 { 228 err := set_console_mode(in, enable_window_input|enable_mouse_input|enable_extended_flags) 229 if err != nil { 230 panic(err) 231 } 232 } else { 233 err := set_console_mode(in, enable_window_input) 234 if err != nil { 235 panic(err) 236 } 237 } 238 239 input_mode = mode 240 return input_mode 241} 242 243// Sets the termbox output mode. 244// 245// Windows console does not support extra colour modes, 246// so this will always set and return OutputNormal. 247func SetOutputMode(mode OutputMode) OutputMode { 248 return OutputNormal 249} 250 251// Sync comes handy when something causes desync between termbox's understanding 252// of a terminal buffer and the reality. Such as a third party process. Sync 253// forces a complete resync between the termbox and a terminal, it may not be 254// visually pretty though. At the moment on Windows it does nothing. 255func Sync() error { 256 return nil 257} 258