123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138 |
- // License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>
- package tui
- import (
- "errors"
- "fmt"
- "strings"
- "kitty/tools/tui/loop"
- "kitty/tools/wcswidth"
- )
- type KilledBySignal struct {
- Msg string
- SignalName string
- }
- func (self *KilledBySignal) Error() string { return self.Msg }
- var Canceled = errors.New("Canceled by user")
- func ReadPassword(prompt string, kill_if_signaled bool) (password string, err error) {
- lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.FullKeyboardProtocol)
- shadow := ""
- if err != nil {
- return
- }
- capspress_was_locked := false
- has_caps_lock := false
- redraw_prompt := func() {
- text := prompt + shadow
- lp.QueueWriteString("\r")
- lp.ClearToEndOfLine()
- if has_caps_lock {
- lp.QueueWriteString("\x1b[31m[CapsLock on!]\x1b[39m ")
- }
- lp.QueueWriteString(text)
- }
- lp.OnInitialize = func() (string, error) {
- lp.QueueWriteString(prompt)
- lp.SetCursorShape(loop.BAR_CURSOR, true)
- return "", nil
- }
- lp.OnFinalize = func() string {
- lp.SetCursorShape(loop.BLOCK_CURSOR, true)
- return "\r\n"
- }
- lp.OnText = func(text string, from_key_event bool, in_bracketed_paste bool) error {
- old_width := wcswidth.Stringwidth(password)
- password += text
- new_width := wcswidth.Stringwidth(password)
- if new_width > old_width {
- extra := strings.Repeat("*", new_width-old_width)
- lp.QueueWriteString(extra)
- shadow += extra
- }
- return nil
- }
- lp.OnKeyEvent = func(event *loop.KeyEvent) error {
- has_caps := false
- if strings.ToLower(event.Key) == "caps_lock" {
- if event.Type == loop.RELEASE {
- has_caps = !capspress_was_locked
- capspress_was_locked = false
- } else {
- capspress_was_locked = event.HasCapsLock()
- has_caps = true
- }
- } else {
- has_caps = event.HasCapsLock()
- }
- if has_caps_lock != has_caps {
- has_caps_lock = has_caps
- redraw_prompt()
- }
- if event.MatchesPressOrRepeat("backspace") || event.MatchesPressOrRepeat("delete") {
- event.Handled = true
- if len(password) > 0 {
- old_width := wcswidth.Stringwidth(password)
- password = password[:len(password)-1]
- new_width := wcswidth.Stringwidth(password)
- delta := old_width - new_width
- if delta > 0 {
- if delta > len(shadow) {
- delta = len(shadow)
- }
- shadow = shadow[:len(shadow)-delta]
- lp.QueueWriteString(strings.Repeat("\x08\x1b[P", delta))
- }
- } else {
- lp.Beep()
- }
- }
- if event.MatchesPressOrRepeat("enter") || event.MatchesPressOrRepeat("return") {
- event.Handled = true
- if password == "" {
- lp.Quit(1)
- } else {
- lp.Quit(0)
- }
- }
- if event.MatchesPressOrRepeat("esc") {
- event.Handled = true
- lp.Quit(1)
- return Canceled
- }
- return nil
- }
- lp.OnResumeFromStop = func() error {
- redraw_prompt()
- return nil
- }
- err = lp.Run()
- if err != nil {
- return
- }
- ds := lp.DeathSignalName()
- if ds != "" {
- if kill_if_signaled {
- lp.KillIfSignalled()
- return
- }
- return "", &KilledBySignal{Msg: fmt.Sprint("Killed by signal: ", ds), SignalName: ds}
- }
- if lp.ExitCode() != 0 {
- password = ""
- }
- return password, nil
- }
|