16
16
package monitor
17
17
18
18
import (
19
+ "bytes"
19
20
"context"
20
21
"errors"
21
22
"fmt"
@@ -35,29 +36,34 @@ import (
35
36
"github.com/fatih/color"
36
37
"github.com/sirupsen/logrus"
37
38
"github.com/spf13/cobra"
39
+ "go.bug.st/cleanup"
38
40
)
39
41
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
48
43
49
44
// NewCommand created a new `monitor` command
50
45
func 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
+ )
51
54
monitorCommand := &cobra.Command{
52
55
Use: "monitor",
53
56
Short: tr("Open a communication port with a board."),
54
57
Long: tr("Open a communication port with a board."),
55
58
Example: "" +
56
59
" " + os.Args[0] + " monitor -p /dev/ttyACM0\n" +
57
60
" " + 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
+ },
59
64
}
60
65
portArgs.AddToCommand(monitorCommand)
66
+ monitorCommand.Flags().BoolVar(&raw, "raw", false, tr("Set terminal in raw mode (unbuffered)."))
61
67
monitorCommand.Flags().BoolVar(&describe, "describe", false, tr("Show all the settings of the communication port."))
62
68
monitorCommand.Flags().StringSliceVarP(&configs, "config", "c", []string{}, tr("Configure communication port settings. The format is <ID>=<value>[,<ID>=<value>]..."))
63
69
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 {
66
72
return monitorCommand
67
73
}
68
74
69
- func runMonitorCmd(cmd *cobra.Command, args []string) {
75
+ func runMonitorCmd(portArgs *arguments.Port, fqbn *arguments.Fqbn, configs []string, describe, quiet, raw bool ) {
70
76
instance := instance.CreateAndInit()
71
77
logrus.Info("Executing `arduino-cli monitor`")
72
78
@@ -93,12 +99,6 @@ func runMonitorCmd(cmd *cobra.Command, args []string) {
93
99
return
94
100
}
95
101
96
- tty, err := newStdInOutTerminal()
97
- if err != nil {
98
- feedback.FatalError(err, feedback.ErrGeneric)
99
- }
100
- defer tty.Close()
101
-
102
102
configuration := &rpc.MonitorPortConfiguration{}
103
103
if len(configs) > 0 {
104
104
for _, config := range configs {
@@ -151,9 +151,33 @@ func runMonitorCmd(cmd *cobra.Command, args []string) {
151
151
}
152
152
defer portProxy.Close()
153
153
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
+
155
179
go func() {
156
- _, err := io.Copy(tty , portProxy)
180
+ _, err := io.Copy(ttyOut , portProxy)
157
181
if err != nil && !errors.Is(err, io.EOF) {
158
182
if !quiet {
159
183
feedback.Print(tr("Port closed: %v", err))
@@ -162,7 +186,7 @@ func runMonitorCmd(cmd *cobra.Command, args []string) {
162
186
cancel()
163
187
}()
164
188
go func() {
165
- _, err := io.Copy(portProxy, tty )
189
+ _, err := io.Copy(portProxy, ttyIn )
166
190
if err != nil && !errors.Is(err, io.EOF) {
167
191
if !quiet {
168
192
feedback.Print(tr("Port closed: %v", err))
@@ -171,14 +195,22 @@ func runMonitorCmd(cmd *cobra.Command, args []string) {
171
195
cancel()
172
196
}()
173
197
174
- if !quiet {
175
- feedback.Print(tr("Connected to %s! Press CTRL-C to exit.", portAddress))
176
- }
177
-
178
198
// Wait for port closed
179
199
<-ctx.Done()
180
200
}
181
201
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
+
182
214
type detailsResult struct {
183
215
Settings []*rpc.MonitorPortSettingDescriptor `json:"settings"`
184
216
}
0 commit comments