term: add get_cursor_position and set_terminal_title (#6279)
* added functions added: - get_cursor_position() - set_terminal_title(title string) * implement term.get_cursor_position and term.set_terminal_title on unix * Cleanup * make x,y fields of term.Coord mutable * fix vrepl compilation * use more descriptive var names in term_test.v * do not change the current terminal title in dumb terminals; do not test term.set_terminal_title outside of CI * unix: in term.set_terminal_title, return true even for dumb terminals Co-authored-by: Brent Pryer <brent@pryermachine.com> Co-authored-by: Delyan Angelov <delian66@gmail.com>pull/6337/head
parent
49c322f120
commit
3f7970db52
|
@ -14,7 +14,7 @@ fn main() {
|
|||
fn sleeping_line(x,y,size int, ch string) {
|
||||
mut i := 0
|
||||
for i < size {
|
||||
term.set_cursor_position(x+i,y)
|
||||
term.set_cursor_position(x: x+i, y: y)
|
||||
print(term.bold(term.yellow(ch)))
|
||||
i++
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ fn sleeping_line(x,y,size int, ch string) {
|
|||
fn standing_line(x,y,size int, ch string) {
|
||||
mut i := 0
|
||||
for i < size {
|
||||
term.set_cursor_position(x,y+i)
|
||||
term.set_cursor_position(x: x, y: y+i)
|
||||
print(term.bold(term.yellow(ch)))
|
||||
i++
|
||||
}
|
||||
|
|
|
@ -451,7 +451,7 @@ fn (mut r Readline) switch_overwrite() {
|
|||
}
|
||||
|
||||
fn (mut r Readline) clear_screen() {
|
||||
term.set_cursor_position(1, 1)
|
||||
term.set_cursor_position(x:1, y:1)
|
||||
term.erase_clear()
|
||||
r.refresh_line()
|
||||
}
|
||||
|
|
|
@ -15,9 +15,9 @@ import os
|
|||
fn main() {
|
||||
term.clear() // clears the content in the terminal
|
||||
width, height := term.get_terminal_size() // get the size of the terminal
|
||||
term.set_cursor_position(width / 2, height / 2) // now we point the cursor to the middle of the terminal
|
||||
term.set_cursor_position(x: width / 2, y: height / 2) // now we point the cursor to the middle of the terminal
|
||||
println(term.strikethrough(term.bright_green("hello world"))) // Print green text
|
||||
term.set_cursor_position(0, height) // Sets the position of the cursor to the bottom of the terminal
|
||||
term.set_cursor_position(x: 0, y: height) // Sets the position of the cursor to the bottom of the terminal
|
||||
mut var := os.input('press q to quit: ')
|
||||
// Keep prompting until the user presses the q key
|
||||
for {
|
||||
|
@ -68,7 +68,7 @@ term.underline(string)
|
|||
term.bg_<color>(string)
|
||||
|
||||
// sets the position of the cursor at a given place in the terminal
|
||||
term.set_cursor_position(x,y)
|
||||
term.set_cursor_position(term.Coord)
|
||||
|
||||
// moves the cursor up
|
||||
term.cursor_up()
|
||||
|
|
|
@ -13,8 +13,8 @@ module term
|
|||
// Setting cursor to the given position
|
||||
// x is the x coordinate
|
||||
// y is the y coordinate
|
||||
pub fn set_cursor_position(x int, y int) {
|
||||
print('\x1b[$y;$x' + 'H')
|
||||
pub fn set_cursor_position(c Coord) {
|
||||
print('\x1b[$c.y;$c.x' + 'H')
|
||||
}
|
||||
|
||||
// n is number of cells
|
||||
|
|
|
@ -6,6 +6,14 @@ const (
|
|||
default_columns_size = 80
|
||||
default_rows_size = 25
|
||||
)
|
||||
|
||||
// Coord - used by term.get_cursor_position and term.set_cursor_position
|
||||
pub struct Coord {
|
||||
pub mut:
|
||||
x int
|
||||
y int
|
||||
}
|
||||
|
||||
// can_show_color_on_stdout returns true if colors are allowed in stdout;
|
||||
// returns false otherwise.
|
||||
pub fn can_show_color_on_stdout() bool {
|
||||
|
|
|
@ -3,9 +3,7 @@ module term
|
|||
import os
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <termios.h> // TIOCGWINSZ
|
||||
|
||||
pub struct C.winsize {
|
||||
pub:
|
||||
ws_row u16
|
||||
|
@ -17,11 +15,79 @@ pub:
|
|||
fn C.ioctl(fd int, request u64, arg voidptr) int
|
||||
|
||||
// get_terminal_size returns a number of colums and rows of terminal window.
|
||||
pub fn get_terminal_size() (int,int) {
|
||||
pub fn get_terminal_size() (int, int) {
|
||||
if is_atty(1) <= 0 || os.getenv('TERM') == 'dumb' {
|
||||
return default_columns_size, default_rows_size
|
||||
}
|
||||
w := C.winsize{}
|
||||
C.ioctl(1, C.TIOCGWINSZ, &w)
|
||||
return int(w.ws_col),int(w.ws_row)
|
||||
return int(w.ws_col), int(w.ws_row)
|
||||
}
|
||||
|
||||
// get_cursor_position returns a Coord containing the current cursor position
|
||||
pub fn get_cursor_position() Coord {
|
||||
if is_atty(1) <= 0 || os.getenv('TERM') == 'dumb' {
|
||||
return Coord{
|
||||
x: 0
|
||||
y: 0
|
||||
}
|
||||
}
|
||||
// TODO: use termios.h, C.tcgetattr & C.tcsetattr directly,
|
||||
// instead of using `stty`
|
||||
oldsettings := os.exec('stty -g') or {
|
||||
os.Result{}
|
||||
}
|
||||
os.system('stty -echo -icanon time 0')
|
||||
print('\033[6n')
|
||||
mut ch := int(0)
|
||||
mut i := 0
|
||||
// ESC [ YYY `;` XXX `R`
|
||||
mut reading_x := false
|
||||
mut reading_y := false
|
||||
mut x := 0
|
||||
mut y := 0
|
||||
for {
|
||||
ch = C.getchar()
|
||||
b := byte(ch)
|
||||
i++
|
||||
assert i < 15
|
||||
// state management:
|
||||
if b == `R` {
|
||||
break
|
||||
}
|
||||
if b == `[` {
|
||||
reading_y = true
|
||||
reading_x = false
|
||||
continue
|
||||
}
|
||||
if b == `;` {
|
||||
reading_y = false
|
||||
reading_x = true
|
||||
continue
|
||||
}
|
||||
// converting string vals to ints:
|
||||
if reading_x {
|
||||
x *= 10
|
||||
x += (b - byte(`0`))
|
||||
}
|
||||
if reading_y {
|
||||
y *= 10
|
||||
y += (b - byte(`0`))
|
||||
}
|
||||
}
|
||||
// restore the old terminal settings:
|
||||
os.system('stty $oldsettings.output')
|
||||
return Coord{
|
||||
x: x
|
||||
y: y
|
||||
}
|
||||
}
|
||||
|
||||
// set_terminal_title change the terminal title
|
||||
pub fn set_terminal_title(title string) bool {
|
||||
if is_atty(1) <= 0 || os.getenv('TERM') == 'dumb' {
|
||||
return true
|
||||
}
|
||||
print('\033]0;${title}\007')
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import os
|
||||
import term
|
||||
|
||||
fn test_get_terminal_size() {
|
||||
cols,_ := term.get_terminal_size()
|
||||
cols, _ := term.get_terminal_size()
|
||||
assert cols > 0
|
||||
}
|
||||
|
||||
|
@ -48,10 +49,54 @@ fn test_header() {
|
|||
eprintln(term.header('123', '_-/\\\/'))
|
||||
eprintln(term.header('1234', '_-/\\/\\'))
|
||||
eprintln(term.header('', '-'))
|
||||
*/
|
||||
*/
|
||||
assert term_width == empty_header.len
|
||||
assert term_width == short_header.len
|
||||
assert term_width == very_long_header.len
|
||||
assert term_width == very_long_header_2.len
|
||||
assert term_width == term.header('1234', '_-/\\/\\').len
|
||||
}
|
||||
|
||||
fn test_get_cursor_position() {
|
||||
original_position := term.get_cursor_position()
|
||||
cursor_position_1 := term.get_cursor_position()
|
||||
assert original_position.x == cursor_position_1.x
|
||||
assert original_position.y == cursor_position_1.y
|
||||
//
|
||||
term.set_cursor_position({
|
||||
x: 10
|
||||
y: 11
|
||||
})
|
||||
cursor_position_2 := term.get_cursor_position()
|
||||
//
|
||||
term.set_cursor_position({
|
||||
x: 5
|
||||
y: 6
|
||||
})
|
||||
cursor_position_3 := term.get_cursor_position()
|
||||
//
|
||||
term.set_cursor_position(original_position)
|
||||
eprintln('original_position: $original_position')
|
||||
eprintln('cursor_position_2: $cursor_position_2')
|
||||
eprintln('cursor_position_3: $cursor_position_3')
|
||||
// 0,0 is returned on dumb terminals
|
||||
if cursor_position_2.x == 0 && cursor_position_2.y == 0 {
|
||||
return
|
||||
}
|
||||
if cursor_position_3.x == 0 && cursor_position_3.y == 0 {
|
||||
return
|
||||
}
|
||||
assert cursor_position_2.x == 10
|
||||
assert cursor_position_2.y == 11
|
||||
assert cursor_position_3.x == 5
|
||||
assert cursor_position_3.y == 6
|
||||
}
|
||||
|
||||
fn test_set_terminal_title() {
|
||||
// do not change the current terminal title outside of CI:
|
||||
if os.getenv('CI') != 'true' {
|
||||
return
|
||||
}
|
||||
title_change := term.set_terminal_title('v is awesome!')
|
||||
assert title_change == true
|
||||
}
|
||||
|
|
|
@ -2,7 +2,8 @@ module term
|
|||
|
||||
import os
|
||||
|
||||
struct Coord {
|
||||
pub struct Coord16 {
|
||||
pub:
|
||||
x i16
|
||||
y i16
|
||||
}
|
||||
|
@ -14,16 +15,22 @@ struct SmallRect {
|
|||
bottom i16
|
||||
}
|
||||
|
||||
// win: CONSOLE_SCREEN_BUFFER_INFO
|
||||
// https://docs.microsoft.com/en-us/windows/console/console-screen-buffer-info-str
|
||||
struct ConsoleScreenBufferInfo {
|
||||
dw_size Coord
|
||||
dw_cursor_position Coord
|
||||
dw_size Coord16
|
||||
dw_cursor_position Coord16
|
||||
w_attributes u16
|
||||
sr_window SmallRect
|
||||
dw_maximum_window_size Coord
|
||||
dw_maximum_window_size Coord16
|
||||
}
|
||||
|
||||
// ref - https://docs.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo
|
||||
fn C.GetConsoleScreenBufferInfo(handle os.HANDLE, info &ConsoleScreenBufferInfo) bool
|
||||
|
||||
// ref - https://docs.microsoft.com/en-us/windows/console/setconsoletitle
|
||||
fn C.SetConsoleTitle(title &u16) bool
|
||||
|
||||
// get_terminal_size returns a number of colums and rows of terminal window.
|
||||
pub fn get_terminal_size() (int, int) {
|
||||
if is_atty(1) > 0 && os.getenv('TERM') != 'dumb' {
|
||||
|
@ -36,3 +43,22 @@ pub fn get_terminal_size() (int, int) {
|
|||
}
|
||||
return default_columns_size, default_rows_size
|
||||
}
|
||||
|
||||
// get_cursor_position returns a Coord containing the current cursor position
|
||||
pub fn get_cursor_position() Coord {
|
||||
mut res := Coord{}
|
||||
if is_atty(1) > 0 && os.getenv('TERM') != 'dumb' {
|
||||
info := ConsoleScreenBufferInfo{}
|
||||
if C.GetConsoleScreenBufferInfo(C.GetStdHandle(C.STD_OUTPUT_HANDLE), &info) {
|
||||
res.x = info.dw_cursor_position.x
|
||||
res.y = info.dw_cursor_position.y
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// set_terminal_title change the terminal title
|
||||
pub fn set_terminal_title(title string) bool {
|
||||
title_change := C.SetConsoleTitle(title.to_wide())
|
||||
return title_change
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue