github.com/xeptore/docker-cli@v20.10.14+incompatible/cli/command/container/tty.go (about)

     1  package container
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	gosignal "os/signal"
     8  	"runtime"
     9  	"time"
    10  
    11  	"github.com/docker/cli/cli/command"
    12  	"github.com/docker/docker/api/types"
    13  	"github.com/docker/docker/client"
    14  	"github.com/docker/docker/pkg/signal"
    15  	"github.com/sirupsen/logrus"
    16  )
    17  
    18  // resizeTtyTo resizes tty to specific height and width
    19  func resizeTtyTo(ctx context.Context, client client.ContainerAPIClient, id string, height, width uint, isExec bool) error {
    20  	if height == 0 && width == 0 {
    21  		return nil
    22  	}
    23  
    24  	options := types.ResizeOptions{
    25  		Height: height,
    26  		Width:  width,
    27  	}
    28  
    29  	var err error
    30  	if isExec {
    31  		err = client.ContainerExecResize(ctx, id, options)
    32  	} else {
    33  		err = client.ContainerResize(ctx, id, options)
    34  	}
    35  
    36  	if err != nil {
    37  		logrus.Debugf("Error resize: %s\r", err)
    38  	}
    39  	return err
    40  }
    41  
    42  // resizeTty is to resize the tty with cli out's tty size
    43  func resizeTty(ctx context.Context, cli command.Cli, id string, isExec bool) error {
    44  	height, width := cli.Out().GetTtySize()
    45  	return resizeTtyTo(ctx, cli.Client(), id, height, width, isExec)
    46  }
    47  
    48  // initTtySize is to init the tty's size to the same as the window, if there is an error, it will retry 5 times.
    49  func initTtySize(ctx context.Context, cli command.Cli, id string, isExec bool, resizeTtyFunc func(ctx context.Context, cli command.Cli, id string, isExec bool) error) {
    50  	rttyFunc := resizeTtyFunc
    51  	if rttyFunc == nil {
    52  		rttyFunc = resizeTty
    53  	}
    54  	if err := rttyFunc(ctx, cli, id, isExec); err != nil {
    55  		go func() {
    56  			var err error
    57  			for retry := 0; retry < 5; retry++ {
    58  				time.Sleep(10 * time.Millisecond)
    59  				if err = rttyFunc(ctx, cli, id, isExec); err == nil {
    60  					break
    61  				}
    62  			}
    63  			if err != nil {
    64  				fmt.Fprintln(cli.Err(), "failed to resize tty, using default size")
    65  			}
    66  		}()
    67  	}
    68  }
    69  
    70  // MonitorTtySize updates the container tty size when the terminal tty changes size
    71  func MonitorTtySize(ctx context.Context, cli command.Cli, id string, isExec bool) error {
    72  	initTtySize(ctx, cli, id, isExec, resizeTty)
    73  	if runtime.GOOS == "windows" {
    74  		go func() {
    75  			prevH, prevW := cli.Out().GetTtySize()
    76  			for {
    77  				time.Sleep(time.Millisecond * 250)
    78  				h, w := cli.Out().GetTtySize()
    79  
    80  				if prevW != w || prevH != h {
    81  					resizeTty(ctx, cli, id, isExec)
    82  				}
    83  				prevH = h
    84  				prevW = w
    85  			}
    86  		}()
    87  	} else {
    88  		sigchan := make(chan os.Signal, 1)
    89  		gosignal.Notify(sigchan, signal.SIGWINCH)
    90  		go func() {
    91  			for range sigchan {
    92  				resizeTty(ctx, cli, id, isExec)
    93  			}
    94  		}()
    95  	}
    96  	return nil
    97  }