console/raw_console

Code

main.odin ¶
92 linesSource

1package main
2
3import "core:fmt"
4import "core:io"
5import "core:os"
6import "core:time"
7import "core:unicode/utf8"
8
9get_password :: proc(allocator := context.allocator) -> string {
10
11	fmt.print("Enter password: ")
12
13	buf := make([dynamic]byte, allocator)
14	in_stream := os.to_stream(os.stdin)
15
16	for {
17		// Read a single character at a time.
18		ch, sz, err := io.read_rune(in_stream)
19		switch {
20		case err != nil:
21			fmt.eprintfln("\nError: %v", err)
22			os.exit(1)
23
24		// End line
25		case ch == '\n': // Posix
26			fallthrough
27		case ch == '\r': // Windows
28			fmt.println()
29			return string(buf[:])
30
31		// Backspace
32		case ch == '\u007f': // Posix
33			fallthrough
34		case ch == '\b':     // Windows
35			_, bs_sz := utf8.decode_last_rune(buf[:])	
36			if bs_sz > 0 {
37				resize(&buf, len(buf)-bs_sz)
38				// Replace last star with a space.
39				fmt.print("\b \b")
40			}
41		case:
42			bytes, _ := utf8.encode_rune(ch)
43			append(&buf, ..bytes[:sz])
44
45			fmt.print('*')
46		}
47	}
48}
49
50draw_progress_bar :: proc(title: string, percent: int, width := 25) {
51	fmt.printf("\r%v[", title, flush=false) // Put cursor back at the start of the line
52
53	done := percent * width / 100
54	left := width - done
55	for _ in 0..<done {
56		fmt.printf("|", flush=false)
57	}
58	for _ in 0..<left {
59		fmt.printf(" ", flush=false)
60	}
61	fmt.printf("] %d%%", percent)
62}
63
64main :: proc() {
65	set_utf8_terminal()
66
67	enable_raw_mode()
68	defer disable_raw_mode()
69
70	password := get_password()
71	defer delete(password)
72
73	for i in 0..=100 {
74		draw_progress_bar("Processing login: ", i)
75		time.sleep(50 * time.Millisecond)
76	}
77	fmt.println("\nDone")
78
79	fmt.printfln("\nYour password was: \"%s\"", password)
80}
81
82enable_raw_mode :: proc() {
83	_enable_raw_mode()
84}
85
86disable_raw_mode :: proc "c" () {
87	_disable_raw_mode()
88}
89
90set_utf8_terminal :: proc() {
91	_set_utf8_terminal()
92}

raw_posix.odin ¶ !windows
30 linesSource

1#+build !windows
2package main
3
4import psx "core:sys/posix"
5
6@(private="file")
7orig_mode: psx.termios
8
9_enable_raw_mode :: proc() {
10	// Get the original terminal attributes.
11	res := psx.tcgetattr(psx.STDIN_FILENO, &orig_mode)
12	assert(res == .OK)
13
14	// Reset to the original attributes at the end of the program.
15	psx.atexit(disable_raw_mode)
16
17	// Copy, and remove the
18	// ECHO (so what is typed is not shown) and
19	// ICANON (so we get each input instead of an entire line at once) flags.
20	raw := orig_mode
21	raw.c_lflag -= {.ECHO, .ICANON}
22	res = psx.tcsetattr(psx.STDIN_FILENO, .TCSANOW, &raw)
23	assert(res == .OK)
24}
25
26_disable_raw_mode :: proc "c" () {
27	psx.tcsetattr(psx.STDIN_FILENO, .TCSANOW, &orig_mode)
28}
29
30_set_utf8_terminal :: proc() {}

raw_windows.odin ¶
41 linesSource

1package main
2
3import       "core:c/libc"
4import win32 "core:sys/windows"
5
6@(private="file")
7orig_mode: win32.DWORD
8
9_enable_raw_mode :: proc() {
10	// Get a handle to the standard input.
11	stdin := win32.GetStdHandle(win32.STD_INPUT_HANDLE)
12	assert(stdin != win32.INVALID_HANDLE_VALUE)
13
14	// Get the original terminal mode.
15	ok := win32.GetConsoleMode(stdin, &orig_mode)
16	assert(ok == true)
17
18	// Reset to the original attributes at the end of the program.
19	libc.atexit(disable_raw_mode)
20
21	// Copy, and remove the
22	// ENABLE_ECHO_INPUT (so what is typed is not shown) and
23	// ENABLE_LINE_INPUT (so we get each input instead of an entire line at once) flags.
24	raw := orig_mode
25	raw &= ~win32.ENABLE_ECHO_INPUT
26	raw &= ~win32.ENABLE_LINE_INPUT
27	ok = win32.SetConsoleMode(stdin, raw)
28	assert(ok == true)
29}
30
31_disable_raw_mode :: proc "c" () {
32	stdin := win32.GetStdHandle(win32.STD_INPUT_HANDLE)
33	assert_contextless(stdin != win32.INVALID_HANDLE_VALUE)
34
35	win32.SetConsoleMode(stdin, orig_mode)
36}
37
38_set_utf8_terminal :: proc() {
39	win32.SetConsoleOutputCP(.UTF8)
40	win32.SetConsoleCP(.UTF8)
41}

Declarations Used 29