github.com/jspc/eggos@v0.5.1-0.20221028160421-556c75c878a5/console/console.go (about) 1 package console 2 3 import ( 4 "io" 5 "sync" 6 "syscall" 7 "unsafe" 8 9 "github.com/jspc/eggos/drivers/cga" 10 "github.com/jspc/eggos/drivers/kbd" 11 "github.com/jspc/eggos/drivers/uart" 12 ) 13 14 const ( 15 CON_BUFLEN = 128 16 ) 17 18 type console struct { 19 rawch chan byte 20 21 buf [CON_BUFLEN]byte 22 r, w, e uint 23 24 tios syscall.Termios 25 26 mutex sync.Mutex 27 notify *sync.Cond 28 29 wmutex sync.Mutex 30 } 31 32 var ( 33 con *console 34 ) 35 36 func newConsole() *console { 37 c := &console{ 38 tios: syscall.Termios{ 39 Lflag: syscall.ICANON | syscall.ECHO, 40 }, 41 } 42 c.notify = sync.NewCond(&c.mutex) 43 return c 44 } 45 46 func ctrl(c byte) byte { 47 return c - '@' 48 } 49 50 //go:nosplit 51 func (c *console) intr(ch byte) { 52 c.handleInput(ch) 53 } 54 55 func (c *console) rawmode() bool { 56 return c.tios.Lflag&syscall.ICANON == 0 57 } 58 59 func (c *console) handleRaw(ch byte) { 60 if c.e-c.r >= CON_BUFLEN { 61 return 62 } 63 idx := c.e % CON_BUFLEN 64 c.e++ 65 c.buf[idx] = byte(ch) 66 c.w = c.e 67 c.notify.Broadcast() 68 } 69 70 func (c *console) handleInput(ch byte) { 71 c.mutex.Lock() 72 defer c.mutex.Unlock() 73 if c.rawmode() { 74 c.handleRaw(ch) 75 return 76 } 77 78 switch ch { 79 case 0x7f, ctrl('H'): 80 if c.e > c.w { 81 c.e-- 82 c.putc(0x7f) 83 } 84 return 85 } 86 87 if c.e-c.r >= CON_BUFLEN { 88 return 89 } 90 if ch == '\r' { 91 ch = '\n' 92 } 93 idx := c.e % CON_BUFLEN 94 c.e++ 95 c.buf[idx] = byte(ch) 96 c.putc(ch) 97 if ch == '\n' || c.e == c.r+CON_BUFLEN { 98 c.w = c.e 99 c.notify.Broadcast() 100 } 101 } 102 103 func (c *console) loop() { 104 for ch := range c.rawch { 105 c.handleInput(ch) 106 } 107 } 108 109 func (c *console) putc(ch byte) { 110 uart.WriteByte(ch) 111 cga.WriteByte(ch) 112 } 113 114 func (c *console) read(p []byte) int { 115 c.mutex.Lock() 116 defer c.mutex.Unlock() 117 118 i := 0 119 for i < len(p) { 120 for c.r == c.w { 121 c.notify.Wait() 122 } 123 idx := c.r 124 c.r++ 125 ch := c.buf[idx%CON_BUFLEN] 126 p[i] = byte(ch) 127 i++ 128 if ch == '\n' || c.rawmode() { 129 break 130 } 131 } 132 return i 133 } 134 135 func (c *console) Read(p []byte) (int, error) { 136 return c.read(p), nil 137 } 138 139 func (c *console) Write(p []byte) (int, error) { 140 c.wmutex.Lock() 141 defer c.wmutex.Unlock() 142 for _, ch := range p { 143 c.putc(ch) 144 } 145 return len(p), nil 146 } 147 148 func (c *console) Ioctl(op, arg uintptr) error { 149 switch op { 150 case syscall.TIOCGWINSZ: 151 w := (*winSize)(unsafe.Pointer(arg)) 152 w.row = 25 153 w.col = 80 154 return nil 155 case syscall.TCGETS: 156 tios := (*syscall.Termios)(unsafe.Pointer(arg)) 157 *tios = c.tios 158 return nil 159 case syscall.TCSETS: 160 tios := (*syscall.Termios)(unsafe.Pointer(arg)) 161 c.tios = *tios 162 return nil 163 164 default: 165 return syscall.EINVAL 166 } 167 } 168 169 type winSize struct { 170 row, col uint16 171 xpixel, ypixel uint16 172 } 173 174 var cononce sync.Once 175 176 func Console() io.ReadWriter { 177 cononce.Do(func() { 178 con = newConsole() 179 }) 180 return con 181 } 182 183 func Init() { 184 cononce.Do(func() { 185 con = newConsole() 186 }) 187 uart.OnInput(con.intr) 188 kbd.OnInput(con.intr) 189 }