1 # Copyright 2019 Free Software Foundation, Inc.
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 3 of the License, or
6 # (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 # An ANSI terminal emulator for expect.
18 # The expect "spawn" function puts the tty name into the spawn_out
19 # array; but dejagnu doesn't export this globally. So, we have to
20 # wrap spawn with our own function, so that we can capture this value.
21 # The value is later used in calls to stty.
22 rename spawn builtin_spawn
24 set result [uplevel builtin_spawn $args]
26 upvar spawn_out spawn_out
27 set gdb_spawn_name $spawn_out(slave,name)
43 # If ARG is empty, return DEF: otherwise ARG. This is useful for
44 # defaulting arguments in CSIs.
45 proc _default {arg def} {
52 # Erase in the line Y from SX to just before EX.
53 proc _clear_in_line {sx ex y} {
56 set lattr [array get _attrs]
58 set _chars($sx,$y) [list " " $lattr]
63 # Erase the lines from SY to just before EY.
64 proc _clear_lines {sy ey} {
67 _clear_in_line 0 $_cols $sy
83 set _cur_x [expr {$_cols - 1}]
96 if {$_cur_y >= $_rows} {
110 set arg [_default [lindex $args 0] 1]
111 set _cur_y [expr {max ($_cur_y - $arg, 0)}]
118 set arg [_default [lindex $args 0] 1]
119 set _cur_y [expr {min ($_cur_y + $arg, $_rows)}]
126 set arg [_default [lindex $args 0] 1]
127 set _cur_x [expr {min ($_cur_x + $arg, $_cols)}]
133 set arg [_default [lindex $args 0] 1]
134 set _cur_x [expr {max ($_cur_x - $arg, 0)}]
142 set arg [_default [lindex $args 0] 1]
144 set _cur_y [expr {min ($_cur_y + $arg, $_rows)}]
147 # Cursor Previous Line.
152 set arg [_default [lindex $args 0] 1]
154 set _cur_y [expr {max ($_cur_y - $arg, 0)}]
157 # Cursor Horizontal Absolute.
161 set arg [_default [lindex $args 0] 1]
162 set _cur_x [expr {min ($arg - 1, $_cols)}]
165 # Move cursor (don't know the official name of this one).
169 set _cur_y [expr {[_default [lindex $args 0] 1] - 1}]
170 set _cur_x [expr {[_default [lindex $args 1] 1] - 1}]
173 # Cursor Forward Tabulation.
175 set n [_default [lindex $args 0] 1]
178 incr _cur_x [expr {$n * 8 - $_cur_x % 8}]
179 if {$_cur_x >= $_cols} {
180 set _cur_x [expr {$_cols - 1}]
190 set arg [_default [lindex $args 0] 0]
192 _clear_in_line $_cur_x $_cols $_cur_y
193 _clear_lines [expr {$_cur_y + 1}] $_rows
194 } elseif {$arg == 1} {
195 _clear_lines 0 [expr {$_cur_y - 1}]
196 _clear_in_line 0 $_cur_x $_cur_y
197 } elseif {$arg == 2} {
198 _clear_lines 0 $_rows
207 set arg [_default [lindex $args 0] 0]
209 # From cursor to end.
210 _clear_in_line $_cur_x $_cols $_cur_y
211 } elseif {$arg == 1} {
212 _clear_in_line 0 $_cur_x $_cur_y
213 } elseif {$arg == 2} {
214 _clear_in_line 0 $_cols $_cur_y
224 set count [_default [lindex $args 0] 1]
226 set next_y [expr {$y + 1}]
227 while {$count > 0 && $next_y < $_rows} {
228 for {set x 0} {$x < $_cols} {incr x} {
229 set _chars($x,$y) $_chars($x,$next_y)
235 _clear_lines $next_y $_rows
240 set n [_default [lindex $args 0] 1]
241 _insert [string repeat " " $n]
247 set n [_default [lindex $args 0] 1]
248 _insert [string repeat $_last_char $n]
251 # Line Position Absolute.
254 set _cur_y [expr {[_default [lindex $args 0] 1] - 1}]
257 # Select Graphic Rendition.
261 switch -exact -- $item {
263 set _attrs(intensity) normal
264 set _attrs(fg) default
265 set _attrs(bg) default
266 set _attrs(underline) 0
267 set _attrs(reverse) 0
270 set _attrs(intensity) bold
273 set _attrs(intensity) dim
276 set _attrs(underline) 1
279 set _attrs(reverse) 1
282 set _attrs(intensity) normal
285 set _attrs(underline) 0
288 set _attrs(reverse) 1
290 30 - 31 - 32 - 33 - 34 - 35 - 36 - 37 {
294 set _attrs(fg) default
296 40 - 41 - 42 - 43 - 44 - 45 - 46 - 47 {
300 set _attrs(bg) default
306 # Insert string at the cursor location.
308 verbose "INSERT <<$str>>"
315 set lattr [array get _attrs]
316 foreach char [split $str {}] {
317 set _chars($_cur_x,$_cur_y) [list $char $lattr]
319 if {$_cur_x >= $_cols} {
322 if {$_cur_y >= $_rows} {
330 proc _setup {rows cols} {
332 set stty_init "rows $rows columns $cols"
352 _clear_lines 0 $_rows
355 # Accept some output from gdb and update the screen.
359 -re "^\[\x07\x08\x0a\x0d\]" {
360 scan $expect_out(0,string) %c val
361 set hexval [format "%02x" $val]
362 verbose "+++ _ctl_0x${hexval}"
366 -re "^\x1b(\[0-9a-zA-Z\])" {
367 verbose "+++ unsupported escape"
368 error "unsupported escape"
370 -re "^\x1b\\\[(\[0-9;\]*)(\[0-9a-zA-Z@\])" {
371 set cmd $expect_out(2,string)
372 set params [split $expect_out(1,string) ";"]
373 verbose "+++ _csi_$cmd <<<$expect_out(1,string)>>>"
374 eval _csi_$cmd $params
377 -re "^\[^\x07\x08\x0a\x0d\x1b\]+" {
378 _insert $expect_out(0,string)
380 set _last_char [string index $expect_out(0,string) end]
381 # If the prompt was just inserted, return.
385 set prev [get_line $_cur_y $_cur_x]
386 if {![regexp -- "$gdb_prompt \$" $prev]} {
393 # Like ::clean_restart, but ensures that gdb starts in an
394 # environment where the TUI can work. ROWS and COLS are the size
395 # of the terminal. EXECUTABLE is passed to clean_restart.
396 proc clean_restart {rows cols executable} {
398 save_vars {env(TERM) stty_init} {
401 ::clean_restart $executable
405 # Start the TUI. Returns 1 on success, 0 if TUI tests should be
408 if {[skip_tui_tests]} {
412 gdb_test_no_output "set tui border-kind ascii"
417 # Send the command CMD to gdb, then wait for a gdb prompt to be
418 # seen in the TUI. CMD should not end with a newline -- that will
419 # be supplied by this function.
425 # Return the text of screen line N, without attributes. Lines are
426 # 0-based. If C is given, stop before column C. Columns are also
428 proc get_line {n {c ""}} {
432 set c [_default $c $_cols]
435 append result [lindex $_chars($x,$n) 0]
441 # Get just the character at (X, Y).
442 proc get_char {x y} {
444 return [lindex $_chars($x,$y) 0]
447 # Get the entire screen as a string.
448 proc get_all_lines {} {
454 for {set y 0} {$y < $_rows} {incr y} {
455 for {set x 0} {$x < $_cols} {incr x} {
456 append result [lindex $_chars($x,$y) 0]
464 # Get the text just before the cursor.
465 proc get_current_line {} {
468 return [get_line $_cur_y $_cur_x]
471 # Helper function for check_box. Returns empty string if the box
472 # is found, description of why not otherwise.
473 proc _check_box {x y width height} {
474 set x2 [expr {$x + $width - 1}]
475 set y2 [expr {$y + $height - 1}]
477 if {[get_char $x $y] != "+"} {
480 if {[get_char $x $y2] != "+"} {
483 if {[get_char $x2 $y] != "+"} {
486 if {[get_char $x2 $y2] != "+"} {
490 for {set i [expr {$x + 1}]} {$i < $x2 - 1} {incr i} {
491 # Note we do not check the top border of the box, because
492 # it will contain a title.
493 if {[get_char $i $y2] != "-"} {
494 return "bottom border $i"
497 for {set i [expr {$y + 1}]} {$i < $y2 - 1} {incr i} {
498 if {[get_char $x $i] != "|"} {
499 return "left side $i"
501 if {[get_char $x2 $i] != "|"} {
502 return "right side $i"
509 # Check for a box at the given coordinates.
510 proc check_box {test_name x y width height} {
511 set why [_check_box $x $y $width $height]
516 fail "$test_name ($why)"
520 # Check whether the text contents of the terminal match the
521 # regular expression. Note that text styling is not considered.
522 proc check_contents {test_name regexp} {
523 set contents [get_all_lines]
524 if {![gdb_assert {[regexp -- $regexp $contents]} $test_name]} {
529 # A debugging function to dump the current screen, with line
531 proc dump_screen {} {
533 verbose "Screen Dump:"
534 for {set y 0} {$y < $_rows} {incr y} {
535 set fmt [format %5d $y]
536 verbose "$fmt [get_line $y]"
540 # Resize the terminal.
541 proc resize {rows cols} {
546 set old_rows [expr {min ($_rows, $rows)}]
547 set old_cols [expr {min ($_cols, $cols)}]
550 array set local_chars [array get _chars]
555 _clear_lines 0 $_rows
557 for {set x 0} {$x < $old_cols} {incr x} {
558 for {set y 0} {$y < $old_rows} {incr y} {
559 set _chars($x,$y) $local_chars($x,$y)
563 global gdb_spawn_name
564 # Somehow the number of columns transmitted to gdb is one less
565 # than what we request from expect. We hide this weird
566 # details from the caller.
567 stty rows $_rows columns [expr {$_cols + 1}] \