mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
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>
This commit is contained in:
parent
49c322f120
commit
3f7970db52
@ -14,7 +14,7 @@ fn main() {
|
|||||||
fn sleeping_line(x,y,size int, ch string) {
|
fn sleeping_line(x,y,size int, ch string) {
|
||||||
mut i := 0
|
mut i := 0
|
||||||
for i < size {
|
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)))
|
print(term.bold(term.yellow(ch)))
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
@ -23,7 +23,7 @@ fn sleeping_line(x,y,size int, ch string) {
|
|||||||
fn standing_line(x,y,size int, ch string) {
|
fn standing_line(x,y,size int, ch string) {
|
||||||
mut i := 0
|
mut i := 0
|
||||||
for i < size {
|
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)))
|
print(term.bold(term.yellow(ch)))
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
@ -451,7 +451,7 @@ fn (mut r Readline) switch_overwrite() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn (mut r Readline) clear_screen() {
|
fn (mut r Readline) clear_screen() {
|
||||||
term.set_cursor_position(1, 1)
|
term.set_cursor_position(x:1, y:1)
|
||||||
term.erase_clear()
|
term.erase_clear()
|
||||||
r.refresh_line()
|
r.refresh_line()
|
||||||
}
|
}
|
||||||
|
@ -15,9 +15,9 @@ import os
|
|||||||
fn main() {
|
fn main() {
|
||||||
term.clear() // clears the content in the terminal
|
term.clear() // clears the content in the terminal
|
||||||
width, height := term.get_terminal_size() // get the size of 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
|
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: ')
|
mut var := os.input('press q to quit: ')
|
||||||
// Keep prompting until the user presses the q key
|
// Keep prompting until the user presses the q key
|
||||||
for {
|
for {
|
||||||
@ -68,7 +68,7 @@ term.underline(string)
|
|||||||
term.bg_<color>(string)
|
term.bg_<color>(string)
|
||||||
|
|
||||||
// sets the position of the cursor at a given place in the terminal
|
// 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
|
// moves the cursor up
|
||||||
term.cursor_up()
|
term.cursor_up()
|
||||||
|
@ -13,8 +13,8 @@ module term
|
|||||||
// Setting cursor to the given position
|
// Setting cursor to the given position
|
||||||
// x is the x coordinate
|
// x is the x coordinate
|
||||||
// y is the y coordinate
|
// y is the y coordinate
|
||||||
pub fn set_cursor_position(x int, y int) {
|
pub fn set_cursor_position(c Coord) {
|
||||||
print('\x1b[$y;$x' + 'H')
|
print('\x1b[$c.y;$c.x' + 'H')
|
||||||
}
|
}
|
||||||
|
|
||||||
// n is number of cells
|
// n is number of cells
|
||||||
|
@ -6,6 +6,14 @@ const (
|
|||||||
default_columns_size = 80
|
default_columns_size = 80
|
||||||
default_rows_size = 25
|
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;
|
// can_show_color_on_stdout returns true if colors are allowed in stdout;
|
||||||
// returns false otherwise.
|
// returns false otherwise.
|
||||||
pub fn can_show_color_on_stdout() bool {
|
pub fn can_show_color_on_stdout() bool {
|
||||||
|
@ -3,9 +3,7 @@ module term
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
#include <termios.h> // TIOCGWINSZ
|
#include <termios.h> // TIOCGWINSZ
|
||||||
|
|
||||||
pub struct C.winsize {
|
pub struct C.winsize {
|
||||||
pub:
|
pub:
|
||||||
ws_row u16
|
ws_row u16
|
||||||
@ -17,11 +15,79 @@ pub:
|
|||||||
fn C.ioctl(fd int, request u64, arg voidptr) int
|
fn C.ioctl(fd int, request u64, arg voidptr) int
|
||||||
|
|
||||||
// get_terminal_size returns a number of colums and rows of terminal window.
|
// 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' {
|
if is_atty(1) <= 0 || os.getenv('TERM') == 'dumb' {
|
||||||
return default_columns_size, default_rows_size
|
return default_columns_size, default_rows_size
|
||||||
}
|
}
|
||||||
w := C.winsize{}
|
w := C.winsize{}
|
||||||
C.ioctl(1, C.TIOCGWINSZ, &w)
|
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
|
import term
|
||||||
|
|
||||||
fn test_get_terminal_size() {
|
fn test_get_terminal_size() {
|
||||||
cols,_ := term.get_terminal_size()
|
cols, _ := term.get_terminal_size()
|
||||||
assert cols > 0
|
assert cols > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,10 +49,54 @@ fn test_header() {
|
|||||||
eprintln(term.header('123', '_-/\\\/'))
|
eprintln(term.header('123', '_-/\\\/'))
|
||||||
eprintln(term.header('1234', '_-/\\/\\'))
|
eprintln(term.header('1234', '_-/\\/\\'))
|
||||||
eprintln(term.header('', '-'))
|
eprintln(term.header('', '-'))
|
||||||
*/
|
*/
|
||||||
assert term_width == empty_header.len
|
assert term_width == empty_header.len
|
||||||
assert term_width == short_header.len
|
assert term_width == short_header.len
|
||||||
assert term_width == very_long_header.len
|
assert term_width == very_long_header.len
|
||||||
assert term_width == very_long_header_2.len
|
assert term_width == very_long_header_2.len
|
||||||
assert term_width == term.header('1234', '_-/\\/\\').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
|
import os
|
||||||
|
|
||||||
struct Coord {
|
pub struct Coord16 {
|
||||||
|
pub:
|
||||||
x i16
|
x i16
|
||||||
y i16
|
y i16
|
||||||
}
|
}
|
||||||
@ -14,16 +15,22 @@ struct SmallRect {
|
|||||||
bottom i16
|
bottom i16
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// win: CONSOLE_SCREEN_BUFFER_INFO
|
||||||
|
// https://docs.microsoft.com/en-us/windows/console/console-screen-buffer-info-str
|
||||||
struct ConsoleScreenBufferInfo {
|
struct ConsoleScreenBufferInfo {
|
||||||
dw_size Coord
|
dw_size Coord16
|
||||||
dw_cursor_position Coord
|
dw_cursor_position Coord16
|
||||||
w_attributes u16
|
w_attributes u16
|
||||||
sr_window SmallRect
|
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
|
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.
|
// 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' {
|
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
|
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
Block a user