Fix tailssh pixels

This commit is contained in:
世界
2026-08-30 17:41:46 +08:00
parent 0a5078a848
commit ec497c7dda
8 changed files with 61 additions and 32 deletions
+2 -2
View File
@@ -25,7 +25,7 @@ func OpenNativeShellSession(
cwd, cwd,
int(uid), int(gid), int(uid), int(gid),
common.Map(iteratorToArray[int32](groups), func(g int32) int { return int(g) }), common.Map(iteratorToArray[int32](groups), func(g int32) int { return int(g) }),
uint16(rows), uint16(cols), uint16(rows), uint16(cols), 0, 0,
) )
if err != nil { if err != nil {
return nil, err return nil, err
@@ -58,7 +58,7 @@ func (s *nativeShellSession) MasterFD() int32 {
} }
func (s *nativeShellSession) Resize(rows, cols int32) error { func (s *nativeShellSession) Resize(rows, cols int32) error {
return s.shell.Resize(uint16(rows), uint16(cols)) return s.shell.Resize(uint16(rows), uint16(cols), 0, 0)
} }
func (s *nativeShellSession) Signal(sig int32) error { func (s *nativeShellSession) Signal(sig int32) error {
+17 -8
View File
@@ -582,11 +582,13 @@ func (s *Server) handleSession(session gliderssh.Session) {
session.DisablePTYEmulation() session.DisablePTYEmulation()
command := session.RawCommand() command := session.RawCommand()
var term string var term string
var rows, cols uint16 var rows, cols, widthPixels, heightPixels uint16
if isPty { if isPty {
term = ptyReq.Term term = ptyReq.Term
rows = clampWindowDimension(ptyReq.Window.Height) rows = clampWindowDimension(ptyReq.Window.Height)
cols = clampWindowDimension(ptyReq.Window.Width) cols = clampWindowDimension(ptyReq.Window.Width)
widthPixels = clampWindowDimension(ptyReq.Window.WidthPixels)
heightPixels = clampWindowDimension(ptyReq.Window.HeightPixels)
} }
var rec *recording var rec *recording
recorderList, onFailure := recorders(connInfo) recorderList, onFailure := recorders(connInfo)
@@ -611,12 +613,14 @@ func (s *Server) handleSession(session gliderssh.Session) {
} }
} }
shellSession, err := s.backend.OpenSession(shellRequest{ shellSession, err := s.backend.OpenSession(shellRequest{
User: localUser, User: localUser,
Command: command, Command: command,
Env: env, Env: env,
Term: term, Term: term,
Rows: rows, Rows: rows,
Cols: cols, Cols: cols,
WidthPixels: widthPixels,
HeightPixels: heightPixels,
}) })
if err != nil { if err != nil {
s.logger.Error("failed to open shell session: ", err) s.logger.Error("failed to open shell session: ", err)
@@ -661,7 +665,12 @@ func (s *Server) handleSession(session gliderssh.Session) {
for win := range winCh { for win := range winCh {
shellAccess.Lock() shellAccess.Lock()
if shellAlive { if shellAlive {
shellSession.Resize(clampWindowDimension(win.Height), clampWindowDimension(win.Width)) shellSession.Resize(
clampWindowDimension(win.Height),
clampWindowDimension(win.Width),
clampWindowDimension(win.WidthPixels),
clampWindowDimension(win.HeightPixels),
)
} }
shellAccess.Unlock() shellAccess.Unlock()
} }
+9 -7
View File
@@ -14,12 +14,14 @@ type shellBackend interface {
} }
type shellRequest struct { type shellRequest struct {
User *adapter.PlatformUser User *adapter.PlatformUser
Command string Command string
Env []string Env []string
Term string Term string
Rows uint16 Rows uint16
Cols uint16 Cols uint16
WidthPixels uint16
HeightPixels uint16
} }
type shellSession interface { type shellSession interface {
@@ -27,7 +29,7 @@ type shellSession interface {
// CloseWrite signals EOF on the child's stdin without tearing down the // CloseWrite signals EOF on the child's stdin without tearing down the
// session, so programs that read stdin to EOF can finish normally. // session, so programs that read stdin to EOF can finish normally.
CloseWrite() error CloseWrite() error
Resize(rows, cols uint16) error Resize(rows, cols, widthPixels, heightPixels uint16) error
Signal(sig int) error Signal(sig int) error
Wait() (exitStatus uint32, err error) Wait() (exitStatus uint32, err error)
} }
+17 -4
View File
@@ -25,11 +25,15 @@ func (b *platformShellBackend) OpenSession(request shellRequest) (shellSession,
return nil, err return nil, err
} }
master := os.NewFile(uintptr(dupFd), "pty-master") master := os.NewFile(uintptr(dupFd), "pty-master")
return &platformShellSession{ shellSession := &platformShellSession{
session: session, session: session,
master: master, master: master,
isPty: request.Term != "", isPty: request.Term != "",
}, nil }
if shellSession.isPty && (request.WidthPixels > 0 || request.HeightPixels > 0) {
_ = SetWinsize(int(master.Fd()), request.Rows, request.Cols, request.WidthPixels, request.HeightPixels)
}
return shellSession, nil
} }
func (b *platformShellBackend) Close() error { func (b *platformShellBackend) Close() error {
@@ -61,8 +65,17 @@ func (s *platformShellSession) CloseWrite() error {
return syscall.Shutdown(int(s.master.Fd()), syscall.SHUT_WR) return syscall.Shutdown(int(s.master.Fd()), syscall.SHUT_WR)
} }
func (s *platformShellSession) Resize(rows, cols uint16) error { func (s *platformShellSession) Resize(rows, cols, widthPixels, heightPixels uint16) error {
return s.session.Resize(int32(rows), int32(cols)) err := s.session.Resize(int32(rows), int32(cols))
if err != nil {
return err
}
// The platform interface carries no pixel dimensions; set them directly
// on the duplicated pty master.
if s.isPty && (widthPixels > 0 || heightPixels > 0) {
return SetWinsize(int(s.master.Fd()), rows, cols, widthPixels, heightPixels)
}
return nil
} }
func (s *platformShellSession) Signal(sig int) error { func (s *platformShellSession) Signal(sig int) error {
+1 -1
View File
@@ -45,7 +45,7 @@ func (b *directShellBackend) OpenSession(request shellRequest) (shellSession, er
args = []string{"-" + filepath.Base(shell)} args = []string{"-" + filepath.Base(shell)}
} }
if request.Term != "" { if request.Term != "" {
return OpenPtyShell(shell, args, request.Env, request.User.HomeDir, request.User.Uid, request.User.Gid, request.User.Groups, request.Rows, request.Cols) return OpenPtyShell(shell, args, request.Env, request.User.HomeDir, request.User.Uid, request.User.Gid, request.User.Groups, request.Rows, request.Cols, request.WidthPixels, request.HeightPixels)
} }
return OpenSocketpairShell(shell, args, request.Env, request.User.HomeDir, request.User.Uid, request.User.Gid, request.User.Groups) return OpenSocketpairShell(shell, args, request.Env, request.User.HomeDir, request.User.Uid, request.User.Gid, request.User.Groups)
} }
@@ -325,7 +325,7 @@ func (s *conptyShellSession) Write(p []byte) (int, error) {
return s.input.Write(p) return s.input.Write(p)
} }
func (s *conptyShellSession) Resize(rows, cols uint16) error { func (s *conptyShellSession) Resize(rows, cols, _, _ uint16) error {
return s.console.Resize(windows.Coord{X: clampConsoleDimension(cols), Y: clampConsoleDimension(rows)}) return s.console.Resize(windows.Coord{X: clampConsoleDimension(cols), Y: clampConsoleDimension(rows)})
} }
@@ -451,7 +451,7 @@ func (s *pipeShellSession) Write(p []byte) (int, error) {
return s.stdin.Write(p) return s.stdin.Write(p)
} }
func (s *pipeShellSession) Resize(_, _ uint16) error { func (s *pipeShellSession) Resize(_, _, _, _ uint16) error {
return nil return nil
} }
+4 -4
View File
@@ -13,8 +13,8 @@ type Shell struct {
isPty bool isPty bool
} }
func OpenPtyShell(shell string, args, env []string, dir string, uid, gid int, groups []int, rows, cols uint16) (*Shell, error) { func OpenPtyShell(shell string, args, env []string, dir string, uid, gid int, groups []int, rows, cols, widthPixels, heightPixels uint16) (*Shell, error) {
master, process, err := StartPtyProcess(shell, args, env, dir, uid, gid, groups, rows, cols) master, process, err := StartPtyProcess(shell, args, env, dir, uid, gid, groups, rows, cols, widthPixels, heightPixels)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -52,11 +52,11 @@ func (s *Shell) Write(p []byte) (int, error) {
return s.master.Write(p) return s.master.Write(p)
} }
func (s *Shell) Resize(rows, cols uint16) error { func (s *Shell) Resize(rows, cols, widthPixels, heightPixels uint16) error {
if !s.isPty { if !s.isPty {
return nil return nil
} }
return SetWinsize(int(s.master.Fd()), rows, cols) return SetWinsize(int(s.master.Fd()), rows, cols, widthPixels, heightPixels)
} }
func (s *Shell) Signal(sig int) error { func (s *Shell) Signal(sig int) error {
@@ -14,7 +14,7 @@ import (
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
func StartPtyProcess(shell string, args, env []string, dir string, uid, gid int, groups []int, rows, cols uint16) (*os.File, *os.Process, error) { func StartPtyProcess(shell string, args, env []string, dir string, uid, gid int, groups []int, rows, cols, widthPixels, heightPixels uint16) (*os.File, *os.Process, error) {
cmd := exec.Command(shell) cmd := exec.Command(shell)
cmd.Args = args cmd.Args = args
cmd.Dir = dir cmd.Dir = dir
@@ -27,7 +27,7 @@ func StartPtyProcess(shell string, args, env []string, dir string, uid, gid int,
setCredential(attrs, uid, gid, groups) setCredential(attrs, uid, gid, groups)
var size *pty.Winsize var size *pty.Winsize
if rows > 0 && cols > 0 { if rows > 0 && cols > 0 {
size = &pty.Winsize{Rows: rows, Cols: cols} size = &pty.Winsize{Rows: rows, Cols: cols, X: widthPixels, Y: heightPixels}
} }
master, err := pty.StartWithAttrs(cmd, size, attrs) master, err := pty.StartWithAttrs(cmd, size, attrs)
if err != nil { if err != nil {
@@ -94,6 +94,11 @@ func setCredential(attr *syscall.SysProcAttr, uid, gid int, groups []int) {
attr.Credential = cred attr.Credential = cred
} }
func SetWinsize(fd int, rows, cols uint16) error { func SetWinsize(fd int, rows, cols, widthPixels, heightPixels uint16) error {
return unix.IoctlSetWinsize(fd, unix.TIOCSWINSZ, &unix.Winsize{Row: rows, Col: cols}) return unix.IoctlSetWinsize(fd, unix.TIOCSWINSZ, &unix.Winsize{
Row: rows,
Col: cols,
Xpixel: widthPixels,
Ypixel: heightPixels,
})
} }