Skip to content

Commit 36c1e5c

Browse files
committed
implement DCS tmux; ESC Pt ST. handle STs better.
1 parent d161794 commit 36c1e5c

File tree

1 file changed

+102
-29
lines changed

1 file changed

+102
-29
lines changed

src/term.js

Lines changed: 102 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,8 @@ var normal = 0
124124
, osc = 3
125125
, charset = 4
126126
, dcs = 5
127-
, ignore = 6;
127+
, ignore = 6
128+
, UDK = { type: 'udk' };
128129

129130
/**
130131
* Terminal
@@ -1440,7 +1441,7 @@ Terminal.prototype.write = function(data) {
14401441

14411442
// this.log(JSON.stringify(data.replace(/\x1b/g, '^[')));
14421443

1443-
for (; i < l; i++) {
1444+
for (; i < l; i++, this.lch = ch) {
14441445
ch = data[i];
14451446
switch (this.state) {
14461447
case normal:
@@ -1556,7 +1557,8 @@ Terminal.prototype.write = function(data) {
15561557
// ESC P Device Control String ( DCS is 0x90).
15571558
case 'P':
15581559
this.params = [];
1559-
this.currentParam = 0;
1560+
this.prefix = '';
1561+
this.currentParam = '';
15601562
this.state = dcs;
15611563
break;
15621564

@@ -1779,8 +1781,14 @@ Terminal.prototype.write = function(data) {
17791781
// OSC Ps ; Pt ST
17801782
// OSC Ps ; Pt BEL
17811783
// Set Text Parameters.
1782-
if (ch === '\x1b' || ch === '\x07') {
1783-
if (ch === '\x1b') i++;
1784+
if ((this.lch === '\x1b' && ch === '\\') || ch === '\x07') {
1785+
if (this.lch === '\x1b') {
1786+
if (typeof this.currentParam === 'string') {
1787+
this.currentParam = this.currentParam.slice(0, -1);
1788+
} else if (typeof this.currentParam == 'number') {
1789+
this.currentParam = (this.currentParam - ('\x1b'.charCodeAt(0) - 48)) / 10;
1790+
}
1791+
}
17841792

17851793
this.params.push(this.currentParam);
17861794

@@ -2312,94 +2320,159 @@ Terminal.prototype.write = function(data) {
23122320
break;
23132321

23142322
case dcs:
2315-
if (ch === '\x1b' || ch === '\x07') {
2316-
if (ch === '\x1b') i++;
2323+
if ((this.lch === '\x1b' && ch === '\\') || ch === '\x07') {
2324+
// Workarounds:
2325+
if (this.prefix === 'tmux;\x1b') {
2326+
// `DCS tmux; Pt ST` may contain a Pt with an ST
2327+
// XXX Does tmux work this way?
2328+
// if (this.lch === '\x1b' & data[i + 1] === '\x1b' && data[i + 2] === '\\') {
2329+
// this.currentParam += ch;
2330+
// continue;
2331+
// }
2332+
// Tmux only accepts ST, not BEL:
2333+
if (ch === '\x07') {
2334+
this.currentParam += ch;
2335+
continue;
2336+
}
2337+
}
2338+
2339+
if (this.lch === '\x1b') {
2340+
if (typeof this.currentParam === 'string') {
2341+
this.currentParam = this.currentParam.slice(0, -1);
2342+
} else if (typeof this.currentParam == 'number') {
2343+
this.currentParam = (this.currentParam - ('\x1b'.charCodeAt(0) - 48)) / 10;
2344+
}
2345+
}
2346+
2347+
this.params.push(this.currentParam);
2348+
this.currentParam = '';
2349+
2350+
var pt = this.params[this.params.length - 1];
23172351

23182352
switch (this.prefix) {
23192353
// User-Defined Keys (DECUDK).
2320-
case '':
2354+
// DCS Ps; Ps| Pt ST
2355+
case UDK:
2356+
this.emit('udk', {
2357+
clearAll: this.params[0] === 0,
2358+
eraseBelow: this.params[0] === 1,
2359+
lockKeys: this.params[1] === 0,
2360+
dontLockKeys: this.params[1] === 1,
2361+
keyList: (this.params[2] + '').split(';').map(function(part) {
2362+
part = part.split('/');
2363+
return {
2364+
keyCode: part[0],
2365+
hexKeyValue: part[1]
2366+
};
2367+
})
2368+
});
23212369
break;
23222370

23232371
// Request Status String (DECRQSS).
2372+
// DCS $ q Pt ST
23242373
// test: echo -e '\eP$q"p\e\\'
23252374
case '$q':
2326-
var pt = this.currentParam
2327-
, valid = false;
2375+
var valid = 0;
23282376

23292377
switch (pt) {
23302378
// DECSCA
2379+
// CSI Ps " q
23312380
case '"q':
23322381
pt = '0"q';
2382+
valid = 1;
23332383
break;
23342384

23352385
// DECSCL
2386+
// CSI Ps ; Ps " p
23362387
case '"p':
2337-
pt = '61"p';
2388+
pt = '61;0"p';
2389+
valid = 1;
23382390
break;
23392391

23402392
// DECSTBM
2393+
// CSI Ps ; Ps r
23412394
case 'r':
23422395
pt = ''
23432396
+ (this.scrollTop + 1)
23442397
+ ';'
23452398
+ (this.scrollBottom + 1)
23462399
+ 'r';
2400+
valid = 1;
23472401
break;
23482402

23492403
// SGR
2404+
// CSI Pm m
23502405
case 'm':
2351-
pt = '0m';
2406+
// TODO: Parse this.curAttr here.
2407+
// pt = '0m';
2408+
// valid = 1;
2409+
valid = 0; // Not implemented.
23522410
break;
23532411

23542412
default:
23552413
this.error('Unknown DCS Pt: %s.', pt);
2356-
pt = '';
2414+
valid = 0; // unimplemented
23572415
break;
23582416
}
23592417

2360-
this.send('\x1bP' + +valid + '$r' + pt + '\x1b\\');
2418+
this.send('\x1bP' + valid + '$r' + pt + '\x1b\\');
23612419
break;
23622420

23632421
// Set Termcap/Terminfo Data (xterm, experimental).
2422+
// DCS + p Pt ST
23642423
case '+p':
2424+
this.emit('set terminfo', {
2425+
name: this.params[0]
2426+
});
23652427
break;
23662428

23672429
// Request Termcap/Terminfo String (xterm, experimental)
23682430
// Regular xterm does not even respond to this sequence.
23692431
// This can cause a small glitch in vim.
2432+
// DCS + q Pt ST
23702433
// test: echo -ne '\eP+q6b64\e\\'
23712434
case '+q':
2372-
var pt = this.currentParam
2373-
, valid = false;
2374-
2435+
var valid = false;
23752436
this.send('\x1bP' + +valid + '+r' + pt + '\x1b\\');
23762437
break;
23772438

2439+
// Implement tmux sequence forwarding is
2440+
// someone uses term.js for a multiplexer.
2441+
// DCS tmux; ESC Pt ST
2442+
case 'tmux;\x1b':
2443+
this.emit('passthrough', pt);
2444+
break;
2445+
23782446
default:
2379-
this.error('Unknown DCS prefix: %s.', this.prefix);
2447+
this.error('Unknown DCS prefix: %s.', pt);
23802448
break;
23812449
}
23822450

2383-
this.currentParam = 0;
2451+
this.currentParam = '';
23842452
this.prefix = '';
23852453
this.state = normal;
2386-
} else if (!this.currentParam) {
2387-
if (!this.prefix && ch !== '$' && ch !== '+') {
2388-
this.currentParam = ch;
2389-
} else if (this.prefix.length === 2) {
2390-
this.currentParam = ch;
2391-
} else {
2392-
this.prefix += ch;
2393-
}
23942454
} else {
23952455
this.currentParam += ch;
2456+
if (!this.prefix) {
2457+
if (/^\d*;\d*\|/.test(this.currentParam)) {
2458+
this.prefix = UDK;
2459+
this.params = this.currentParam.split(/[;|]/).map(function(n) {
2460+
if (!n.length) return 0;
2461+
return +n;
2462+
}).slice(0, -1);
2463+
this.currentParam = '';
2464+
} else if (/^[$+][a-zA-Z]/.test(this.currentParam)
2465+
|| /^\w+;\x1b/.test(this.currentParam)) {
2466+
this.prefix = this.currentParam;
2467+
this.currentParam = '';
2468+
}
2469+
}
23962470
}
23972471
break;
23982472

23992473
case ignore:
24002474
// For PM and APC.
2401-
if (ch === '\x1b' || ch === '\x07') {
2402-
if (ch === '\x1b') i++;
2475+
if ((this.lch === '\x1b' && ch === '\\') || ch === '\x07') {
24032476
this.state = normal;
24042477
}
24052478
break;

0 commit comments

Comments
 (0)