github.com/markusbkk/elvish@v0.0.0-20231204143114-91dc52438621/pkg/cli/term/reader_windows.go (about)

     1  package term
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"log"
     7  	"os"
     8  	"sync"
     9  
    10  	"golang.org/x/sys/windows"
    11  
    12  	"github.com/markusbkk/elvish/pkg/sys/ewindows"
    13  	"github.com/markusbkk/elvish/pkg/ui"
    14  )
    15  
    16  type reader struct {
    17  	console   windows.Handle
    18  	stopEvent windows.Handle
    19  	// A mutex that is held during ReadEvent.
    20  	mutex sync.Mutex
    21  }
    22  
    23  // Creates a new Reader instance.
    24  func newReader(file *os.File) Reader {
    25  	console, err := windows.GetStdHandle(windows.STD_INPUT_HANDLE)
    26  	if err != nil {
    27  		panic(fmt.Errorf("GetStdHandle(STD_INPUT_HANDLE): %v", err))
    28  	}
    29  	stopEvent, err := windows.CreateEvent(nil, 0, 0, nil)
    30  	if err != nil {
    31  		panic(fmt.Errorf("CreateEvent: %v", err))
    32  	}
    33  	return &reader{console: console, stopEvent: stopEvent}
    34  }
    35  
    36  func (r *reader) ReadEvent() (Event, error) {
    37  	r.mutex.Lock()
    38  	defer r.mutex.Unlock()
    39  	handles := []windows.Handle{r.console, r.stopEvent}
    40  	for {
    41  		triggered, _, err := ewindows.WaitForMultipleObjects(handles, false, ewindows.INFINITE)
    42  		if err != nil {
    43  			return nil, err
    44  		}
    45  		if triggered == 1 {
    46  			return nil, ErrStopped
    47  		}
    48  
    49  		var buf [1]ewindows.InputRecord
    50  		nr, err := ewindows.ReadConsoleInput(r.console, buf[:])
    51  		if nr == 0 {
    52  			return nil, io.ErrNoProgress
    53  		}
    54  		if err != nil {
    55  			return nil, err
    56  		}
    57  		event := convertEvent(buf[0].GetEvent())
    58  		if event != nil {
    59  			return event, nil
    60  		}
    61  		// Got an event that should be ignored; keep going.
    62  	}
    63  }
    64  
    65  func (r *reader) ReadRawEvent() (Event, error) {
    66  	return r.ReadEvent()
    67  }
    68  
    69  func (r *reader) Close() {
    70  	err := windows.SetEvent(r.stopEvent)
    71  	if err != nil {
    72  		log.Println("SetEvent:", err)
    73  	}
    74  	r.mutex.Lock()
    75  	r.mutex.Unlock()
    76  	err = windows.CloseHandle(r.stopEvent)
    77  	if err != nil {
    78  		log.Println("Closing stopEvent handle for reader:", err)
    79  	}
    80  }
    81  
    82  // A subset of virtual key codes listed in
    83  // https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
    84  var keyCodeToRune = map[uint16]rune{
    85  	0x08: ui.Backspace, 0x09: ui.Tab,
    86  	0x0d: ui.Enter,
    87  	0x1b: '\x1b',
    88  	0x20: ' ',
    89  	0x23: ui.End, 0x24: ui.Home,
    90  	0x25: ui.Left, 0x26: ui.Up, 0x27: ui.Right, 0x28: ui.Down,
    91  	0x2d: ui.Insert, 0x2e: ui.Delete,
    92  	/* 0x30 - 0x39: digits, same with ASCII */
    93  	/* 0x41 - 0x5a: letters, same with ASCII */
    94  	/* 0x60 - 0x6f: numpads; currently ignored */
    95  	0x70: ui.F1, 0x71: ui.F2, 0x72: ui.F3, 0x73: ui.F4, 0x74: ui.F5, 0x75: ui.F6,
    96  	0x76: ui.F7, 0x77: ui.F8, 0x78: ui.F9, 0x79: ui.F10, 0x7a: ui.F11, 0x7b: ui.F12,
    97  	/* 0x7c - 0x87: F13 - F24; currently ignored */
    98  	0xba: ';', 0xbb: '=', 0xbc: ',', 0xbd: '-', 0xbe: '.', 0xbf: '/', 0xc0: '`',
    99  	0xdb: '[', 0xdc: '\\', 0xdd: ']', 0xde: '\'',
   100  }
   101  
   102  // A subset of constants listed in
   103  // https://docs.microsoft.com/en-us/windows/console/key-event-record-str
   104  const (
   105  	leftAlt   = 0x02
   106  	leftCtrl  = 0x08
   107  	rightAlt  = 0x01
   108  	rightCtrl = 0x04
   109  	shift     = 0x10
   110  )
   111  
   112  // Converts the native ewindows.InputEvent type to a suitable Event type. It returns
   113  // nil if the event should be ignored.
   114  func convertEvent(event ewindows.InputEvent) Event {
   115  	switch event := event.(type) {
   116  	case *ewindows.KeyEvent:
   117  		if event.BKeyDown == 0 {
   118  			// Ignore keyup events.
   119  			return nil
   120  		}
   121  		r := rune(event.UChar[0]) + rune(event.UChar[1])<<8
   122  		filteredMod := event.DwControlKeyState & (leftAlt | leftCtrl | rightAlt | rightCtrl | shift)
   123  		if r >= 0x20 && r != 0x7f {
   124  			// This key inputs a character. The flags present in
   125  			// DwControlKeyState might indicate modifier keys that are needed to
   126  			// input this character (e.g. the Shift key when inputting 'A'), or
   127  			// modifier keys that are pressed in addition (e.g. the Alt key when
   128  			// pressing Alt-A). There doesn't seem to be an easy way to tell
   129  			// which is the case, so we rely on heuristics derived from
   130  			// real-world observations.
   131  			if filteredMod == 0 {
   132  				// TODO: Handle surrogate pairs
   133  				return KeyEvent(ui.Key{Rune: r})
   134  			} else if filteredMod == shift {
   135  				// A lone Shift seems to be always part of the character.
   136  				return KeyEvent(ui.Key{Rune: r})
   137  			} else if filteredMod == leftCtrl|rightAlt || filteredMod == leftCtrl|rightAlt|shift {
   138  				// The combination leftCtrl|rightAlt is used to represent AltGr.
   139  				// Furthermore, when the actual left Ctrl and right Alt are used
   140  				// together, the UChar field seems to be always 0; so if we are
   141  				// here, we can actually be sure that it's AltGr.
   142  				//
   143  				// Some characters require AltGr+Shift to input, such as the
   144  				// upper-case sharp S on a German keyboard.
   145  				return KeyEvent(ui.Key{Rune: r})
   146  			}
   147  		}
   148  		mod := convertMod(filteredMod)
   149  		if mod == 0 && event.WVirtualKeyCode == 0x1b {
   150  			// Special case: Normalize 0x1b to Ctrl-[.
   151  			//
   152  			// TODO(xiaq): This is Unix-centric. Maybe the normalized form
   153  			// should be Escape.
   154  			return KeyEvent(ui.Key{Rune: '[', Mod: ui.Ctrl})
   155  		}
   156  		r = convertRune(event.WVirtualKeyCode, mod)
   157  		if r == 0 {
   158  			return nil
   159  		}
   160  		return KeyEvent(ui.Key{Rune: r, Mod: mod})
   161  	default:
   162  		// Other events are ignored.
   163  		return nil
   164  	}
   165  }
   166  
   167  func convertRune(keyCode uint16, mod ui.Mod) rune {
   168  	r, ok := keyCodeToRune[keyCode]
   169  	if ok {
   170  		return r
   171  	}
   172  	if '0' <= keyCode && keyCode <= '9' {
   173  		return rune(keyCode)
   174  	}
   175  	if 'A' <= keyCode && keyCode <= 'Z' {
   176  		// If Ctrl is involved, emulate UNIX's convention and use upper case;
   177  		// otherwise use lower case.
   178  		//
   179  		// TODO(xiaq): This is quite Unix-centric. Maybe we should make the
   180  		// base rune case-insensitive when there are modifiers involved.
   181  		if mod&ui.Ctrl != 0 {
   182  			return rune(keyCode)
   183  		}
   184  		return rune(keyCode - 'A' + 'a')
   185  	}
   186  	return 0
   187  }
   188  
   189  func convertMod(state uint32) ui.Mod {
   190  	mod := ui.Mod(0)
   191  	if state&(leftAlt|rightAlt) != 0 {
   192  		mod |= ui.Alt
   193  	}
   194  	if state&(leftCtrl|rightCtrl) != 0 {
   195  		mod |= ui.Ctrl
   196  	}
   197  	if state&shift != 0 {
   198  		mod |= ui.Shift
   199  	}
   200  	return mod
   201  }