v / vlib / term / term_windows.c.v
212 lines · 184 sloc · 6.17 KB · a87a4d73b9ab25cfff0822f4e94cf2a2d9e64323
Raw
1module term
2
3import os
4import time
5
6#include <conio.h>
7
8@[typedef]
9pub struct C.COORD {
10mut:
11 X i16
12 Y i16
13}
14
15@[typedef]
16pub struct C.SMALL_RECT {
17mut:
18 Left u16
19 Top u16
20 Right u16
21 Bottom u16
22}
23
24// win: CONSOLE_SCREEN_BUFFER_INFO
25// https://docs.microsoft.com/en-us/windows/console/console-screen-buffer-info-str
26@[typedef]
27pub struct C.CONSOLE_SCREEN_BUFFER_INFO {
28mut:
29 dwSize C.COORD
30 dwCursorPosition C.COORD
31 wAttributes u16
32 srWindow C.SMALL_RECT
33 dwMaximumWindowSize C.COORD
34}
35
36pub union C.uChar {
37mut:
38 UnicodeChar rune
39 AsciiChar u8
40}
41
42@[typedef]
43pub struct C.CHAR_INFO {
44mut:
45 Char C.uChar
46 Attributes u16
47}
48
49// ref - https://docs.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo
50fn C.GetConsoleScreenBufferInfo(handle C.HANDLE, info &C.CONSOLE_SCREEN_BUFFER_INFO) bool
51
52// ref - https://docs.microsoft.com/en-us/windows/console/setconsoletitle
53fn C.SetConsoleTitle(title &u16) bool
54
55// ref - https://docs.microsoft.com/en-us/windows/console/setconsolecursorposition
56fn C.SetConsoleCursorPosition(handle C.HANDLE, coord C.COORD) bool
57
58// ref - https://docs.microsoft.com/en-us/windows/console/scrollconsolescreenbuffer
59fn C.ScrollConsoleScreenBuffer(output C.HANDLE, scroll_rect &C.SMALL_RECT, clip_rect &C.SMALL_RECT, des C.COORD,
60 fill &C.CHAR_INFO) bool
61
62// get_terminal_size returns a number of columns and rows of terminal window.
63pub fn get_terminal_size() (int, int) {
64 if os.is_atty(1) > 0 && os.getenv('TERM') != 'dumb' {
65 info := C.CONSOLE_SCREEN_BUFFER_INFO{}
66 if C.GetConsoleScreenBufferInfo(C.GetStdHandle(C.STD_OUTPUT_HANDLE), &info) {
67 columns := int(info.srWindow.Right - info.srWindow.Left + 1)
68 rows := int(info.srWindow.Bottom - info.srWindow.Top + 1)
69 return columns, rows
70 }
71 }
72 return default_columns_size, default_rows_size
73}
74
75// get_cursor_position returns a Coord containing the current cursor position.
76pub fn get_cursor_position() !Coord {
77 mut res := Coord{}
78 if os.is_atty(1) > 0 && os.getenv('TERM') != 'dumb' {
79 info := C.CONSOLE_SCREEN_BUFFER_INFO{}
80 if C.GetConsoleScreenBufferInfo(C.GetStdHandle(C.STD_OUTPUT_HANDLE), &info) {
81 res.x = info.dwCursorPosition.X
82 res.y = info.dwCursorPosition.Y
83 } else {
84 return os.last_error()
85 }
86 }
87 return res
88}
89
90// set_terminal_title changes the terminal title.
91pub fn set_terminal_title(title string) bool {
92 wide_title := title.to_wide()
93 return C.SetConsoleTitle(wide_title)
94}
95
96// set_tab_title changes the terminal *tab title*, for terminal emulators that do support several tabs.
97pub fn set_tab_title(title string) bool {
98 // TODO: investigate, whether there is an API for changing just the tab title on windows yet.
99 return set_terminal_title(title)
100}
101
102// clear clears current terminal screen.
103// Implementation taken from https://docs.microsoft.com/en-us/windows/console/clearing-the-screen#example-2.
104pub fn clear() bool {
105 hconsole := C.GetStdHandle(C.STD_OUTPUT_HANDLE)
106 mut csbi := C.CONSOLE_SCREEN_BUFFER_INFO{}
107 mut scrollrect := C.SMALL_RECT{}
108 mut scrolltarget := C.COORD{}
109 mut fill := C.CHAR_INFO{}
110
111 // Get the number of character cells in the current buffer.
112 if !C.GetConsoleScreenBufferInfo(hconsole, &csbi) {
113 return false
114 }
115 // Scroll the rectangle of the entire buffer.
116 scrollrect.Left = 0
117 scrollrect.Top = 0
118 scrollrect.Right = u16(csbi.dwSize.X)
119 scrollrect.Bottom = u16(csbi.dwSize.Y)
120
121 // Scroll it upwards off the top of the buffer with a magnitude of the entire height.
122 scrolltarget.X = 0
123 scrolltarget.Y = (0 - csbi.dwSize.Y)
124
125 // Fill with empty spaces with the buffer's default text attribute.
126 fill.Char.UnicodeChar = rune(` `)
127 fill.Attributes = csbi.wAttributes
128
129 // Do the scroll
130 C.ScrollConsoleScreenBuffer(hconsole, &scrollrect, C.NULL, scrolltarget, &fill)
131
132 // Move the cursor to the top left corner too.
133 csbi.dwCursorPosition.X = 0
134 csbi.dwCursorPosition.Y = 0
135
136 C.SetConsoleCursorPosition(hconsole, csbi.dwCursorPosition)
137 return true
138}
139
140// supports_sixel returns `true` if the terminal supports Sixel graphics
141//
142// For more info on the sixel format:
143// See https://en.wikipedia.org/wiki/Sixel
144// See https://www.digiater.nl/openvms/decus/vax90b1/krypton-nasa/all-about-sixels.text
145// For more info on terminal support:
146// See https://www.arewesixelyet.com
147pub fn supports_sixel() bool {
148 // According to (2024) https://www.arewesixelyet.com/#windows-console there's no support
149 return false
150}
151
152// graphics_num_colors returns the number of color registers the terminal
153// graphic attribute is set to use. This can be useful to know if the terminal
154// is configured to support Sixel graphics.
155//
156// See "CSI ? Pi ; Pa ; Pv S" from https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
157pub fn graphics_num_colors() u16 {
158 // Since this call is related to sixel terminal graphics and Windows Console and Terminal
159 // does not have support for querying the graphics setup this call returns 0
160 return 0
161}
162
163// enable_echo enable/disable echo input characters.
164pub fn enable_echo(enable bool) {
165 // no need under windows, use key_pressed func's echo
166}
167
168fn C.kbhit() bool
169fn C._getch() i32
170fn C._getche() i32
171
172// KeyPressedParams contains the optional parameters that you can pass to key_pressed.
173@[params]
174pub struct KeyPressedParams {
175pub mut:
176 blocking bool // whether to wait for a pressed key
177 echo bool // whether to output the pressed key to stdout
178}
179
180// key_pressed gives back a single character, read from the standard input.
181// It returns -1 on error or no character in non-blocking mode
182pub fn key_pressed(params KeyPressedParams) i64 {
183 for {
184 if C.kbhit() {
185 res := if params.echo {
186 C._getche()
187 } else {
188 C._getch()
189 }
190 // see https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/getche-getwche?view=msvc-170
191 // > When _getche or _getwche reads a function key or an arrow key, the function must be called twice;
192 // > the first call returns 0 or 0xE0, and the second call returns the actual key code.
193 if res in [0, 0xe0] {
194 if C.kbhit() {
195 res2 := if params.echo {
196 C._getche()
197 } else {
198 C._getch()
199 }
200 return i64(u32(0xe0) << 16 | u32(res2))
201 }
202 }
203 return i64(res)
204 }
205 if !params.blocking {
206 // in non-blocking mode, we need to return immediately
207 return -1
208 }
209 time.sleep(1 * time.millisecond)
210 }
211 return 0
212}
213