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