term-vt102 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.travis.yml +3 -0
- data/CREDITS.md +25 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +27 -0
- data/README.md +50 -0
- data/Rakefile +9 -0
- data/examples/tmux_ghost.rb +72 -0
- data/lib/term/vt102.rb +1389 -0
- data/lib/term/vt102/version.rb +5 -0
- data/term-vt102.gemspec +23 -0
- data/test/test_01_load.rb +15 -0
- data/test/test_02_setsize.rb +18 -0
- data/test/test_03_text.rb +34 -0
- data/test/test_04_cursor.rb +73 -0
- data/test/test_05_scrolling.rb +187 -0
- data/test/test_06_insdel.rb +309 -0
- data/test/test_07_colour.rb +48 -0
- data/test/test_08_options.rb +27 -0
- data/test/test_09_callback.rb +57 -0
- data/test/test_10_decaln.rb +34 -0
- data/test/test_11_decscrc.rb +34 -0
- data/test/test_12_cupsvrs.rb +34 -0
- data/test/test_13_xonxoff.rb +47 -0
- data/test/test_14_tabstop.rb +32 -0
- data/test/test_helper.rb +92 -0
- metadata +127 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 42642e1555fefd4cc8829b4e4000c7daf8d24b43
|
4
|
+
data.tar.gz: 673456148028019d84d0a7de65c399d5dc2e5082
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 16e9bc770fcc90f0f292d0c35096dcc48743fbc629f0ecae524ca9db457df801d51fab6702b0ec19e66dcf231d1805655d190c09e1747705630aa5ea18d2514a
|
7
|
+
data.tar.gz: c4a16753bbc634516b7ee92815dcc51f0de1082af6fb17274bafc93c609b0e9219edbe5b4591087213283958ce315faa082c4396ceec2dc56b942ea7fac47bea
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/CREDITS.md
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
## Credits
|
2
|
+
|
3
|
+
Mike Owens <mike@filespanker.com>
|
4
|
+
- Ruby Port.
|
5
|
+
|
6
|
+
The following people have contributed to the original Term::VT102 Perl module.
|
7
|
+
|
8
|
+
Andrew Wood <andrew dot wood at ivarch dot com>.
|
9
|
+
- Author of the Term::VT102 Perl module, without which
|
10
|
+
this project would not exist.
|
11
|
+
|
12
|
+
Charles Harker <CHarker at interland dot com>
|
13
|
+
- reported and helped to diagnose a bug in the handling of TABs
|
14
|
+
|
15
|
+
Steve van der Burg <steve dot vanderburg at lhsc dot on dot ca>
|
16
|
+
- supplied basis for an example script using Net::Telnet
|
17
|
+
|
18
|
+
Chris R. Donnelly <cdonnelly at digitalmotorworks dot com>
|
19
|
+
- added support for DECTCEM, partial support for SM/RM
|
20
|
+
|
21
|
+
Paul L. Stoddard
|
22
|
+
- reported a possible bug in cursor movement handling
|
23
|
+
|
24
|
+
Joerg Walter
|
25
|
+
- provided a patch for Unicode handling
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
Andrew Wood has granted permission for this port to be released under the
|
2
|
+
MIT License, which follows:
|
3
|
+
=====================================================================
|
4
|
+
|
5
|
+
Copyright (c) 2014 Mike Owens
|
6
|
+
Copyright (c) 2001-2008 Andrew Wood
|
7
|
+
|
8
|
+
MIT License
|
9
|
+
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
11
|
+
a copy of this software and associated documentation files (the
|
12
|
+
"Software"), to deal in the Software without restriction, including
|
13
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
14
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
15
|
+
permit persons to whom the Software is furnished to do so, subject to
|
16
|
+
the following conditions:
|
17
|
+
|
18
|
+
The above copyright notice and this permission notice shall be
|
19
|
+
included in all copies or substantial portions of the Software.
|
20
|
+
|
21
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
22
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
23
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
24
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
25
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
26
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
27
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# Term::VT102
|
2
|
+
|
3
|
+
Term::VT102 provides emulation of a VT102 terminal, in Ruby. It's a great way
|
4
|
+
to automate interactions with remote systems, particularly ones that only
|
5
|
+
provide interactive/curses style interfaces. It can tell you what's on the
|
6
|
+
screen at any time, and notify you of changes.
|
7
|
+
|
8
|
+
A lot of terrible legacy applications fall into this category.
|
9
|
+
|
10
|
+
This gem is a port of Andrew Wood's Perl module, Term::VT102. Permission has
|
11
|
+
been granted to release this derived work under the MIT license.
|
12
|
+
|
13
|
+
Term::VT102 aims to be fairly literal port of the Perl module, and higher-level
|
14
|
+
features will most likely show up in other gems instead of being integrated
|
15
|
+
here.
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
Add this line to your application's Gemfile:
|
20
|
+
|
21
|
+
gem 'term-vt102'
|
22
|
+
|
23
|
+
And then execute:
|
24
|
+
|
25
|
+
$ bundle
|
26
|
+
|
27
|
+
Or install it yourself as:
|
28
|
+
|
29
|
+
$ gem install term-vt102
|
30
|
+
|
31
|
+
## Usage
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
|
35
|
+
require 'term/vt102'
|
36
|
+
|
37
|
+
vt = Term::VT102.new(cols: 80, rows: 25)
|
38
|
+
|
39
|
+
# Await patiently at your editor for me to write documentation, or check out
|
40
|
+
# the tests.
|
41
|
+
|
42
|
+
```
|
43
|
+
|
44
|
+
## Contributing
|
45
|
+
|
46
|
+
1. Fork it ( http://github.com/mieko/term-vt102/fork )
|
47
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
48
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
49
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
50
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# This example just pulls a cool little tmux trick.
|
4
|
+
#
|
5
|
+
# Steps:
|
6
|
+
# 1. Start a tmux session
|
7
|
+
# 2. Make sure you're not doing anything important
|
8
|
+
# 3. Run this file
|
9
|
+
# 4. Watch either (or both) terminals
|
10
|
+
#
|
11
|
+
# This program creates a pty, and connects to it as the VT102.
|
12
|
+
# It then executes "tmux attach", and prints a ghost to the screen.
|
13
|
+
#
|
14
|
+
# The emulator will stay attached and refresh its screen every
|
15
|
+
# half second until you ^C it out of there.
|
16
|
+
#
|
17
|
+
# For a moment there, you're gonna have a terminal emulator (VT102) in a
|
18
|
+
# terminal emulator (tmux) in a terminal emulator (xterm, Konsole,
|
19
|
+
# Terminal.app, whatever). Let that sink in, dawg.
|
20
|
+
|
21
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
22
|
+
|
23
|
+
# Unix-y enough require list for you?
|
24
|
+
require 'term/vt102'
|
25
|
+
require 'pty'
|
26
|
+
require 'io/console'
|
27
|
+
|
28
|
+
# Prints the entire VT102 screen to an IO
|
29
|
+
def dump(vt, to: $stderr)
|
30
|
+
to.puts " ." + ('-' * (vt.cols)) + "."
|
31
|
+
(1 .. vt.rows).each do |row|
|
32
|
+
to.puts " |#{vt.row_plaintext(row)}|"
|
33
|
+
end
|
34
|
+
to.puts " '" + ('-' * (vt.cols)) + "'"
|
35
|
+
end
|
36
|
+
|
37
|
+
# Does the cool ghost thing.
|
38
|
+
def spooky(to:)
|
39
|
+
@ghost ||= DATA.read
|
40
|
+
@ghost.each_line do |line|
|
41
|
+
to.puts "\# #{line}"
|
42
|
+
sleep 0.2
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
vt = Term::VT102.new
|
47
|
+
rd, wr, pid = PTY.spawn("tmux attach")
|
48
|
+
|
49
|
+
loop do
|
50
|
+
s = rd.readpartial(1024)
|
51
|
+
if s && !s.empty?
|
52
|
+
vt.process(s)
|
53
|
+
dump(vt, to: $stderr)
|
54
|
+
|
55
|
+
unless defined?(@greeting)
|
56
|
+
@greeting = true
|
57
|
+
spooky(to: wr)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
sleep 0.5
|
62
|
+
end
|
63
|
+
|
64
|
+
__END__
|
65
|
+
___
|
66
|
+
_/ ..\
|
67
|
+
( \ 0/__
|
68
|
+
\ \__)
|
69
|
+
/ \
|
70
|
+
jgs / _\
|
71
|
+
`"""""``
|
72
|
+
BOO FROM TEH GHOST IN TEH MACHINE
|
data/lib/term/vt102.rb
ADDED
@@ -0,0 +1,1389 @@
|
|
1
|
+
# Term::VT102 - module for VT102 emulation in Ruby
|
2
|
+
#
|
3
|
+
# Ported from Andrew Wood's Perl module, 'Term::VT102'.
|
4
|
+
#
|
5
|
+
# Copyright (C) Mike Owens
|
6
|
+
# Copyright (C) Andrew Wood
|
7
|
+
# NO WARRANTY - see LICENSE.txt
|
8
|
+
#
|
9
|
+
|
10
|
+
require 'term/vt102/version'
|
11
|
+
|
12
|
+
module Term
|
13
|
+
class VT102
|
14
|
+
# Return the packed version of a set of attributes fg, bg, bo, fa, st, ul,
|
15
|
+
# bl, rv.
|
16
|
+
#
|
17
|
+
def self.attr_pack(fg, bg, bo, fa, st, ul, bl, rv)
|
18
|
+
num = (fg & 7) |
|
19
|
+
((bg & 7) << 4) |
|
20
|
+
(bo << 8) |
|
21
|
+
(fa << 9) |
|
22
|
+
(st << 10) |
|
23
|
+
(ul << 11) |
|
24
|
+
(bl << 12) |
|
25
|
+
(rv << 13)
|
26
|
+
[num].pack('S')
|
27
|
+
end
|
28
|
+
|
29
|
+
def attr_pack(*args)
|
30
|
+
self.class.attr_pack(*args)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Return the unpacked version of a packed attribute.
|
34
|
+
#
|
35
|
+
def attr_unpack(data)
|
36
|
+
num = data.unpack('S').first
|
37
|
+
|
38
|
+
fg = num & 7
|
39
|
+
bg = (num >> 4) & 7
|
40
|
+
bo = (num >> 8) & 1
|
41
|
+
fa = (num >> 9) & 1
|
42
|
+
st = (num >> 10) & 1
|
43
|
+
ul = (num >> 11) & 1
|
44
|
+
bl = (num >> 12) & 1
|
45
|
+
rv = (num >> 13) & 1
|
46
|
+
|
47
|
+
[fg, bg, bo, fa, st, ul, bl, rv]
|
48
|
+
end
|
49
|
+
|
50
|
+
DEFAULT_ATTR = [7, 0, 0, 0, 0, 0, 0, 0].freeze
|
51
|
+
DEFAULT_ATTR_PACKED = attr_pack(*DEFAULT_ATTR).freeze
|
52
|
+
|
53
|
+
# Constructor function.
|
54
|
+
#
|
55
|
+
def initialize(cols: 80, rows: 24)
|
56
|
+
# control characters
|
57
|
+
@_ctlseq = {
|
58
|
+
"\000" => 'NUL', # ignored
|
59
|
+
"\005" => 'ENQ', # trigger answerback message
|
60
|
+
"\007" => 'BEL', # beep
|
61
|
+
"\010" => 'BS', # backspace one column
|
62
|
+
"\011" => 'HT', # horizontal tab to next tab stop
|
63
|
+
"\012" => 'LF', # line feed
|
64
|
+
"\013" => 'VT', # line feed
|
65
|
+
"\014" => 'FF', # line feed
|
66
|
+
"\015" => 'CR', # carriage return
|
67
|
+
"\016" => 'SO', # activate G1 character set & newline
|
68
|
+
"\017" => 'SI', # activate G0 character set
|
69
|
+
"\021" => 'XON', # resume transmission
|
70
|
+
"\023" => 'XOFF', # stop transmission, ignore characters
|
71
|
+
"\030" => 'CAN', # interrupt escape sequence
|
72
|
+
"\032" => 'SUB', # interrupt escape sequence
|
73
|
+
"\033" => 'ESC', # start escape sequence
|
74
|
+
"\177" => 'DEL', # ignored
|
75
|
+
"\233" => 'CSI' # equivalent to ESC [
|
76
|
+
}
|
77
|
+
|
78
|
+
# escape sequences
|
79
|
+
@_escseq = {
|
80
|
+
'c' => 'RIS', # reset
|
81
|
+
'D' => 'IND', # line feed
|
82
|
+
'E' => 'NEL', # newline
|
83
|
+
'H' => 'HTS', # set tab stop at current column
|
84
|
+
'M' => 'RI', # reverse line feed
|
85
|
+
'Z' => 'DECID', # DEC private ID; return ESC [ ? 6 c (VT102)
|
86
|
+
'7' => 'DECSC', # save state (position, charset, attributes)
|
87
|
+
'8' => 'DECRC', # restore most recently saved state
|
88
|
+
'[' => 'CSI', # control sequence introducer
|
89
|
+
'[[' => 'IGN', # ignored control sequence
|
90
|
+
'%@' => 'CSDFL', # select default charset (ISO646/8859-1)
|
91
|
+
'%G' => 'CSUTF8', # select UTF-8
|
92
|
+
'%8' => 'CSUTF8', # select UTF-8 (obsolete)
|
93
|
+
'#8' => 'DECALN', # DEC alignment test - fill screen with E's
|
94
|
+
'(8' => 'G0DFL', # G0 charset = default mapping (ISO8859-1)
|
95
|
+
'(0' => 'G0GFX', # G0 charset = VT100 graphics mapping
|
96
|
+
'(U' => 'G0ROM', # G0 charset = null mapping (straight to ROM)
|
97
|
+
'(K' => 'G0USR', # G0 charset = user defined mapping
|
98
|
+
'(B' => 'G0TXT', # G0 charset = ASCII mapping
|
99
|
+
')8' => 'G1DFL', # G1 charset = default mapping (ISO8859-1)
|
100
|
+
')0' => 'G1GFX', # G1 charset = VT100 graphics mapping
|
101
|
+
')U' => 'G1ROM', # G1 charset = null mapping (straight to ROM)
|
102
|
+
')K' => 'G1USR', # G1 charset = user defined mapping
|
103
|
+
')B' => 'G1TXT', # G1 charset = ASCII mapping
|
104
|
+
'*8' => 'G2DFL', # G2 charset = default mapping (ISO8859-1)
|
105
|
+
'*0' => 'G2GFX', # G2 charset = VT100 graphics mapping
|
106
|
+
'*U' => 'G2ROM', # G2 charset = null mapping (straight to ROM)
|
107
|
+
'*K' => 'G2USR', # G2 charset = user defined mapping
|
108
|
+
'+8' => 'G3DFL', # G3 charset = default mapping (ISO8859-1)
|
109
|
+
'+0' => 'G3GFX', # G3 charset = VT100 graphics mapping
|
110
|
+
'+U' => 'G3ROM', # G3 charset = null mapping (straight to ROM)
|
111
|
+
'+K' => 'G3USR', # G3 charset = user defined mapping
|
112
|
+
'>' => 'DECPNM', # set numeric keypad mode
|
113
|
+
'=' => 'DECPAM', # set application keypad mode
|
114
|
+
'N' => 'SS2', # select G2 charset for next char only
|
115
|
+
'O' => 'SS3', # select G3 charset for next char only
|
116
|
+
'P' => 'DCS', # device control string (ended by ST)
|
117
|
+
'X' => 'SOS', # start of string
|
118
|
+
'^' => 'PM', # privacy message (ended by ST)
|
119
|
+
'_' => 'APC', # application program command (ended by ST)
|
120
|
+
"\\" => 'ST', # string terminator
|
121
|
+
'n' => 'LS2', # invoke G2 charset
|
122
|
+
'o' => 'LS3', # invoke G3 charset
|
123
|
+
'|' => 'LS3R', # invoke G3 charset as GR
|
124
|
+
'}' => 'LS2R', # invoke G2 charset as GR
|
125
|
+
'~' => 'LS1R', # invoke G1 charset as GR
|
126
|
+
']' => 'OSC', # operating system command
|
127
|
+
'g' => 'BEL', # alternate BEL
|
128
|
+
}
|
129
|
+
|
130
|
+
# ECMA-48 CSI sequences
|
131
|
+
@_csiseq = {
|
132
|
+
'[' => 'IGN', # ignored control sequence
|
133
|
+
'@' => 'ICH', # insert blank characters
|
134
|
+
'A' => 'CUU', # move cursor up
|
135
|
+
'B' => 'CUD', # move cursor down
|
136
|
+
'C' => 'CUF', # move cursor right
|
137
|
+
'D' => 'CUB', # move cursor left
|
138
|
+
'E' => 'CNL', # move cursor down and to column 1
|
139
|
+
'F' => 'CPL', # move cursor up and to column 1
|
140
|
+
'G' => 'CHA', # move cursor to column in current row
|
141
|
+
'H' => 'CUP', # move cursor to row, column
|
142
|
+
'J' => 'ED', # erase display
|
143
|
+
'K' => 'EL', # erase line
|
144
|
+
'L' => 'IL', # insert blank lines
|
145
|
+
'M' => 'DL', # delete lines
|
146
|
+
'P' => 'DCH', # delete characters on current line
|
147
|
+
'X' => 'ECH', # erase characters on current line
|
148
|
+
'a' => 'HPR', # move cursor right
|
149
|
+
'c' => 'DA', # return ESC [ ? 6 c (VT102)
|
150
|
+
'd' => 'VPA', # move to row (current column)
|
151
|
+
'e' => 'VPR', # move cursor down
|
152
|
+
'f' => 'HVP', # move cursor to row, column
|
153
|
+
'g' => 'TBC', # clear tab stop (CSI 3 g = clear all stops)
|
154
|
+
'h' => 'SM', # set mode
|
155
|
+
'l' => 'RM', # reset mode
|
156
|
+
'm' => 'SGR', # set graphic rendition
|
157
|
+
'n' => 'DSR', # device status report
|
158
|
+
'q' => 'DECLL', # set keyboard LEDs
|
159
|
+
'r' => 'DECSTBM', # set scrolling region to (top, bottom) rows
|
160
|
+
's' => 'CUPSV', # save cursor position
|
161
|
+
'u' => 'CUPRS', # restore cursor position
|
162
|
+
'`' => 'HPA' # move cursor to column in current row
|
163
|
+
}
|
164
|
+
|
165
|
+
# ANSI/DEC specified modes for SM/RM
|
166
|
+
@_modeseq = {
|
167
|
+
# ANSI Specified Modes
|
168
|
+
'0' => 'IGN', # Error (Ignored)
|
169
|
+
'1' => 'GATM', # guarded-area transfer mode (ignored)
|
170
|
+
'2' => 'KAM', # keyboard action mode (always reset)
|
171
|
+
'3' => 'CRM', # control representation mode (always reset)
|
172
|
+
'4' => 'IRM', # insertion/replacement mode (always reset)
|
173
|
+
'5' => 'SRTM', # status-reporting transfer mode
|
174
|
+
'6' => 'ERM', # erasure mode (always set)
|
175
|
+
'7' => 'VEM', # vertical editing mode (ignored)
|
176
|
+
'10' => 'HEM', # horizontal editing mode
|
177
|
+
'11' => 'PUM', # positioning unit mode
|
178
|
+
'12' => 'SRM', # send/receive mode (echo on/off)
|
179
|
+
'13' => 'FEAM', # format effector action mode
|
180
|
+
'14' => 'FETM', # format effector transfer mode
|
181
|
+
'15' => 'MATM', # multiple area transfer mode
|
182
|
+
'16' => 'TTM', # transfer termination mode
|
183
|
+
'17' => 'SATM', # selected area transfer mode
|
184
|
+
'18' => 'TSM', # tabulation stop mode
|
185
|
+
'19' => 'EBM', # editing boundary mode
|
186
|
+
'20' => 'LNM', # Line Feed / New Line Mode
|
187
|
+
# DEC Private Modes
|
188
|
+
'?0' => 'IGN', # Error (Ignored)
|
189
|
+
'?1' => 'DECCKM', # Cursorkeys application (set); Cursorkeys normal (reset)
|
190
|
+
'?2' => 'DECANM', # ANSI (set); VT52 (reset)
|
191
|
+
'?3' => 'DECCOLM', # 132 columns (set); 80 columns (reset)
|
192
|
+
'?4' => 'DECSCLM', # Jump scroll (set); Smooth scroll (reset)
|
193
|
+
'?5' => 'DECSCNM', # Reverse screen (set); Normal screen (reset)
|
194
|
+
'?6' => 'DECOM', # Sets relative coordinates (set); Sets absolute coordinates (reset)
|
195
|
+
'?7' => 'DECAWM', # Auto Wrap
|
196
|
+
'?8' => 'DECARM', # Auto Repeat
|
197
|
+
'?9' => 'DECINLM', # Interlace
|
198
|
+
'?18' => 'DECPFF', # Send FF to printer after print screen (set); No char after PS (reset)
|
199
|
+
'?19' => 'DECPEX', # Print screen: prints full screen (set); prints scroll region (reset)
|
200
|
+
'?25' => 'DECTCEM', # Cursor on (set); Cursor off (reset)
|
201
|
+
}
|
202
|
+
|
203
|
+
# supported character sequences
|
204
|
+
@_funcs = {
|
205
|
+
'BS' => :_code_BS, # backspace one column
|
206
|
+
'CR' => :_code_CR, # carriage return
|
207
|
+
'DA' => :_code_DA, # return ESC [ ? 6 c (VT102)
|
208
|
+
'DL' => :_code_DL, # delete lines
|
209
|
+
'ED' => :_code_ED, # erase display
|
210
|
+
'EL' => :_code_EL, # erase line
|
211
|
+
'FF' => :_code_LF, # line feed
|
212
|
+
'HT' => :_code_HT, # horizontal tab to next tab stop
|
213
|
+
'IL' => :_code_IL, # insert blank lines
|
214
|
+
'LF' => :_code_LF, # line feed
|
215
|
+
'PM' => :_code_PM, # privacy message (ended by ST)
|
216
|
+
'RI' => :_code_RI, # reverse line feed
|
217
|
+
'RM' => :_code_RM, # reset mode
|
218
|
+
'SI' => nil, # activate G0 character set
|
219
|
+
'SM' => :_code_SM, # set mode
|
220
|
+
'SO' => nil, # activate G1 character set & CR
|
221
|
+
'ST' => nil, # string terminator
|
222
|
+
'VT' => :_code_LF, # line feed
|
223
|
+
'APC' => :_code_APC, # application program command (ended by ST)
|
224
|
+
'BEL' => :_code_BEL, # beep
|
225
|
+
'CAN' => :_code_CAN, # interrupt escape sequence
|
226
|
+
'CHA' => :_code_CHA, # move cursor to column in current row
|
227
|
+
'CNL' => :_code_CNL, # move cursor down and to column 1
|
228
|
+
'CPL' => :_code_CPL, # move cursor up and to column 1
|
229
|
+
'CRM' => nil, # control representation mode
|
230
|
+
'CSI' => :_code_CSI, # equivalent to ESC [
|
231
|
+
'CUB' => :_code_CUB, # move cursor left
|
232
|
+
'CUD' => :_code_CUD, # move cursor down
|
233
|
+
'CUF' => :_code_CUF, # move cursor right
|
234
|
+
'CUP' => :_code_CUP, # move cursor to row, column
|
235
|
+
'CUU' => :_code_CUU, # move cursor up
|
236
|
+
'DCH' => :_code_DCH, # delete characters on current line
|
237
|
+
'DCS' => :_code_DCS, # device control string (ended by ST)
|
238
|
+
'DEL' => :_code_IGN, # ignored
|
239
|
+
'DSR' => :_code_DSR, # device status report
|
240
|
+
'EBM' => nil, # editing boundary mode
|
241
|
+
'ECH' => :_code_ECH, # erase characters on current line
|
242
|
+
'ENQ' => nil, # trigger answerback message
|
243
|
+
'ERM' => nil, # erasure mode
|
244
|
+
'ESC' => :_code_ESC, # start escape sequence
|
245
|
+
'HEM' => nil, # horizontal editing mode
|
246
|
+
'HPA' => :_code_CHA, # move cursor to column in current row
|
247
|
+
'HPR' => :_code_CUF, # move cursor right
|
248
|
+
'HTS' => :_code_HTS, # set tab stop at current column
|
249
|
+
'HVP' => :_code_CUP, # move cursor to row, column
|
250
|
+
'ICH' => :_code_ICH, # insert blank characters
|
251
|
+
'IGN' => :_code_IGN, # ignored control sequence
|
252
|
+
'IND' => :_code_LF, # line feed
|
253
|
+
'IRM' => nil, # insert/replace mode
|
254
|
+
'KAM' => nil, # keyboard action mode
|
255
|
+
'LNM' => nil, # line feed / newline mode
|
256
|
+
'LS2' => nil, # invoke G2 charset
|
257
|
+
'LS3' => nil, # invoke G3 charset
|
258
|
+
'NEL' => :_code_NEL, # newline
|
259
|
+
'NUL' => :_code_IGN, # ignored
|
260
|
+
'OSC' => :_code_OSC, # operating system command
|
261
|
+
'PUM' => nil, # positioning unit mode
|
262
|
+
'RIS' => :_code_RIS, # reset
|
263
|
+
'SGR' => :_code_SGR, # set graphic rendition
|
264
|
+
'SOS' => nil, # start of string
|
265
|
+
'SRM' => nil, # send/receive mode (echo on/off)
|
266
|
+
'SS2' => nil, # select G2 charset for next char only
|
267
|
+
'SS3' => nil, # select G3 charset for next char only
|
268
|
+
'SUB' => :_code_CAN, # interrupt escape sequence
|
269
|
+
'TBC' => :_code_TBC, # clear tab stop (CSI 3 g = clear all stops)
|
270
|
+
'TSM' => nil, # tabulation stop mode
|
271
|
+
'TTM' => nil, # transfer termination mode
|
272
|
+
'VEM' => nil, # vertical editing mode
|
273
|
+
'VPA' => :_code_VPA, # move to row (current column)
|
274
|
+
'VPR' => :_code_CUD, # move cursor down
|
275
|
+
'XON' => :_code_XON, # resume transmission
|
276
|
+
'FEAM' => nil, # format effector action mode
|
277
|
+
'FETM' => nil, # format effector transfer mode
|
278
|
+
'GATM' => nil, # guarded-area transfer mode
|
279
|
+
'LS1R' => nil, # invoke G1 charset as GR
|
280
|
+
'LS2R' => nil, # invoke G2 charset as GR
|
281
|
+
'LS3R' => nil, # invoke G3 charset as GR
|
282
|
+
'MATM' => nil, # multiple area transfer mode
|
283
|
+
'SATM' => nil, # selected area transfer mode
|
284
|
+
'SRTM' => nil, # status-reporting transfer mode
|
285
|
+
'XOFF' => :_code_XOFF, # stop transmission, ignore characters
|
286
|
+
'CSDFL' => nil, # select default charset (ISO646/8859-1)
|
287
|
+
'CUPRS' => :_code_CUPRS, # restore cursor position
|
288
|
+
'CUPSV' => :_code_CUPSV, # save cursor position
|
289
|
+
'DECID' => :_code_DA, # DEC private ID; return ESC [ ? 6 c (VT102)
|
290
|
+
'DECLL' => nil, # set keyboard LEDs
|
291
|
+
'DECOM' => nil, # relative/absolute coordinate mode
|
292
|
+
'DECRC' => :_code_DECRC, # restore most recently saved state
|
293
|
+
'DECSC' => :_code_DECSC, # save state (position, charset, attributes)
|
294
|
+
'G0DFL' => nil, # G0 charset = default mapping (ISO8859-1)
|
295
|
+
'G0GFX' => nil, # G0 charset = VT100 graphics mapping
|
296
|
+
'G0ROM' => nil, # G0 charset = null mapping (straight to ROM)
|
297
|
+
'G0TXT' => nil, # G0 charset = ASCII mapping
|
298
|
+
'G0USR' => nil, # G0 charset = user defined mapping
|
299
|
+
'G1DFL' => nil, # G1 charset = default mapping (ISO8859-1)
|
300
|
+
'G1GFX' => nil, # G1 charset = VT100 graphics mapping
|
301
|
+
'G1ROM' => nil, # G1 charset = null mapping (straight to ROM)
|
302
|
+
'G1TXT' => nil, # G1 charset = ASCII mapping
|
303
|
+
'G1USR' => nil, # G1 charset = user defined mapping
|
304
|
+
'G2DFL' => nil, # G2 charset = default mapping (ISO8859-1)
|
305
|
+
'G2GFX' => nil, # G2 charset = VT100 graphics mapping
|
306
|
+
'G2ROM' => nil, # G2 charset = null mapping (straight to ROM)
|
307
|
+
'G2USR' => nil, # G2 charset = user defined mapping
|
308
|
+
'G3DFL' => nil, # G3 charset = default mapping (ISO8859-1)
|
309
|
+
'G3GFX' => nil, # G3 charset = VT100 graphics mapping
|
310
|
+
'G3ROM' => nil, # G3 charset = null mapping (straight to ROM)
|
311
|
+
'G3USR' => nil, # G3 charset = user defined mapping
|
312
|
+
'CSUTF8' => nil, # select UTF-8 (obsolete)
|
313
|
+
'DECALN' => :_code_DECALN, # DEC alignment test - fill screen with E's
|
314
|
+
'DECANM' => nil, # ANSI/VT52 mode
|
315
|
+
'DECARM' => nil, # auto repeat mode
|
316
|
+
'DECAWM' => nil, # auto wrap mode
|
317
|
+
'DECCKM' => nil, # cursor key mode
|
318
|
+
'DECPAM' => nil, # set application keypad mode
|
319
|
+
'DECPEX' => nil, # print screen / scrolling region
|
320
|
+
'DECPFF' => nil, # sent FF after print screen, or not
|
321
|
+
'DECPNM' => nil, # set numeric keypad mode
|
322
|
+
'DECCOLM' => nil, # 132 column mode
|
323
|
+
'DECINLM' => nil, # interlace mode
|
324
|
+
'DECSCLM' => nil, # jump/smooth scroll mode
|
325
|
+
'DECSCNM' => nil, # reverse/normal screen mode
|
326
|
+
'DECSTBM' => :_code_DECSTBM, # set scrolling region
|
327
|
+
'DECTCEM' => :_code_DECTCEM, # Cursor on (set); Cursor off (reset)
|
328
|
+
}
|
329
|
+
|
330
|
+
@_callbacks = {
|
331
|
+
'BELL' => nil, # bell character received
|
332
|
+
'CLEAR' => nil, # screen cleared
|
333
|
+
'OUTPUT' => nil, # data to be sent back to originator
|
334
|
+
'ROWCHANGE' => nil, # screen row changed
|
335
|
+
'SCROLL_DOWN' => nil, # text about to move up (par=top row)
|
336
|
+
'SCROLL_UP' => nil, # text about to move down (par=bott.)
|
337
|
+
'UNKNOWN' => nil, # unknown character / sequence
|
338
|
+
'STRING' => nil, # string received
|
339
|
+
'XICONNAME' => nil, # xterm icon name changed
|
340
|
+
'XWINTITLE' => nil, # xterm window title changed
|
341
|
+
'LINEFEED' => nil, # line feed about to be processed
|
342
|
+
}
|
343
|
+
|
344
|
+
# stored arguments for callbacks
|
345
|
+
@_callbackarg = {}
|
346
|
+
|
347
|
+
# saved state for DECSC/DECRC
|
348
|
+
@_decsc = []
|
349
|
+
|
350
|
+
# saved state for CUPSV/CUPRS
|
351
|
+
@_cupsv = []
|
352
|
+
|
353
|
+
# state is XON (characters accepted)
|
354
|
+
@_xon = 1
|
355
|
+
|
356
|
+
# tab stops
|
357
|
+
@_tabstops = []
|
358
|
+
|
359
|
+
@cols, @rows = (cols > 0 ? cols : 80),
|
360
|
+
(rows > 0 ? rows : 24)
|
361
|
+
|
362
|
+
reset
|
363
|
+
end
|
364
|
+
|
365
|
+
# Call a callback function with the given parameters.
|
366
|
+
#
|
367
|
+
def callback_call(callback, arg1 = 0, arg2 = 0)
|
368
|
+
if (func = @_callbacks[callback])
|
369
|
+
priv = @_callbackarg[callback]
|
370
|
+
func.call(self, callback, arg1, arg2, priv)
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
# Set a callback function.
|
375
|
+
#
|
376
|
+
def callback_set(callback, ref, arg = nil)
|
377
|
+
@_callbacks[callback] = ref
|
378
|
+
@_callbackarg[callback] = arg
|
379
|
+
end
|
380
|
+
|
381
|
+
def on(callback_type, extra: nil, &proc)
|
382
|
+
callback_set(callback_type.to_s.upcase, proc, extra)
|
383
|
+
end
|
384
|
+
|
385
|
+
# Reset the terminal to "power-on" values.
|
386
|
+
#
|
387
|
+
def reset
|
388
|
+
@x = 1 # default X position: 1
|
389
|
+
@y = 1 # default Y position: 1
|
390
|
+
|
391
|
+
@attr = DEFAULT_ATTR_PACKED
|
392
|
+
|
393
|
+
@ti = '' # default: blank window title
|
394
|
+
@ic = '' # default: blank icon title
|
395
|
+
|
396
|
+
@srt = 1 # scrolling region top: row 1
|
397
|
+
@srb = @rows # scrolling region bottom
|
398
|
+
|
399
|
+
@opts = {} # blank all options
|
400
|
+
@opts['LINEWRAP'] = 0 # line wrapping off
|
401
|
+
@opts['LFTOCRLF'] = 0 # don't map LF -> CRLF
|
402
|
+
@opts['IGNOREXOFF'] = 1 # ignore XON/XOFF by default
|
403
|
+
|
404
|
+
@scrt = [] # blank screen text
|
405
|
+
@scra = [] # blank screen attributes
|
406
|
+
|
407
|
+
(1 .. @rows).each do |i|
|
408
|
+
@scrt[i] = "\000" * @cols # set text to NUL
|
409
|
+
@scra[i] = @attr * @cols # set attributes to default
|
410
|
+
end
|
411
|
+
|
412
|
+
@_tabstops = [] # reset tab stops
|
413
|
+
i = 1
|
414
|
+
while i < @cols
|
415
|
+
@_tabstops[i] = 1
|
416
|
+
i += 8
|
417
|
+
end
|
418
|
+
|
419
|
+
|
420
|
+
@_buf = nil # blank the esc-sequence buffer
|
421
|
+
@_inesc = '' # not in any escape sequence
|
422
|
+
@_xon = 1 # state is XON (chars accepted)
|
423
|
+
|
424
|
+
@cursor = 1 # turn cursor on
|
425
|
+
end
|
426
|
+
|
427
|
+
# Resize the terminal.
|
428
|
+
#
|
429
|
+
def resize(cols, rows)
|
430
|
+
callback_call 'CLEAR'
|
431
|
+
@cols, @rows = cols, rows
|
432
|
+
reset
|
433
|
+
end
|
434
|
+
|
435
|
+
# Return the current number of columns.
|
436
|
+
# Return the current number of rows.
|
437
|
+
attr_reader :cols, :rows
|
438
|
+
|
439
|
+
# Return the current terminal size.
|
440
|
+
#
|
441
|
+
def size
|
442
|
+
[cols, rows]
|
443
|
+
end
|
444
|
+
|
445
|
+
# Return the current cursor X/Y co-ordinates
|
446
|
+
#
|
447
|
+
attr_reader :x, :y
|
448
|
+
|
449
|
+
# Return the current cursor state (true = on, false = off).
|
450
|
+
#
|
451
|
+
def cursor?
|
452
|
+
@cursor == 1
|
453
|
+
end
|
454
|
+
|
455
|
+
# Return the current xterm title text.
|
456
|
+
#
|
457
|
+
def xtitle
|
458
|
+
@ti
|
459
|
+
end
|
460
|
+
|
461
|
+
# Return the current xterm icon text.
|
462
|
+
#
|
463
|
+
def xicon
|
464
|
+
@ic
|
465
|
+
end
|
466
|
+
|
467
|
+
# Return the current terminal status.
|
468
|
+
#
|
469
|
+
def status
|
470
|
+
[@x, @y, @attr, @ti, @ic]
|
471
|
+
end
|
472
|
+
|
473
|
+
# Process the given string, updating the terminal object and calling any
|
474
|
+
# necessary callbacks on the way.
|
475
|
+
#
|
476
|
+
def process(string)
|
477
|
+
while !string.empty?
|
478
|
+
if @_buf # in escape sequence
|
479
|
+
if string.sub!(/\A(.)/m, '')
|
480
|
+
ch = $1
|
481
|
+
if ch.match(/[\x00-\x1F]/mn)
|
482
|
+
_process_ctl(ch)
|
483
|
+
else
|
484
|
+
@_buf += ch
|
485
|
+
_process_escseq
|
486
|
+
end
|
487
|
+
end
|
488
|
+
else # not in escape sequence
|
489
|
+
if string.sub!(/\A([^\x00-\x1F\x7F\x9B]+)/mn, '')
|
490
|
+
_process_text($1)
|
491
|
+
elsif string.sub!(/\A(.)/m, '')
|
492
|
+
_process_ctl($1)
|
493
|
+
end
|
494
|
+
end
|
495
|
+
end
|
496
|
+
end
|
497
|
+
|
498
|
+
# Return the current value of the given option, or nil if it doesn't exist.
|
499
|
+
#
|
500
|
+
def option_read(option)
|
501
|
+
@opts[option]
|
502
|
+
end
|
503
|
+
|
504
|
+
# Set the value of the given option to the given value, returning the old
|
505
|
+
# value or undef if an invalid option was given.
|
506
|
+
#
|
507
|
+
def option_set(option, value)
|
508
|
+
return nil unless @opts.has_key?(option)
|
509
|
+
|
510
|
+
prev = @opts[option]
|
511
|
+
@opts[option] = value
|
512
|
+
prev
|
513
|
+
end
|
514
|
+
|
515
|
+
# Return the attributes of the given row, or undef if out of range.
|
516
|
+
#
|
517
|
+
def row_attr(row, startcol = nil, endcol = nil)
|
518
|
+
return nil unless (1 .. @rows).cover?(row)
|
519
|
+
data = @scra[row].dup
|
520
|
+
|
521
|
+
if startcol && endcol
|
522
|
+
data = data[(startcol - 1) * 2, ((endcol - startcol) + 1) * 2]
|
523
|
+
end
|
524
|
+
|
525
|
+
data
|
526
|
+
end
|
527
|
+
|
528
|
+
# Return the textual contents of the given row, or undef if out of range.
|
529
|
+
#
|
530
|
+
def row_text(row, startcol = nil, endcol = nil)
|
531
|
+
return nil if (row < 1) || (row > @rows)
|
532
|
+
|
533
|
+
text = @scrt[row].dup
|
534
|
+
|
535
|
+
if startcol && endcol
|
536
|
+
text = text[startcol - 1, (endcol - startcol) + 1]
|
537
|
+
end
|
538
|
+
text
|
539
|
+
end
|
540
|
+
|
541
|
+
# Return the textual contents of the given row, or undef if out of range,
|
542
|
+
# with unused characters represented as a space instead of \0.
|
543
|
+
#
|
544
|
+
def row_plaintext(row, startcol = nil, endcol = nil)
|
545
|
+
return nil if (row < 1) || (row > @rows)
|
546
|
+
|
547
|
+
text = @scrt[row].dup
|
548
|
+
text.gsub!(/\0/, ' ')
|
549
|
+
|
550
|
+
if startcol && endcol
|
551
|
+
text = text[startcol - 1, (endcol - startcol) + 1]
|
552
|
+
end
|
553
|
+
|
554
|
+
text
|
555
|
+
end
|
556
|
+
|
557
|
+
# Return a set of SGR escape sequences that will change colours and
|
558
|
+
# attributes from "source" to "dest" (packed attributes).
|
559
|
+
#
|
560
|
+
def sgr_change(source = DEFAULT_ATTR_PACKED, dest = DEFAULT_ATTR_PACKED)
|
561
|
+
out, off, on = '', {}, {}
|
562
|
+
|
563
|
+
return '' if source == dest
|
564
|
+
return "\e[m" if dest == DEFAULT_ATTR_PACKED
|
565
|
+
|
566
|
+
sfg, sbg, sbo, sfa, sst, sul, sbl, srv = attr_unpack(source)
|
567
|
+
dfg, dbg, dbo, dfa, _dst, dul, dbl, drv = attr_unpack(dest)
|
568
|
+
|
569
|
+
if sfg != dfg || sbg != dbg
|
570
|
+
out += sprintf("\e[m\e[3%d;4%dm", dfg, dbg)
|
571
|
+
sbo = sfa = sst = sul = sbl = srv = 0
|
572
|
+
end
|
573
|
+
|
574
|
+
if sbo > dbo || sfa > dfa
|
575
|
+
off['22'] = 1
|
576
|
+
sbo = sfa = 0
|
577
|
+
end
|
578
|
+
|
579
|
+
off['24'] = 1 if sul > dul
|
580
|
+
off['25'] = 1 if sbl > dbl
|
581
|
+
off['27'] = 1 if srv > drv
|
582
|
+
|
583
|
+
if off.size > 2
|
584
|
+
out += "\e[m"
|
585
|
+
sbo = sfa = sst = sul = sbl = srv = 0
|
586
|
+
elsif off.size > 0
|
587
|
+
out += "\e[" + off.keys.join(';') + "m"
|
588
|
+
end
|
589
|
+
|
590
|
+
on['1'] = 1 if dbo > sbo
|
591
|
+
on['2'] = 1 if (dfa > sfa) && !(dbo > sbo)
|
592
|
+
on['4'] = 1 if dul > sul
|
593
|
+
on['5'] = 1 if dbl > sbl
|
594
|
+
on['7'] = 1 if drv > srv
|
595
|
+
|
596
|
+
unless on.empty?
|
597
|
+
out += "\e[" + on.keys.join(';') + "m"
|
598
|
+
end
|
599
|
+
|
600
|
+
out
|
601
|
+
end
|
602
|
+
|
603
|
+
# Return the textual contents of the given row, or undef if out of range,
|
604
|
+
# with unused characters represented as a space instead of \0, and any
|
605
|
+
# colour or attribute changes expressed by the relevant SGR escape
|
606
|
+
# sequences.
|
607
|
+
#
|
608
|
+
def row_sgrtext(row, startcol = 1, endcol = nil)
|
609
|
+
return nil if row < 1 || row > @rows
|
610
|
+
|
611
|
+
endcol ||= @cols
|
612
|
+
|
613
|
+
return nil if endcol < startcol
|
614
|
+
return nil if startcol < 1 || endcol > @cols
|
615
|
+
|
616
|
+
row_text = @scrt[row]
|
617
|
+
row_attr = @scra[row]
|
618
|
+
|
619
|
+
text = ''
|
620
|
+
attr_cur = DEFAULT_ATTR_PACKED
|
621
|
+
|
622
|
+
while startcol <= endcol
|
623
|
+
char = row_text[startcol - 1, 1]
|
624
|
+
char.gsub!(/\0/, '')
|
625
|
+
char = ' ' unless char.match(/./)
|
626
|
+
attr_next = row_attr[(startcol - 1) * 2, 2]
|
627
|
+
text += sgr_change(attr_cur, attr_next) + char
|
628
|
+
attr_cur = attr_next
|
629
|
+
|
630
|
+
startcol += 1
|
631
|
+
end
|
632
|
+
|
633
|
+
attr_next = DEFAULT_ATTR_PACKED
|
634
|
+
text += sgr_change(attr_cur, attr_next)
|
635
|
+
|
636
|
+
text
|
637
|
+
end
|
638
|
+
|
639
|
+
# Process a string of plain text, with no special characters in it.
|
640
|
+
#
|
641
|
+
def _process_text(text)
|
642
|
+
return if @_xon == 0
|
643
|
+
|
644
|
+
width = (@cols + 1) - @x
|
645
|
+
|
646
|
+
if @opts['LINEWRAP'] == 0
|
647
|
+
return if width < 1
|
648
|
+
text = text[0, width]
|
649
|
+
@scrt[@y][@x - 1, text.size] = text
|
650
|
+
@scra[@y][2 * (@x - 1), 2 * text.size] = @attr * text.size
|
651
|
+
@x += text.size
|
652
|
+
@x = @cols if @x > @cols
|
653
|
+
|
654
|
+
callback_call('ROWCHANGE', @y)
|
655
|
+
return
|
656
|
+
end
|
657
|
+
|
658
|
+
while !text.empty? # line wrapping enabled
|
659
|
+
if width > 0
|
660
|
+
segment = text[0, width]
|
661
|
+
text[0, width] = ''
|
662
|
+
@scrt[@y][@x - 1, segment.size] = segment
|
663
|
+
@scra[@y][2 * (@x - 1), 2 * segment.size] = @attr * segment.size
|
664
|
+
@x += segment.size
|
665
|
+
else
|
666
|
+
if @x > @cols # wrap to next line
|
667
|
+
callback_call('ROWCHANGE', @y, 0)
|
668
|
+
callback_call('LINEFEED', @y, 0)
|
669
|
+
@x = 1
|
670
|
+
_move_down
|
671
|
+
end
|
672
|
+
end
|
673
|
+
width = (@cols + 1) - @x
|
674
|
+
end
|
675
|
+
callback_call('ROWCHANGE', @y, 0)
|
676
|
+
end
|
677
|
+
|
678
|
+
# Process a control character.
|
679
|
+
#
|
680
|
+
def _process_ctl(ctl)
|
681
|
+
name = @_ctlseq[ctl]
|
682
|
+
return if name.nil? # ignore unknown characters
|
683
|
+
|
684
|
+
#If we're in XOFF mode, ifgnore anything other than XON
|
685
|
+
if @_xon == 0
|
686
|
+
return if name != 'XON'
|
687
|
+
end
|
688
|
+
|
689
|
+
|
690
|
+
symbol = @_funcs[name]
|
691
|
+
if symbol.nil?
|
692
|
+
callback_call('UNKNOWN', name, ctl)
|
693
|
+
else
|
694
|
+
m = method(symbol)
|
695
|
+
send(symbol, *([name].first(m.arity)))
|
696
|
+
end
|
697
|
+
end
|
698
|
+
|
699
|
+
# Check the escape-sequence buffer, and process it if necessary.
|
700
|
+
#
|
701
|
+
def _process_escseq
|
702
|
+
params = []
|
703
|
+
|
704
|
+
return if @_buf.nil? || @_buf.empty?
|
705
|
+
return if @_xon == 0
|
706
|
+
|
707
|
+
if @_inesc == 'OSC'
|
708
|
+
if @_buf.match(/\A0;([^\007]*)(?:\007|\033\\)/m)
|
709
|
+
dat = $1 # icon & window
|
710
|
+
callback_call('XWINTITLE', dat)
|
711
|
+
callback_call('XICONNAME', dat)
|
712
|
+
@ic = dat
|
713
|
+
@ti = dat
|
714
|
+
@_buf = nil
|
715
|
+
@_inesc = ''
|
716
|
+
elsif @_buf.match(/\A1;([^\007]*)(?:\007|\033\\)/m)
|
717
|
+
dat = $1 # set icon name
|
718
|
+
callback_call('XICONNAME', dat)
|
719
|
+
@ic = dat
|
720
|
+
@_buf = nil
|
721
|
+
@_inesc = ''
|
722
|
+
elsif @_buf.match(/\A2;([^\007]*)(?:\007|\033\\)/m)
|
723
|
+
dat = $1 # set window title
|
724
|
+
callback_call('XWINTITLE', dat)
|
725
|
+
@ti = dat
|
726
|
+
@_buf = nil
|
727
|
+
@_inesc = ''
|
728
|
+
elsif @_buf.match(/\A\d+;([^\007]*)(?:\007|\033\\)/m)
|
729
|
+
# unknown OSC
|
730
|
+
callback_call('UNKNOWN', 'OSC', "\033]" + @_buf)
|
731
|
+
@_buf = nil
|
732
|
+
@_inesc = ''
|
733
|
+
elsif @_buf.size > 1024 # OSC too long
|
734
|
+
callback_call('UNKNOWN', 'OSC', "\033]" + @_buf)
|
735
|
+
@_buf = nil
|
736
|
+
@_inesc = ''
|
737
|
+
end
|
738
|
+
elsif @_inesc == 'CSI' # in CSI sequence
|
739
|
+
@_csiseq.keys.each do |suffix|
|
740
|
+
next if @_buf.size < suffix.size
|
741
|
+
next if @_buf[@_buf.size - suffix.size, suffix.size] != suffix
|
742
|
+
|
743
|
+
@_buf = @_buf[0, @_buf.size - suffix.size]
|
744
|
+
|
745
|
+
name = @_csiseq[suffix]
|
746
|
+
func = @_funcs[name]
|
747
|
+
|
748
|
+
if func.nil? # unsupported sequence
|
749
|
+
callback_call('UNKNOWN', name, "\033[" + @_buf + suffix)
|
750
|
+
@_buf = nil
|
751
|
+
@_inesc = ''
|
752
|
+
return
|
753
|
+
end
|
754
|
+
|
755
|
+
params = @_buf.split(';').map(&:to_i)
|
756
|
+
@_buf = nil
|
757
|
+
@_inesc = ''
|
758
|
+
|
759
|
+
send(func, *params)
|
760
|
+
return
|
761
|
+
end
|
762
|
+
|
763
|
+
if @_buf.size > 64 # abort CSI sequence if too long
|
764
|
+
callback_call('UNKNOWN', 'CSI', "\033[" + @_buf)
|
765
|
+
@_buf = nil
|
766
|
+
@_inesc = ''
|
767
|
+
end
|
768
|
+
elsif @_inesc =~ /_ST\z/m
|
769
|
+
if @_buf.sub!(/\033\\\z/m, '')
|
770
|
+
@_inesc.sub!(/_ST\z/m, '')
|
771
|
+
callback_call('STRING', @_inesc, @_buf)
|
772
|
+
@_buf = nil
|
773
|
+
@_inesc = ''
|
774
|
+
elsif @_buf.size > 1024 # string too long
|
775
|
+
@_inesc.sub!(/_ST\z/m, '')
|
776
|
+
callback_call('STRING', @_inesc, @_buf)
|
777
|
+
@_buf = nil
|
778
|
+
@_inesc = ''
|
779
|
+
end
|
780
|
+
else # in ESC sequence
|
781
|
+
@_escseq.keys.each do |prefix|
|
782
|
+
next if @_buf[0, prefix.size] != prefix
|
783
|
+
|
784
|
+
name = @_escseq[prefix]
|
785
|
+
func = @_funcs[name]
|
786
|
+
if func.nil? # unsupported sequence
|
787
|
+
callback_call('UNKNOWN', name, "\033" + @_buf)
|
788
|
+
@_buf = nil
|
789
|
+
@_inesc = ''
|
790
|
+
return
|
791
|
+
end
|
792
|
+
@_buf = nil
|
793
|
+
@_inesc = ''
|
794
|
+
send(func)
|
795
|
+
return
|
796
|
+
end
|
797
|
+
|
798
|
+
if @_buf.size > 8 # abort ESC sequence if too long
|
799
|
+
callback_call('UNKNOWN', 'ESC', "\033" + @_buf)
|
800
|
+
@_buf = nil
|
801
|
+
@_inesc = ''
|
802
|
+
end
|
803
|
+
end
|
804
|
+
end
|
805
|
+
|
806
|
+
# Scroll the scrolling region up such that the text in the scrolling region
|
807
|
+
# moves down, by the given number of lines.
|
808
|
+
#
|
809
|
+
def _scroll_up(lines)
|
810
|
+
return if lines < 1
|
811
|
+
callback_call('SCROLL_UP', @srb, lines)
|
812
|
+
|
813
|
+
i = @srb
|
814
|
+
while i >= @srt + lines
|
815
|
+
@scrt[i] = @scrt[i - lines]
|
816
|
+
@scra[i] = @scra[i - lines]
|
817
|
+
i -= 1
|
818
|
+
end
|
819
|
+
|
820
|
+
attr = DEFAULT_ATTR_PACKED
|
821
|
+
|
822
|
+
i = @srt
|
823
|
+
while (i <= @srb) && (i < (@srt + lines))
|
824
|
+
@scrt[i] = "\000" * @cols # blank new lines
|
825
|
+
@scra[i] = attr * @cols # wipe attributes of new lines
|
826
|
+
i += 1
|
827
|
+
end
|
828
|
+
end
|
829
|
+
|
830
|
+
# Scroll the scrolling region down such that the text in the scrolling region
|
831
|
+
# moves up, by the given number of lines.
|
832
|
+
#
|
833
|
+
def _scroll_down(lines)
|
834
|
+
callback_call('SCROLL_DOWN', @srt, lines)
|
835
|
+
|
836
|
+
i = @srt
|
837
|
+
while i <= (@srb - lines)
|
838
|
+
@scrt[i] = @scrt[i + lines]
|
839
|
+
@scra[i] = @scra[i + lines]
|
840
|
+
i += 1
|
841
|
+
end
|
842
|
+
|
843
|
+
attr = DEFAULT_ATTR_PACKED
|
844
|
+
|
845
|
+
i = @srb
|
846
|
+
while (i >= @srt) && (i > (@srb - lines))
|
847
|
+
@scrt[i] = "\000" * @cols # blank new lines
|
848
|
+
@scra[i] = attr * @cols # wipe attributes of new lines
|
849
|
+
i -= 1
|
850
|
+
end
|
851
|
+
end
|
852
|
+
|
853
|
+
# Move the cursor up the given number of lines, without triggering a GOTO
|
854
|
+
# callback, taking scrolling into account.
|
855
|
+
#
|
856
|
+
def _move_up(num = 1)
|
857
|
+
num = [num, 1].max
|
858
|
+
|
859
|
+
@y -= num
|
860
|
+
return if @y >= @srt
|
861
|
+
|
862
|
+
_scroll_up(@srt - @y) # scroll
|
863
|
+
@y = @srt
|
864
|
+
end
|
865
|
+
|
866
|
+
# Move the cursor down the given number of lines, without triggering a GOTO
|
867
|
+
# callback, taking scrolling into account.
|
868
|
+
#
|
869
|
+
def _move_down(num = 1)
|
870
|
+
num = [num, 1].max
|
871
|
+
|
872
|
+
@y += num
|
873
|
+
return if @y <= @srb
|
874
|
+
|
875
|
+
_scroll_down(@y - @srb) # scroll
|
876
|
+
@y = @srb
|
877
|
+
end
|
878
|
+
|
879
|
+
def _code_BEL # beep
|
880
|
+
if @_buf && @_inesc == 'OSC'
|
881
|
+
# CSI OSC can be terminated with a BEL
|
882
|
+
@_buf += "\007"
|
883
|
+
_process_escseq()
|
884
|
+
else
|
885
|
+
callback_call('BELL')
|
886
|
+
end
|
887
|
+
end
|
888
|
+
|
889
|
+
def _code_BS # move left 1 character
|
890
|
+
@x -= 1
|
891
|
+
@x = 1 if @x < 1
|
892
|
+
end
|
893
|
+
|
894
|
+
def _code_CAN # cancel escape sequence
|
895
|
+
@_buf = nil
|
896
|
+
@_inesc = ''
|
897
|
+
end
|
898
|
+
|
899
|
+
def _code_TBC(num = nil) # clear tab stop (CSI 3 g = clear all stops)
|
900
|
+
if num == 3
|
901
|
+
@_tabstops = []
|
902
|
+
else
|
903
|
+
@_tabstops[@x] = nil
|
904
|
+
end
|
905
|
+
end
|
906
|
+
|
907
|
+
def _code_CHA(col = 1) # move to column in current row
|
908
|
+
return if @x == col
|
909
|
+
|
910
|
+
callback_call('GOTO', col, @y)
|
911
|
+
|
912
|
+
@x = col
|
913
|
+
@x = 1 if @x < 1
|
914
|
+
@x = @cols if (@x > @cols)
|
915
|
+
end
|
916
|
+
|
917
|
+
def _code_CNL(num = 1) # move cursor down and to column 1
|
918
|
+
callback_call('GOTO', 1, @y + num)
|
919
|
+
@x = 1
|
920
|
+
_move_down(num)
|
921
|
+
end
|
922
|
+
|
923
|
+
def _code_CPL(num = 1) # move cursor up and to column 1
|
924
|
+
callback_call('GOTO', @x, @y - num)
|
925
|
+
@x = 1
|
926
|
+
_move_up(num)
|
927
|
+
end
|
928
|
+
|
929
|
+
def _code_CR # carriage return
|
930
|
+
@x = 1
|
931
|
+
end
|
932
|
+
|
933
|
+
def _code_CSI # ESC [
|
934
|
+
@_buf = '' # restart ESC buffering
|
935
|
+
@_inesc = 'CSI' # ...for a CSI, not an ESC
|
936
|
+
end
|
937
|
+
|
938
|
+
def _code_CUB(num = 1) # move cursor left
|
939
|
+
num = [num, 1].max
|
940
|
+
|
941
|
+
callback_call('GOTO', @x - num, @y)
|
942
|
+
|
943
|
+
@x -= num
|
944
|
+
@x = [@x, 1].max
|
945
|
+
end
|
946
|
+
|
947
|
+
def _code_CUD(num = 1) # move cursor down
|
948
|
+
num = [num, 1].max
|
949
|
+
callback_call('GOTO', @x, @y + num)
|
950
|
+
_move_down(num)
|
951
|
+
end
|
952
|
+
|
953
|
+
def _code_CUF(num = 1) # move cursor right
|
954
|
+
num = [num, 1].max
|
955
|
+
callback_call('GOTO', @x + num, @y)
|
956
|
+
@x += num
|
957
|
+
@x = @cols if (@x > @cols)
|
958
|
+
end
|
959
|
+
|
960
|
+
def _code_CUP(row = 1, col = 1) # move cursor to row, column
|
961
|
+
row = [row, 1].max
|
962
|
+
col = [col, 1].max
|
963
|
+
|
964
|
+
row = @rows if row > @rows
|
965
|
+
col = @cols if col > @cols
|
966
|
+
|
967
|
+
callback_call('GOTO', col, row)
|
968
|
+
|
969
|
+
@x, @y = col, row
|
970
|
+
end
|
971
|
+
|
972
|
+
def _code_RI # reverse line feed
|
973
|
+
callback_call('GOTO', @x, @y - 1)
|
974
|
+
_move_up
|
975
|
+
end
|
976
|
+
|
977
|
+
def _code_CUU(num = 1) # move cursor up
|
978
|
+
num = [num, 1].max
|
979
|
+
callback_call('GOTO', @x, @y - num)
|
980
|
+
_move_up(num)
|
981
|
+
end
|
982
|
+
|
983
|
+
def _code_DA # return ESC [ ? 6 c (VT102)
|
984
|
+
callback_call('OUTPUT', "\033[?6c", 0)
|
985
|
+
end
|
986
|
+
|
987
|
+
def _code_DCH(num = 1) # delete characters on current line
|
988
|
+
num = [num, 1].max
|
989
|
+
|
990
|
+
width = @cols + 1 - @x
|
991
|
+
todel = num
|
992
|
+
todel = width if todel > width
|
993
|
+
|
994
|
+
line = @scrt[@y]
|
995
|
+
lsub, rsub = '', ''
|
996
|
+
lsub = line[0, @x - 1] if @x > 1
|
997
|
+
rsub = line[(@x - 1 + todel) .. -1]
|
998
|
+
@scrt[@y] = lsub + rsub + ("\0" * todel)
|
999
|
+
|
1000
|
+
line = @scra[@y]
|
1001
|
+
lsub, rsub = '', ''
|
1002
|
+
lsub = line[0, 2 * (@x - 1)] if @x > 1
|
1003
|
+
rsub = line[(2 * (@x - 1 + todel)) .. -1]
|
1004
|
+
@scra[@y] = lsub + rsub + (DEFAULT_ATTR_PACKED * todel)
|
1005
|
+
|
1006
|
+
callback_call('ROWCHANGE', @y, 0)
|
1007
|
+
end
|
1008
|
+
|
1009
|
+
def _code_DCS # device control string (ignored)
|
1010
|
+
@_buf = ''
|
1011
|
+
@_inesc = 'DCS_ST'
|
1012
|
+
end
|
1013
|
+
|
1014
|
+
def _code_DECSTBM(top = 1, bottom = nil) # set scrolling region
|
1015
|
+
bottom ||= @rows
|
1016
|
+
top = [top, 1].max
|
1017
|
+
bottom = [bottom, 1].max
|
1018
|
+
|
1019
|
+
top = @rows if top > @rows
|
1020
|
+
bottom = @rows if bottom > @rows
|
1021
|
+
|
1022
|
+
(top, bottom = bottom, top) if bottom < top
|
1023
|
+
|
1024
|
+
@srb, @srt = bottom, top
|
1025
|
+
end
|
1026
|
+
|
1027
|
+
def _code_DECTCEM(cursor) # Cursor on (set); Cursor off (reset)
|
1028
|
+
@cursor = cursor
|
1029
|
+
end
|
1030
|
+
|
1031
|
+
def _code_IGN # ignored control sequence
|
1032
|
+
end
|
1033
|
+
|
1034
|
+
def _code_DL(lines = 1) # delete lines
|
1035
|
+
lines = [lines, 1].max
|
1036
|
+
|
1037
|
+
attr = DEFAULT_ATTR_PACKED
|
1038
|
+
|
1039
|
+
scrb = @srb
|
1040
|
+
scrb = @rows if @y > @srb
|
1041
|
+
scrb = @srt - 1 if @y < @srt
|
1042
|
+
|
1043
|
+
row = @y
|
1044
|
+
while row <= scrb - lines
|
1045
|
+
@scrt[row] = @scrt[row + lines]
|
1046
|
+
@scra[row] = @scra[row + lines]
|
1047
|
+
callback_call('ROWCHANGE', row, 0)
|
1048
|
+
row += 1
|
1049
|
+
end
|
1050
|
+
|
1051
|
+
row = scrb
|
1052
|
+
while row > (scrb - lines) && row >= @y
|
1053
|
+
@scrt[row] = "\000" * @cols
|
1054
|
+
@scra[row] = attr * @cols
|
1055
|
+
callback_call('ROWCHANGE', row, 0)
|
1056
|
+
row -= 1
|
1057
|
+
end
|
1058
|
+
end
|
1059
|
+
|
1060
|
+
def _code_DSR(num = 5) # device status report
|
1061
|
+
if num == 6 # CPR - cursor position report
|
1062
|
+
callback_call('OUTPUT', "\e[#{@y};#{@x}R", 0)
|
1063
|
+
elsif num == 5 # DSR - reply ESC [ 0 n
|
1064
|
+
callback_call('OUTPUT', "\e[0n", 0)
|
1065
|
+
end
|
1066
|
+
end
|
1067
|
+
|
1068
|
+
def _code_ECH(num = 1) # erase characters on current line
|
1069
|
+
num = [num, 1].max
|
1070
|
+
|
1071
|
+
width = @cols + 1 - @x
|
1072
|
+
todel = num
|
1073
|
+
todel = width if todel > width
|
1074
|
+
|
1075
|
+
line = @scrt[@y]
|
1076
|
+
lsub, rsub = '', ''
|
1077
|
+
lsub = line[0, @x - 1] if @x > 1
|
1078
|
+
rsub = line[(@x - 1 + todel) .. -1]
|
1079
|
+
@scrt[@y] = lsub + ("\0" * todel) + rsub
|
1080
|
+
|
1081
|
+
|
1082
|
+
line = @scra[@y]
|
1083
|
+
lsub, rsub = '', ''
|
1084
|
+
lsub = line[0, 2 * (@x - 1)] if @x > 1
|
1085
|
+
rsub = line[(2 * (@x - 1 + todel)) .. -1]
|
1086
|
+
|
1087
|
+
@scra[@y] = lsub + (DEFAULT_ATTR_PACKED * todel) + rsub
|
1088
|
+
callback_call('ROWCHANGE', @y, 0)
|
1089
|
+
end
|
1090
|
+
|
1091
|
+
def _code_ED(num = 0) # erase display
|
1092
|
+
attr = DEFAULT_ATTR_PACKED
|
1093
|
+
|
1094
|
+
# Wipe-cursor-to-end is the same as clear-whole-screen if cursor at top left
|
1095
|
+
#
|
1096
|
+
num = 2 if (num == 0) && (@x == 1) && (@y == 1)
|
1097
|
+
|
1098
|
+
if num == 0 # 0 = cursor to end
|
1099
|
+
@scrt[@y] = @scrt[@y][0, @x - 1] + ("\0" * (@cols + 1 - @x))
|
1100
|
+
@scra[@y] = @scra[@y][0, 2 * (@x - 1)] + (attr * (cols + 1 - @x))
|
1101
|
+
callback_call('ROWCHANGE', @y, 0)
|
1102
|
+
|
1103
|
+
row = @y + 1
|
1104
|
+
while row <= @rows
|
1105
|
+
@scrt[row] = "\0" * @cols
|
1106
|
+
@scra[row] = attr * @cols
|
1107
|
+
callback_call('ROWCHANGE', row, 0)
|
1108
|
+
row += 1
|
1109
|
+
end
|
1110
|
+
elsif num == 1 # 1 = start to cursor
|
1111
|
+
row = 1
|
1112
|
+
while row < @y
|
1113
|
+
@scrt[row] = "\0" * @cols
|
1114
|
+
@scra[row] = attr * @cols
|
1115
|
+
callback_call('ROWCHANGE', row, 0)
|
1116
|
+
row += 1
|
1117
|
+
end
|
1118
|
+
|
1119
|
+
@scrt[@y] = ("\0" * @x) + @scrt[@y][@x .. -1]
|
1120
|
+
@scra[@y] = (attr * @x) + @scra[@y][2 * @x .. -1]
|
1121
|
+
callback_call('ROWCHANGE', @y, 0)
|
1122
|
+
else # 2 = whole display
|
1123
|
+
callback_call('CLEAR', 0, 0)
|
1124
|
+
row = 1
|
1125
|
+
while row <= rows
|
1126
|
+
@scrt[row] = "\0" * @cols
|
1127
|
+
@scra[row] = attr * @cols
|
1128
|
+
row += 1
|
1129
|
+
end
|
1130
|
+
end
|
1131
|
+
end
|
1132
|
+
|
1133
|
+
def _code_EL(num = 0) # erase line
|
1134
|
+
attr = DEFAULT_ATTR_PACKED
|
1135
|
+
|
1136
|
+
if num == 0 # 0 = cursor to end of line
|
1137
|
+
@scrt[@y] = @scrt[@y][0, @x - 1] + ("\0" * (@cols + 1 - @x))
|
1138
|
+
@scra[@y] = @scra[@y][0, 2 * (@x - 1)] + (attr * (@cols + 1 - @x))
|
1139
|
+
callback_call('ROWCHANGE', @y, 0)
|
1140
|
+
elsif num == 1 # 1 = start of line to cursor
|
1141
|
+
@scrt[@y] = ("\0" * @x) + @scrt[@y][@x .. -1]
|
1142
|
+
@scra[@y] = (attr * @x) + @scra[@y][(2 * @x) .. -1]
|
1143
|
+
callback_call('ROWCHANGE', @y, 0)
|
1144
|
+
else # 2 = whole line
|
1145
|
+
@scrt[@y] = "\0" * @cols
|
1146
|
+
@scra[@y] = attr * @cols
|
1147
|
+
callback_call('ROWCHANGE', @y, 0)
|
1148
|
+
end
|
1149
|
+
end
|
1150
|
+
|
1151
|
+
def _code_ESC # start escape sequence
|
1152
|
+
if @_buf && @_inesc.match(/OSC|_ST/)
|
1153
|
+
# Some sequences are terminated with an ST
|
1154
|
+
@_buf += "\033"
|
1155
|
+
_process_escseq
|
1156
|
+
return
|
1157
|
+
end
|
1158
|
+
|
1159
|
+
@_buf = '' # set ESC buffer
|
1160
|
+
@_inesc = 'ESC' # ...for ESC, not CSI
|
1161
|
+
end
|
1162
|
+
|
1163
|
+
def _code_LF # line feed
|
1164
|
+
_code_CR if @opts['LFTOCRLF'] != 0
|
1165
|
+
|
1166
|
+
callback_call('LINEFEED', @y, 0)
|
1167
|
+
_move_down
|
1168
|
+
end
|
1169
|
+
|
1170
|
+
def _code_NEL # newline
|
1171
|
+
_code_CR # cursor always to start
|
1172
|
+
_code_LF # standard line feed
|
1173
|
+
end
|
1174
|
+
|
1175
|
+
def _code_HT # horizontal tab to next tab stop
|
1176
|
+
if @opts['LINEWRAP'] != 0 && @x >= @cols
|
1177
|
+
callback_call('LINEFEED', @y, 0)
|
1178
|
+
@x = 1
|
1179
|
+
_move_down
|
1180
|
+
end
|
1181
|
+
|
1182
|
+
newx = @x + 1
|
1183
|
+
while newx < @cols && @_tabstops[newx] != 1
|
1184
|
+
newx += 1
|
1185
|
+
end
|
1186
|
+
|
1187
|
+
width = (@cols + 1) - @x
|
1188
|
+
spaces = newx - @x
|
1189
|
+
spaces = width + 1 if spaces > width
|
1190
|
+
|
1191
|
+
if spaces > 0
|
1192
|
+
@x += spaces
|
1193
|
+
@x = @cols if @x > @cols
|
1194
|
+
end
|
1195
|
+
end
|
1196
|
+
|
1197
|
+
def _code_HTS # set tab stop at current column
|
1198
|
+
@_tabstops[@x] = 1
|
1199
|
+
end
|
1200
|
+
|
1201
|
+
def _code_ICH(num = 1) # insert blank characters
|
1202
|
+
num = [num, 1].max
|
1203
|
+
|
1204
|
+
width = @cols + 1 - @x
|
1205
|
+
toins = num
|
1206
|
+
toins = width if toins > width
|
1207
|
+
|
1208
|
+
line = @scrt[@y]
|
1209
|
+
lsub, rsub = '', ''
|
1210
|
+
lsub = line[0, @x - 1] if @x > 1
|
1211
|
+
rsub = line[@x - 1, width - toins]
|
1212
|
+
@scrt[@y] = lsub + ("\0" * toins) + rsub
|
1213
|
+
|
1214
|
+
attr = DEFAULT_ATTR_PACKED
|
1215
|
+
line = @scra[@y]
|
1216
|
+
lsub, rsub = '', ''
|
1217
|
+
lsub = line[0, 2 * (@x - 1)] if @x > 1
|
1218
|
+
rsub = line[2 * (@x - 1), 2 * (width - toins)]
|
1219
|
+
@scra[@y] = lsub + (attr * toins) + rsub
|
1220
|
+
|
1221
|
+
callback_call('ROWCHANGE', @y, 0)
|
1222
|
+
end
|
1223
|
+
|
1224
|
+
def _code_IL(lines = 1) # insert blank lines
|
1225
|
+
lines = [lines, 1].max
|
1226
|
+
|
1227
|
+
attr = DEFAULT_ATTR_PACKED
|
1228
|
+
|
1229
|
+
scrb = @srb
|
1230
|
+
scrb = @rows if @y > @srb
|
1231
|
+
scrb = @srt - 1 if @y < @srt
|
1232
|
+
|
1233
|
+
row = scrb
|
1234
|
+
while row >= y + lines
|
1235
|
+
@scrt[row] = @scrt[row - lines]
|
1236
|
+
@scra[row] = @scra[row - lines]
|
1237
|
+
callback_call('ROWCHANGE', row, 0)
|
1238
|
+
|
1239
|
+
row -= 1
|
1240
|
+
end
|
1241
|
+
|
1242
|
+
row = @y
|
1243
|
+
while (row <= scrb) && (row < (@y + lines))
|
1244
|
+
@scrt[row] = "\000" * @cols
|
1245
|
+
@scra[row] = attr * @cols
|
1246
|
+
callback_call('ROWCHANGE', row, 0)
|
1247
|
+
row += 1
|
1248
|
+
end
|
1249
|
+
end
|
1250
|
+
|
1251
|
+
def _code_PM # privacy message (ignored)
|
1252
|
+
@_buf = ''
|
1253
|
+
@_inesc = 'PM_ST'
|
1254
|
+
end
|
1255
|
+
|
1256
|
+
def _code_APC # application program command (ignored)
|
1257
|
+
@_buf = ''
|
1258
|
+
@_inesc = 'APC_ST'
|
1259
|
+
end
|
1260
|
+
|
1261
|
+
def _code_OSC # operating system command
|
1262
|
+
@_buf = '' # restart buffering
|
1263
|
+
@_inesc = 'OSC' # ...for OSC, not ESC or CSI
|
1264
|
+
end
|
1265
|
+
|
1266
|
+
def _code_RIS # reset
|
1267
|
+
reset
|
1268
|
+
end
|
1269
|
+
|
1270
|
+
def _toggle_mode(flag, modes) # set/reset modes
|
1271
|
+
# Transcription Note: This isn't really a loop
|
1272
|
+
fail ArgumentError, "only first mode applied" if modes.size > 1
|
1273
|
+
|
1274
|
+
modes.each do |mode|
|
1275
|
+
name = @_modeseq[mode]
|
1276
|
+
func = nil
|
1277
|
+
func = @_funcs[name] unless name.nil?
|
1278
|
+
|
1279
|
+
if func.nil?
|
1280
|
+
callback_call('UNKNOWN', name, "\033[#{mode}" + (flag ? "h" : "l"))
|
1281
|
+
@_buf = nil
|
1282
|
+
@_inesc = ''
|
1283
|
+
return
|
1284
|
+
end
|
1285
|
+
|
1286
|
+
@_buf = nil
|
1287
|
+
@_inesc = ''
|
1288
|
+
send(func, flag)
|
1289
|
+
return
|
1290
|
+
end
|
1291
|
+
end
|
1292
|
+
|
1293
|
+
def _code_RM(*args) # reset mode
|
1294
|
+
_toggle_mode(0, args)
|
1295
|
+
end
|
1296
|
+
|
1297
|
+
def _code_SM(*args) # set mode
|
1298
|
+
_toggle_mode(1, args)
|
1299
|
+
end
|
1300
|
+
|
1301
|
+
def _code_SGR(*parms) # set graphic rendition
|
1302
|
+
fg, bg, bo, fa, st, ul, bl, rv = attr_unpack(@attr)
|
1303
|
+
|
1304
|
+
parms = [0] if parms.empty? # ESC [ m = ESC [ 0 m
|
1305
|
+
parms.each do |val|
|
1306
|
+
case val
|
1307
|
+
when 0 # reset all attributes
|
1308
|
+
fg, bg, bo, fa, st, ul, bl, rv = DEFAULT_ATTR
|
1309
|
+
when 1 # bold ON
|
1310
|
+
bo, fa = 1, 0
|
1311
|
+
when 2 # faint ON
|
1312
|
+
bo, fa = 0, 1
|
1313
|
+
when 4 # underline ON
|
1314
|
+
ul = 1
|
1315
|
+
when 5 # blink ON
|
1316
|
+
bl = 1
|
1317
|
+
when 7 # reverse video ON
|
1318
|
+
rv = 1
|
1319
|
+
when 21..22 # normal intensity
|
1320
|
+
bo, fa = 0, 0
|
1321
|
+
when 24 # underline OFF
|
1322
|
+
ul = 0
|
1323
|
+
when 25 # blink OFF
|
1324
|
+
bl = 0
|
1325
|
+
when 27 # reverse video OFF
|
1326
|
+
rv = 0
|
1327
|
+
when 30..37 # set foreground colour
|
1328
|
+
fg = val - 30
|
1329
|
+
when 38 # underline on, default fg
|
1330
|
+
ul, fg = 1, 7
|
1331
|
+
when 39 # underline off, default fg
|
1332
|
+
ul, fg = 0, 7
|
1333
|
+
when 40..47 # set background colour
|
1334
|
+
bg = val - 40
|
1335
|
+
when 49 # default background
|
1336
|
+
bg = 0
|
1337
|
+
end
|
1338
|
+
end
|
1339
|
+
|
1340
|
+
@attr = attr_pack(fg, bg, bo, fa, st, ul, bl, rv)
|
1341
|
+
end
|
1342
|
+
|
1343
|
+
def _code_VPA(row = 1) # move to row (current column)
|
1344
|
+
return if @y == row
|
1345
|
+
|
1346
|
+
@y = [row, 1].max
|
1347
|
+
@y = @rows if @y > @rows
|
1348
|
+
end
|
1349
|
+
|
1350
|
+
def _code_DECALN # fill screen with E's
|
1351
|
+
attr = DEFAULT_ATTR_PACKED
|
1352
|
+
|
1353
|
+
(1 .. @rows).each do |row|
|
1354
|
+
@scrt[row] = 'E' * @cols
|
1355
|
+
@scra[row] = attr * @cols
|
1356
|
+
callback_call('ROWCHANGE', @y, 0)
|
1357
|
+
end
|
1358
|
+
|
1359
|
+
@x = @y = 1
|
1360
|
+
end
|
1361
|
+
|
1362
|
+
def _code_DECSC # save state
|
1363
|
+
@_decsc.push([@x, @y, @attr, @ti, @ic, @cursor])
|
1364
|
+
end
|
1365
|
+
|
1366
|
+
def _code_DECRC # restore most recently saved state
|
1367
|
+
return if @_decsc.empty?
|
1368
|
+
@x, @y, @attr, @ti, @ic, @cursor = @_decsc.pop
|
1369
|
+
end
|
1370
|
+
|
1371
|
+
def _code_CUPSV # save cursor position
|
1372
|
+
@_cupsv.push([@x, @y])
|
1373
|
+
end
|
1374
|
+
|
1375
|
+
def _code_CUPRS # restore cursor position
|
1376
|
+
return if @_cupsv.empty?
|
1377
|
+
@x, @y = @_cupsv.pop
|
1378
|
+
end
|
1379
|
+
|
1380
|
+
def _code_XON # resume character processing
|
1381
|
+
@_xon = 1
|
1382
|
+
end
|
1383
|
+
|
1384
|
+
def _code_XOFF # stop character processing
|
1385
|
+
return if @opts['IGNOREXOFF'] == 1
|
1386
|
+
@_xon = 0
|
1387
|
+
end
|
1388
|
+
end
|
1389
|
+
end
|