Start work on proper TUI support
This commit is contained in:
parent
67f03621ae
commit
3c3e7b7f70
16
gen-rc-go.py
16
gen-rc-go.py
@ -3,10 +3,10 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
|
||||||
from typing import Dict, List, Tuple
|
from typing import Dict, List, Tuple
|
||||||
|
|
||||||
import kitty.constants as kc
|
import kitty.constants as kc
|
||||||
|
from kittens.tui.operations import Mode
|
||||||
from kitty.cli import OptionDict, OptionSpecSeq, parse_option_spec
|
from kitty.cli import OptionDict, OptionSpecSeq, parse_option_spec
|
||||||
from kitty.rc.base import RemoteCommand, all_command_names, command_for_name
|
from kitty.rc.base import RemoteCommand, all_command_names, command_for_name
|
||||||
|
|
||||||
@ -184,9 +184,6 @@ def build_go_code(name: str, cmd: RemoteCommand, seq: OptionSpecSeq, template: s
|
|||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
if 'prewarmed' in getattr(sys, 'kitty_run_data'):
|
|
||||||
os.environ.pop('KITTY_PREWARM_SOCKET')
|
|
||||||
os.execlp(sys.executable, sys.executable, '+launch', __file__, *sys.argv[1:])
|
|
||||||
with open('constants_generated.go', 'w') as f:
|
with open('constants_generated.go', 'w') as f:
|
||||||
dp = ", ".join(map(lambda x: f'"{serialize_as_go_string(x)}"', kc.default_pager_for_help))
|
dp = ", ".join(map(lambda x: f'"{serialize_as_go_string(x)}"', kc.default_pager_for_help))
|
||||||
f.write(f'''\
|
f.write(f'''\
|
||||||
@ -197,13 +194,14 @@ package kitty
|
|||||||
type VersionType struct {{
|
type VersionType struct {{
|
||||||
Major, Minor, Patch int
|
Major, Minor, Patch int
|
||||||
}}
|
}}
|
||||||
var VersionString string = "{kc.str_version}"
|
const VersionString string = "{kc.str_version}"
|
||||||
var WebsiteBaseURL string = "{kc.website_base_url}"
|
const WebsiteBaseURL string = "{kc.website_base_url}"
|
||||||
|
const VCSRevision string = ""
|
||||||
|
const RC_ENCRYPTION_PROTOCOL_VERSION string = "{kc.RC_ENCRYPTION_PROTOCOL_VERSION}"
|
||||||
|
const IsFrozenBuild bool = false
|
||||||
|
const HandleTermiosSignals = {Mode.HANDLE_TERMIOS_SIGNALS.value[0]}
|
||||||
var Version VersionType = VersionType{{Major: {kc.version.major}, Minor: {kc.version.minor}, Patch: {kc.version.patch},}}
|
var Version VersionType = VersionType{{Major: {kc.version.major}, Minor: {kc.version.minor}, Patch: {kc.version.patch},}}
|
||||||
var DefaultPager []string = []string{{ {dp} }}
|
var DefaultPager []string = []string{{ {dp} }}
|
||||||
var VCSRevision string = ""
|
|
||||||
var RC_ENCRYPTION_PROTOCOL_VERSION string = "{kc.RC_ENCRYPTION_PROTOCOL_VERSION}"
|
|
||||||
var IsFrozenBuild bool = false
|
|
||||||
''')
|
''')
|
||||||
with open('tools/cmd/at/template.go') as f:
|
with open('tools/cmd/at/template.go') as f:
|
||||||
template = f.read()
|
template = f.read()
|
||||||
|
|||||||
135
tools/tui/loop.go
Normal file
135
tools/tui/loop.go
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
package tui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"kitty/tools/tty"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TerminalState struct {
|
||||||
|
alternate_screen, grab_mouse bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type Loop struct {
|
||||||
|
controlling_term *tty.Term
|
||||||
|
keep_going bool
|
||||||
|
flush_write_buf bool
|
||||||
|
write_buf []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateLoop() (*Loop, error) {
|
||||||
|
l := Loop{controlling_term: nil}
|
||||||
|
return &l, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Loop) Run() (err error) {
|
||||||
|
signal_read_file, signal_write_file, err := os.Pipe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
signal_read_file.Close()
|
||||||
|
signal_write_file.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
sigchnl := make(chan os.Signal, 256)
|
||||||
|
reset_signals := notify_signals(sigchnl, SIGINT, SIGTERM, SIGTSTP, SIGHUP)
|
||||||
|
defer reset_signals()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
s := <-sigchnl
|
||||||
|
if write_signal(signal_write_file, s) != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
controlling_term, err := tty.OpenControllingTerm()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
self.controlling_term = controlling_term
|
||||||
|
defer func() {
|
||||||
|
self.controlling_term.RestoreAndClose()
|
||||||
|
self.controlling_term = nil
|
||||||
|
}()
|
||||||
|
err = self.controlling_term.ApplyOperations(tty.TCSANOW, tty.SetRaw)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var selector Select
|
||||||
|
selector.RegisterRead(int(signal_read_file.Fd()))
|
||||||
|
selector.RegisterRead(controlling_term.Fd())
|
||||||
|
|
||||||
|
self.keep_going = true
|
||||||
|
self.flush_write_buf = true
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if self.flush_write_buf {
|
||||||
|
self.flush()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
for self.keep_going {
|
||||||
|
num_ready, err := selector.WaitForever()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to call select() with error: %w", err)
|
||||||
|
}
|
||||||
|
if num_ready == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Loop) write() error {
|
||||||
|
if len(self.write_buf) == 0 || self.controlling_term == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
n, err := self.controlling_term.Write(self.write_buf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if n == 0 {
|
||||||
|
return io.EOF
|
||||||
|
}
|
||||||
|
remainder := self.write_buf[n:]
|
||||||
|
if len(remainder) > 0 {
|
||||||
|
self.write_buf = self.write_buf[:len(remainder)]
|
||||||
|
copy(self.write_buf, remainder)
|
||||||
|
} else {
|
||||||
|
self.write_buf = self.write_buf[:0]
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Loop) flush() error {
|
||||||
|
var selector Select
|
||||||
|
if self.controlling_term == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
selector.RegisterWrite(self.controlling_term.Fd())
|
||||||
|
deadline := time.Now().Add(2 * time.Second)
|
||||||
|
for len(self.write_buf) > 0 {
|
||||||
|
timeout := deadline.Sub(time.Now())
|
||||||
|
if timeout < 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
num_ready, err := selector.Wait(timeout)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if num_ready > 0 && selector.IsReadyToWrite(self.controlling_term.Fd()) {
|
||||||
|
err = self.write()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
95
tools/tui/select.go
Normal file
95
tools/tui/select.go
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
package tui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
"kitty/tools/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Select struct {
|
||||||
|
read_set, write_set, err_set unix.FdSet
|
||||||
|
read_fds, write_fds, err_fds map[int]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Select) register(fd int, fdset *map[int]bool) {
|
||||||
|
(*fdset)[fd] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Select) RegisterRead(fd int) {
|
||||||
|
self.register(fd, &self.read_fds)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Select) RegisterWrite(fd int) {
|
||||||
|
self.register(fd, &self.write_fds)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Select) RegisterError(fd int) {
|
||||||
|
self.register(fd, &self.err_fds)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Select) unregister(fd int, fdset *map[int]bool) {
|
||||||
|
(*fdset)[fd] = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Select) UnRegisterRead(fd int) {
|
||||||
|
self.unregister(fd, &self.read_fds)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Select) UnRegisterWrite(fd int) {
|
||||||
|
self.unregister(fd, &self.write_fds)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Select) UnRegisterError(fd int) {
|
||||||
|
self.unregister(fd, &self.err_fds)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Select) Wait(timeout time.Duration) (num_ready int, err error) {
|
||||||
|
self.read_set.Zero()
|
||||||
|
self.write_set.Zero()
|
||||||
|
self.err_set.Zero()
|
||||||
|
max_fd_num := 0
|
||||||
|
|
||||||
|
init_set := func(s *unix.FdSet, m *map[int]bool) {
|
||||||
|
s.Zero()
|
||||||
|
for fd, enabled := range *m {
|
||||||
|
if fd > -1 && enabled {
|
||||||
|
if max_fd_num < fd {
|
||||||
|
max_fd_num = fd
|
||||||
|
}
|
||||||
|
s.Set(fd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
init_set(&self.read_set, &self.read_fds)
|
||||||
|
init_set(&self.write_set, &self.write_fds)
|
||||||
|
init_set(&self.err_set, &self.err_fds)
|
||||||
|
num_ready, err = utils.Select(max_fd_num+1, &self.read_set, &self.write_set, &self.err_set, timeout)
|
||||||
|
if err == unix.EINTR {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Select) WaitForever() (num_ready int, err error) {
|
||||||
|
return self.Wait(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Select) IsReadyToRead(fd int) bool {
|
||||||
|
return fd > -1 && self.read_set.IsSet(fd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Select) IsReadyToWrite(fd int) bool {
|
||||||
|
return fd > -1 && self.write_set.IsSet(fd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Select) IsErrored(fd int) bool {
|
||||||
|
return fd > -1 && self.err_set.IsSet(fd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Select) UnregisterAll() {
|
||||||
|
self.read_fds = make(map[int]bool)
|
||||||
|
self.write_fds = make(map[int]bool)
|
||||||
|
self.err_fds = make(map[int]bool)
|
||||||
|
}
|
||||||
96
tools/tui/signal.go
Normal file
96
tools/tui/signal.go
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
package tui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Signal byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
SIGNULL Signal = 0
|
||||||
|
SIGINT Signal = 1
|
||||||
|
SIGTERM Signal = 2
|
||||||
|
SIGTSTP Signal = 3
|
||||||
|
SIGHUP Signal = 4
|
||||||
|
SIGTTIN Signal = 5
|
||||||
|
SIGTTOU Signal = 6
|
||||||
|
SIGUSR1 Signal = 7
|
||||||
|
SIGUSR2 Signal = 8
|
||||||
|
SIGALRM Signal = 9
|
||||||
|
)
|
||||||
|
|
||||||
|
func as_signal(which os.Signal) Signal {
|
||||||
|
switch which {
|
||||||
|
case os.Interrupt:
|
||||||
|
return SIGINT
|
||||||
|
case syscall.SIGTERM:
|
||||||
|
return SIGTERM
|
||||||
|
case syscall.SIGTSTP:
|
||||||
|
return SIGTSTP
|
||||||
|
case syscall.SIGHUP:
|
||||||
|
return SIGHUP
|
||||||
|
case syscall.SIGTTIN:
|
||||||
|
return SIGTTIN
|
||||||
|
case syscall.SIGTTOU:
|
||||||
|
return SIGTTOU
|
||||||
|
case syscall.SIGUSR1:
|
||||||
|
return SIGUSR1
|
||||||
|
case syscall.SIGUSR2:
|
||||||
|
return SIGUSR2
|
||||||
|
case syscall.SIGALRM:
|
||||||
|
return SIGALRM
|
||||||
|
default:
|
||||||
|
return SIGNULL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const zero_go_signal = syscall.Signal(0)
|
||||||
|
|
||||||
|
func as_go_signal(which Signal) os.Signal {
|
||||||
|
switch which {
|
||||||
|
case SIGINT:
|
||||||
|
return os.Interrupt
|
||||||
|
case SIGTERM:
|
||||||
|
return syscall.SIGTERM
|
||||||
|
case SIGTSTP:
|
||||||
|
return syscall.SIGTSTP
|
||||||
|
case SIGHUP:
|
||||||
|
return syscall.SIGHUP
|
||||||
|
case SIGTTIN:
|
||||||
|
return syscall.SIGTTIN
|
||||||
|
case SIGTTOU:
|
||||||
|
return syscall.SIGTTOU
|
||||||
|
case SIGUSR1:
|
||||||
|
return syscall.SIGUSR1
|
||||||
|
case SIGUSR2:
|
||||||
|
return syscall.SIGUSR2
|
||||||
|
case SIGALRM:
|
||||||
|
return syscall.SIGALRM
|
||||||
|
default:
|
||||||
|
return zero_go_signal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func write_signal(dest *os.File, which os.Signal) error {
|
||||||
|
b := make([]byte, 1)
|
||||||
|
b[0] = byte(as_signal(which))
|
||||||
|
if b[0] == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
_, err := dest.Write(b)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func notify_signals(c chan os.Signal, signals ...Signal) func() {
|
||||||
|
s := make([]os.Signal, len(signals))
|
||||||
|
for i, x := range signals {
|
||||||
|
g := as_go_signal(x)
|
||||||
|
if g != zero_go_signal {
|
||||||
|
s[i] = g
|
||||||
|
}
|
||||||
|
}
|
||||||
|
signal.Notify(c, s...)
|
||||||
|
return func() { signal.Reset(s...) }
|
||||||
|
}
|
||||||
142
tools/tui/terminal-state.go
Normal file
142
tools/tui/terminal-state.go
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
package tui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"kitty"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SAVE_CURSOR = "\0337"
|
||||||
|
RESTORE_CURSOR = "\0338"
|
||||||
|
S7C1T = "\033 F"
|
||||||
|
SAVE_PRIVATE_MODE_VALUES = "\033[?s"
|
||||||
|
RESTORE_PRIVATE_MODE_VALUES = "\033[?r"
|
||||||
|
SAVE_COLORS = "\033[#P"
|
||||||
|
RESTORE_COLORS = "\033[#Q"
|
||||||
|
DECSACE_DEFAULT_REGION_SELECT = "\033[*x"
|
||||||
|
CLEAR_SCREEN = "\033[H\033[2J"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Mode uint32
|
||||||
|
|
||||||
|
const private Mode = 1 << 31
|
||||||
|
|
||||||
|
const (
|
||||||
|
LNM Mode = 20
|
||||||
|
IRM = 4
|
||||||
|
DECKM = 1 | private
|
||||||
|
DECSCNM = 5 | private
|
||||||
|
DECOM = 6 | private
|
||||||
|
DECAWM = 7 | private
|
||||||
|
DECARM = 8 | private
|
||||||
|
DECTCEM = 25 | private
|
||||||
|
MOUSE_BUTTON_TRACKING = 1000 | private
|
||||||
|
MOUSE_MOTION_TRACKING = 1002 | private
|
||||||
|
MOUSE_MOVE_TRACKING = 1003 | private
|
||||||
|
FOCUS_TRACKING = 1004 | private
|
||||||
|
MOUSE_UTF8_MODE = 1005 | private
|
||||||
|
MOUSE_SGR_MODE = 1006 | private
|
||||||
|
MOUSE_URXVT_MODE = 1015 | private
|
||||||
|
MOUSE_SGR_PIXEL_MODE = 1016 | private
|
||||||
|
ALTERNATE_SCREEN = 1049 | private
|
||||||
|
BRACKETED_PASTE = 2004 | private
|
||||||
|
PENDING_UPDATE = 2026 | private
|
||||||
|
HANDLE_TERMIOS_SIGNALS = kitty.HandleTermiosSignals | private
|
||||||
|
)
|
||||||
|
|
||||||
|
func (self *Mode) escape_code(which string) string {
|
||||||
|
num := *self
|
||||||
|
priv := ""
|
||||||
|
if num&private > 0 {
|
||||||
|
priv = "?"
|
||||||
|
num &^= private
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("\033[%s%d%s", priv, uint32(num), which)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Mode) EscapeCodeToSet() string {
|
||||||
|
return self.escape_code("h")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Mode) EscapeCodeToReset() string {
|
||||||
|
return self.escape_code("h")
|
||||||
|
}
|
||||||
|
|
||||||
|
type MouseTracking uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
NO_MOUSE_TRACKING MouseTracking = iota
|
||||||
|
BUTTONS_ONLY_MOUSE_TRACKING
|
||||||
|
BUTTONS_AND_DRAG_MOUSE_TRACKING
|
||||||
|
FULL_MOUSE_TRACKING
|
||||||
|
)
|
||||||
|
|
||||||
|
type TerminalState struct {
|
||||||
|
alternate_screen, kitty_keyboard_mode bool
|
||||||
|
mouse_tracking MouseTracking
|
||||||
|
}
|
||||||
|
|
||||||
|
func set_modes(sb *strings.Builder, modes ...Mode) {
|
||||||
|
for _, m := range modes {
|
||||||
|
sb.WriteString(m.EscapeCodeToSet())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func reset_modes(sb *strings.Builder, modes ...Mode) {
|
||||||
|
for _, m := range modes {
|
||||||
|
sb.WriteString(m.EscapeCodeToReset())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *TerminalState) SetStateEscapeCodes() []byte {
|
||||||
|
var sb strings.Builder
|
||||||
|
sb.Grow(256)
|
||||||
|
sb.WriteString(S7C1T)
|
||||||
|
if self.alternate_screen {
|
||||||
|
sb.WriteString(SAVE_CURSOR)
|
||||||
|
}
|
||||||
|
sb.WriteString(SAVE_PRIVATE_MODE_VALUES)
|
||||||
|
sb.WriteString(SAVE_COLORS)
|
||||||
|
sb.WriteString(DECSACE_DEFAULT_REGION_SELECT)
|
||||||
|
reset_modes(&sb, IRM, DECKM, DECSCNM, MOUSE_BUTTON_TRACKING, MOUSE_MOTION_TRACKING,
|
||||||
|
MOUSE_MOVE_TRACKING, FOCUS_TRACKING, MOUSE_UTF8_MODE, MOUSE_SGR_MODE, BRACKETED_PASTE)
|
||||||
|
set_modes(&sb, DECARM, DECAWM, DECTCEM)
|
||||||
|
if self.alternate_screen {
|
||||||
|
set_modes(&sb, ALTERNATE_SCREEN)
|
||||||
|
sb.WriteString(CLEAR_SCREEN)
|
||||||
|
}
|
||||||
|
if self.kitty_keyboard_mode {
|
||||||
|
sb.WriteString("\033[>31u")
|
||||||
|
} else {
|
||||||
|
sb.WriteString("\033[>u")
|
||||||
|
}
|
||||||
|
if self.mouse_tracking != NO_MOUSE_TRACKING {
|
||||||
|
sb.WriteString(MOUSE_SGR_PIXEL_MODE.EscapeCodeToSet())
|
||||||
|
switch self.mouse_tracking {
|
||||||
|
case BUTTONS_ONLY_MOUSE_TRACKING:
|
||||||
|
sb.WriteString(MOUSE_BUTTON_TRACKING.EscapeCodeToSet())
|
||||||
|
case BUTTONS_AND_DRAG_MOUSE_TRACKING:
|
||||||
|
sb.WriteString(MOUSE_MOTION_TRACKING.EscapeCodeToSet())
|
||||||
|
case FULL_MOUSE_TRACKING:
|
||||||
|
sb.WriteString(MOUSE_MOVE_TRACKING.EscapeCodeToSet())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return []byte(sb.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *TerminalState) ResetStateData() []byte {
|
||||||
|
var sb strings.Builder
|
||||||
|
sb.Grow(64)
|
||||||
|
sb.WriteString("\033[<u")
|
||||||
|
if self.alternate_screen {
|
||||||
|
sb.WriteString(ALTERNATE_SCREEN.EscapeCodeToReset())
|
||||||
|
} else {
|
||||||
|
sb.WriteString(SAVE_CURSOR)
|
||||||
|
}
|
||||||
|
sb.WriteString(RESTORE_PRIVATE_MODE_VALUES)
|
||||||
|
sb.WriteString(RESTORE_CURSOR)
|
||||||
|
sb.WriteString(RESTORE_COLORS)
|
||||||
|
return []byte(sb.String())
|
||||||
|
}
|
||||||
@ -3,27 +3,15 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Select(nfd int, r *unix.FdSet, w *unix.FdSet, e *unix.FdSet, timeout time.Duration) (n int, err error) {
|
func Select(nfd int, r *unix.FdSet, w *unix.FdSet, e *unix.FdSet, timeout time.Duration) (n int, err error) {
|
||||||
deadline := time.Now().Add(timeout)
|
if timeout < 0 {
|
||||||
for {
|
return unix.Pselect(nfd, r, w, e, nil, nil)
|
||||||
t := deadline.Sub(time.Now())
|
|
||||||
if t < 0 {
|
|
||||||
t = 0
|
|
||||||
}
|
|
||||||
ts := NsecToTimespec(t)
|
|
||||||
q, qerr := unix.Pselect(nfd, r, w, w, &ts, nil)
|
|
||||||
if qerr == unix.EINTR {
|
|
||||||
if time.Now().After(deadline) {
|
|
||||||
return 0, os.ErrDeadlineExceeded
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return q, qerr
|
|
||||||
}
|
}
|
||||||
|
ts := NsecToTimespec(timeout)
|
||||||
|
return unix.Pselect(nfd, r, w, e, &ts, nil)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,20 +12,9 @@ import (
|
|||||||
// Go unix does not wrap pselect on darwin
|
// Go unix does not wrap pselect on darwin
|
||||||
|
|
||||||
func Select(nfd int, r *unix.FdSet, w *unix.FdSet, e *unix.FdSet, timeout time.Duration) (n int, err error) {
|
func Select(nfd int, r *unix.FdSet, w *unix.FdSet, e *unix.FdSet, timeout time.Duration) (n int, err error) {
|
||||||
deadline := time.Now().Add(timeout)
|
if timeout < 0 {
|
||||||
for {
|
return unix.Select(nfd, r, w, e, nil)
|
||||||
t := deadline.Sub(time.Now())
|
|
||||||
if t < 0 {
|
|
||||||
t = 0
|
|
||||||
}
|
|
||||||
ts := NsecToTimeval(t)
|
|
||||||
q, qerr := unix.Select(nfd, r, w, w, &ts)
|
|
||||||
if qerr == unix.EINTR {
|
|
||||||
if time.Now().After(deadline) {
|
|
||||||
return 0, os.ErrDeadlineExceeded
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return q, qerr
|
|
||||||
}
|
}
|
||||||
|
ts := NsecToTimeval(timeout)
|
||||||
|
return unix.Select(nfd, r, w, e, &ts)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user