1616package monitor
1717
1818import (
19+ "bytes"
1920 "context"
2021 "errors"
2122 "fmt"
@@ -35,29 +36,34 @@ import (
3536 "github.com/fatih/color"
3637 "github.com/sirupsen/logrus"
3738 "github.com/spf13/cobra"
39+ "go.bug.st/cleanup"
3840)
3941
40- var (
41- portArgs arguments.Port
42- describe bool
43- configs []string
44- quiet bool
45- fqbn arguments.Fqbn
46- tr = i18n .Tr
47- )
42+ var tr = i18n .Tr
4843
4944// NewCommand created a new `monitor` command
5045func NewCommand () * cobra.Command {
46+ var (
47+ raw bool
48+ portArgs arguments.Port
49+ describe bool
50+ configs []string
51+ quiet bool
52+ fqbn arguments.Fqbn
53+ )
5154 monitorCommand := & cobra.Command {
5255 Use : "monitor" ,
5356 Short : tr ("Open a communication port with a board." ),
5457 Long : tr ("Open a communication port with a board." ),
5558 Example : "" +
5659 " " + os .Args [0 ] + " monitor -p /dev/ttyACM0\n " +
5760 " " + os .Args [0 ] + " monitor -p /dev/ttyACM0 --describe" ,
58- Run : runMonitorCmd ,
61+ Run : func (cmd * cobra.Command , args []string ) {
62+ runMonitorCmd (& portArgs , & fqbn , configs , describe , quiet , raw )
63+ },
5964 }
6065 portArgs .AddToCommand (monitorCommand )
66+ monitorCommand .Flags ().BoolVar (& raw , "raw" , false , tr ("Set terminal in raw mode (unbuffered)." ))
6167 monitorCommand .Flags ().BoolVar (& describe , "describe" , false , tr ("Show all the settings of the communication port." ))
6268 monitorCommand .Flags ().StringSliceVarP (& configs , "config" , "c" , []string {}, tr ("Configure communication port settings. The format is <ID>=<value>[,<ID>=<value>]..." ))
6369 monitorCommand .Flags ().BoolVarP (& quiet , "quiet" , "q" , false , tr ("Run in silent mode, show only monitor input and output." ))
@@ -66,7 +72,7 @@ func NewCommand() *cobra.Command {
6672 return monitorCommand
6773}
6874
69- func runMonitorCmd (cmd * cobra. Command , args []string ) {
75+ func runMonitorCmd (portArgs * arguments. Port , fqbn * arguments. Fqbn , configs []string , describe , quiet , raw bool ) {
7076 instance := instance .CreateAndInit ()
7177 logrus .Info ("Executing `arduino-cli monitor`" )
7278
@@ -93,12 +99,6 @@ func runMonitorCmd(cmd *cobra.Command, args []string) {
9399 return
94100 }
95101
96- tty , err := newStdInOutTerminal ()
97- if err != nil {
98- feedback .FatalError (err , feedback .ErrGeneric )
99- }
100- defer tty .Close ()
101-
102102 configuration := & rpc.MonitorPortConfiguration {}
103103 if len (configs ) > 0 {
104104 for _ , config := range configs {
@@ -151,9 +151,33 @@ func runMonitorCmd(cmd *cobra.Command, args []string) {
151151 }
152152 defer portProxy .Close ()
153153
154- ctx , cancel := context .WithCancel (context .Background ())
154+ if ! quiet {
155+ feedback .Print (tr ("Connected to %s! Press CTRL-C to exit." , portAddress ))
156+ }
157+
158+ ttyIn , ttyOut , err := feedback .InteractiveStreams ()
159+ if err != nil {
160+ feedback .FatalError (err , feedback .ErrGeneric )
161+ }
162+
163+ ctx , cancel := cleanup .InterruptableContext (context .Background ())
164+ if raw {
165+ feedback .SetRawModeStdin ()
166+ defer func () {
167+ feedback .RestoreModeStdin ()
168+ }()
169+
170+ // In RAW mode CTRL-C is not converted into an Interrupt by
171+ // the terminal, we must intercept ASCII 3 (CTRL-C) on our own...
172+ ctrlCDetector := & charDetectorWriter {
173+ callback : cancel ,
174+ detectedChar : 3 , // CTRL-C
175+ }
176+ ttyIn = io .TeeReader (ttyIn , ctrlCDetector )
177+ }
178+
155179 go func () {
156- _ , err := io .Copy (tty , portProxy )
180+ _ , err := io .Copy (ttyOut , portProxy )
157181 if err != nil && ! errors .Is (err , io .EOF ) {
158182 if ! quiet {
159183 feedback .Print (tr ("Port closed: %v" , err ))
@@ -162,7 +186,7 @@ func runMonitorCmd(cmd *cobra.Command, args []string) {
162186 cancel ()
163187 }()
164188 go func () {
165- _ , err := io .Copy (portProxy , tty )
189+ _ , err := io .Copy (portProxy , ttyIn )
166190 if err != nil && ! errors .Is (err , io .EOF ) {
167191 if ! quiet {
168192 feedback .Print (tr ("Port closed: %v" , err ))
@@ -171,14 +195,22 @@ func runMonitorCmd(cmd *cobra.Command, args []string) {
171195 cancel ()
172196 }()
173197
174- if ! quiet {
175- feedback .Print (tr ("Connected to %s! Press CTRL-C to exit." , portAddress ))
176- }
177-
178198 // Wait for port closed
179199 <- ctx .Done ()
180200}
181201
202+ type charDetectorWriter struct {
203+ callback func ()
204+ detectedChar byte
205+ }
206+
207+ func (cd * charDetectorWriter ) Write (buf []byte ) (int , error ) {
208+ if bytes .IndexByte (buf , cd .detectedChar ) != - 1 {
209+ cd .callback ()
210+ }
211+ return len (buf ), nil
212+ }
213+
182214type detailsResult struct {
183215 Settings []* rpc.MonitorPortSettingDescriptor `json:"settings"`
184216}
0 commit comments