program.js 118 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295
  1. /**
  2. * program.js - basic curses-like functionality for blessed.
  3. * Copyright (c) 2013-2015, Christopher Jeffrey and contributors (MIT License).
  4. * https://github.com/chjj/blessed
  5. */
  6. /**
  7. * Modules
  8. */
  9. var EventEmitter = require('events').EventEmitter
  10. , StringDecoder = require('string_decoder').StringDecoder
  11. , cp = require('child_process')
  12. , util = require('util')
  13. , fs = require('fs');
  14. var Tput = require('./tput')
  15. , colors = require('./colors')
  16. , slice = Array.prototype.slice;
  17. var nextTick = global.setImmediate || process.nextTick.bind(process);
  18. /**
  19. * Program
  20. */
  21. function Program(options) {
  22. var self = this;
  23. if (!(this instanceof Program)) {
  24. return new Program(options);
  25. }
  26. Program.bind(this);
  27. EventEmitter.call(this);
  28. if (!options || options.__proto__ !== Object.prototype) {
  29. options = {
  30. input: arguments[0],
  31. output: arguments[1]
  32. };
  33. }
  34. this.options = options;
  35. this.input = options.input || process.stdin;
  36. this.output = options.output || process.stdout;
  37. options.log = options.log || options.dump;
  38. if (options.log) {
  39. this._logger = fs.createWriteStream(options.log);
  40. if (options.dump) this.setupDump();
  41. }
  42. this.zero = options.zero !== false;
  43. this.useBuffer = options.buffer;
  44. this.x = 0;
  45. this.y = 0;
  46. this.savedX = 0;
  47. this.savedY = 0;
  48. this.cols = this.output.columns || 1;
  49. this.rows = this.output.rows || 1;
  50. this.scrollTop = 0;
  51. this.scrollBottom = this.rows - 1;
  52. this._terminal = options.terminal
  53. || options.term
  54. || process.env.TERM
  55. || (process.platform === 'win32' ? 'windows-ansi' : 'xterm');
  56. this._terminal = this._terminal.toLowerCase();
  57. // OSX
  58. this.isOSXTerm = process.env.TERM_PROGRAM === 'Apple_Terminal';
  59. this.isiTerm2 = process.env.TERM_PROGRAM === 'iTerm.app'
  60. || !!process.env.ITERM_SESSION_ID;
  61. // VTE
  62. // NOTE: lxterminal does not provide an env variable to check for.
  63. // NOTE: gnome-terminal and sakura use a later version of VTE
  64. // which provides VTE_VERSION as well as supports SGR events.
  65. this.isXFCE = /xfce/i.test(process.env.COLORTERM);
  66. this.isTerminator = !!process.env.TERMINATOR_UUID;
  67. this.isLXDE = false;
  68. this.isVTE = !!process.env.VTE_VERSION
  69. || this.isXFCE
  70. || this.isTerminator
  71. || this.isLXDE;
  72. // xterm and rxvt - not accurate
  73. this.isRxvt = /rxvt/i.test(process.env.COLORTERM);
  74. this.isXterm = false;
  75. this.tmux = !!process.env.TMUX;
  76. this.tmuxVersion = (function() {
  77. if (!self.tmux) return 2;
  78. try {
  79. var version = cp.execFileSync('tmux', ['-V'], { encoding: 'utf8' });
  80. return +/^tmux ([\d.]+)/i.exec(version.trim().split('\n')[0])[1];
  81. } catch (e) {
  82. return 2;
  83. }
  84. })();
  85. this._buf = '';
  86. this._flush = this.flush.bind(this);
  87. if (options.tput !== false) {
  88. this.setupTput();
  89. }
  90. this.listen();
  91. }
  92. Program.global = null;
  93. Program.total = 0;
  94. Program.instances = [];
  95. Program.bind = function(program) {
  96. if (!Program.global) {
  97. Program.global = program;
  98. }
  99. if (!~Program.instances.indexOf(program)) {
  100. Program.instances.push(program);
  101. program.index = Program.total;
  102. Program.total++;
  103. }
  104. if (Program._bound) return;
  105. Program._bound = true;
  106. unshiftEvent(process, 'exit', Program._exitHandler = function() {
  107. Program.instances.forEach(function(program) {
  108. // Potentially reset window title on exit:
  109. // if (program._originalTitle) {
  110. // program.setTitle(program._originalTitle);
  111. // }
  112. // Ensure the buffer is flushed (it should
  113. // always be at this point, but who knows).
  114. program.flush();
  115. // Ensure _exiting is set (could technically
  116. // use process._exiting).
  117. program._exiting = true;
  118. });
  119. });
  120. };
  121. Program.prototype.__proto__ = EventEmitter.prototype;
  122. Program.prototype.type = 'program';
  123. Program.prototype.log = function() {
  124. return this._log('LOG', util.format.apply(util, arguments));
  125. };
  126. Program.prototype.debug = function() {
  127. if (!this.options.debug) return;
  128. return this._log('DEBUG', util.format.apply(util, arguments));
  129. };
  130. Program.prototype._log = function(pre, msg) {
  131. if (!this._logger) return;
  132. return this._logger.write(pre + ': ' + msg + '\n-\n');
  133. };
  134. Program.prototype.setupDump = function() {
  135. var self = this
  136. , write = this.output.write
  137. , decoder = new StringDecoder('utf8');
  138. function stringify(data) {
  139. return caret(data
  140. .replace(/\r/g, '\\r')
  141. .replace(/\n/g, '\\n')
  142. .replace(/\t/g, '\\t'))
  143. .replace(/[^ -~]/g, function(ch) {
  144. if (ch.charCodeAt(0) > 0xff) return ch;
  145. ch = ch.charCodeAt(0).toString(16);
  146. if (ch.length > 2) {
  147. if (ch.length < 4) ch = '0' + ch;
  148. return '\\u' + ch;
  149. }
  150. if (ch.length < 2) ch = '0' + ch;
  151. return '\\x' + ch;
  152. });
  153. }
  154. function caret(data) {
  155. return data.replace(/[\0\x80\x1b-\x1f\x7f\x01-\x1a]/g, function(ch) {
  156. switch (ch) {
  157. case '\0':
  158. case '\200':
  159. ch = '@';
  160. break;
  161. case '\x1b':
  162. ch = '[';
  163. break;
  164. case '\x1c':
  165. ch = '\\';
  166. break;
  167. case '\x1d':
  168. ch = ']';
  169. break;
  170. case '\x1e':
  171. ch = '^';
  172. break;
  173. case '\x1f':
  174. ch = '_';
  175. break;
  176. case '\x7f':
  177. ch = '?';
  178. break;
  179. default:
  180. ch = ch.charCodeAt(0);
  181. // From ('A' - 64) to ('Z' - 64).
  182. if (ch >= 1 && ch <= 26) {
  183. ch = String.fromCharCode(ch + 64);
  184. } else {
  185. return String.fromCharCode(ch);
  186. }
  187. break;
  188. }
  189. return '^' + ch;
  190. });
  191. }
  192. this.input.on('data', function(data) {
  193. self._log('IN', stringify(decoder.write(data)));
  194. });
  195. this.output.write = function(data) {
  196. self._log('OUT', stringify(data));
  197. return write.apply(this, arguments);
  198. };
  199. };
  200. Program.prototype.setupTput = function() {
  201. if (this._tputSetup) return;
  202. this._tputSetup = true;
  203. var self = this
  204. , options = this.options
  205. , write = this._write.bind(this);
  206. var tput = this.tput = new Tput({
  207. terminal: this.terminal,
  208. padding: options.padding,
  209. extended: options.extended,
  210. printf: options.printf,
  211. termcap: options.termcap,
  212. forceUnicode: options.forceUnicode
  213. });
  214. if (tput.error) {
  215. nextTick(function() {
  216. self.emit('warning', tput.error.message);
  217. });
  218. }
  219. if (tput.padding) {
  220. nextTick(function() {
  221. self.emit('warning', 'Terminfo padding has been enabled.');
  222. });
  223. }
  224. this.put = function() {
  225. var args = slice.call(arguments)
  226. , cap = args.shift();
  227. if (tput[cap]) {
  228. return this._write(tput[cap].apply(tput, args));
  229. }
  230. };
  231. Object.keys(tput).forEach(function(key) {
  232. if (self[key] == null) {
  233. self[key] = tput[key];
  234. }
  235. if (typeof tput[key] !== 'function') {
  236. self.put[key] = tput[key];
  237. return;
  238. }
  239. if (tput.padding) {
  240. self.put[key] = function() {
  241. return tput._print(tput[key].apply(tput, arguments), write);
  242. };
  243. } else {
  244. self.put[key] = function() {
  245. return self._write(tput[key].apply(tput, arguments));
  246. };
  247. }
  248. });
  249. };
  250. Program.prototype.__defineGetter__('terminal', function() {
  251. return this._terminal;
  252. });
  253. Program.prototype.__defineSetter__('terminal', function(terminal) {
  254. this.setTerminal(terminal);
  255. return this.terminal;
  256. });
  257. Program.prototype.setTerminal = function(terminal) {
  258. this._terminal = terminal.toLowerCase();
  259. delete this._tputSetup;
  260. this.setupTput();
  261. };
  262. Program.prototype.has = function(name) {
  263. return this.tput
  264. ? this.tput.has(name)
  265. : false;
  266. };
  267. Program.prototype.term = function(is) {
  268. return this.terminal.indexOf(is) === 0;
  269. };
  270. Program.prototype.listen = function() {
  271. var self = this;
  272. // Potentially reset window title on exit:
  273. // if (!this.isRxvt) {
  274. // if (!this.isVTE) this.setTitleModeFeature(3);
  275. // this.manipulateWindow(21, function(err, data) {
  276. // if (err) return;
  277. // self._originalTitle = data.text;
  278. // });
  279. // }
  280. // Listen for keys/mouse on input
  281. if (!this.input._blessedInput) {
  282. this.input._blessedInput = 1;
  283. this._listenInput();
  284. } else {
  285. this.input._blessedInput++;
  286. }
  287. this.on('newListener', this._newHandler = function fn(type) {
  288. if (type === 'keypress' || type === 'mouse') {
  289. self.removeListener('newListener', fn);
  290. if (self.input.setRawMode && !self.input.isRaw) {
  291. self.input.setRawMode(true);
  292. self.input.resume();
  293. }
  294. }
  295. });
  296. this.on('newListener', function fn(type) {
  297. if (type === 'mouse') {
  298. self.removeListener('newListener', fn);
  299. self.bindMouse();
  300. }
  301. });
  302. // Listen for resize on output
  303. if (!this.output._blessedOutput) {
  304. this.output._blessedOutput = 1;
  305. this._listenOutput();
  306. } else {
  307. this.output._blessedOutput++;
  308. }
  309. };
  310. Program.prototype._listenInput = function() {
  311. var keys = require('./keys')
  312. , self = this;
  313. // Input
  314. this.input.on('keypress', this.input._keypressHandler = function(ch, key) {
  315. key = key || { ch: ch };
  316. if (key.name === 'undefined'
  317. && (key.code === '[M' || key.code === '[I' || key.code === '[O')) {
  318. // A mouse sequence. The `keys` module doesn't understand these.
  319. return;
  320. }
  321. if (key.name === 'undefined') {
  322. // Not sure what this is, but we should probably ignore it.
  323. return;
  324. }
  325. if (key.name === 'enter' && key.sequence === '\n') {
  326. key.name = 'linefeed';
  327. }
  328. if (key.name === 'return' && key.sequence === '\r') {
  329. self.input.emit('keypress', ch, merge({}, key, { name: 'enter' }));
  330. }
  331. var name = (key.ctrl ? 'C-' : '')
  332. + (key.meta ? 'M-' : '')
  333. + (key.shift && key.name ? 'S-' : '')
  334. + (key.name || ch);
  335. key.full = name;
  336. Program.instances.forEach(function(program) {
  337. if (program.input !== self.input) return;
  338. program.emit('keypress', ch, key);
  339. program.emit('key ' + name, ch, key);
  340. });
  341. });
  342. this.input.on('data', this.input._dataHandler = function(data) {
  343. Program.instances.forEach(function(program) {
  344. if (program.input !== self.input) return;
  345. program.emit('data', data);
  346. });
  347. });
  348. keys.emitKeypressEvents(this.input);
  349. };
  350. Program.prototype._listenOutput = function() {
  351. var self = this;
  352. if (!this.output.isTTY) {
  353. nextTick(function() {
  354. self.emit('warning', 'Output is not a TTY');
  355. });
  356. }
  357. // Output
  358. function resize() {
  359. Program.instances.forEach(function(program) {
  360. if (program.output !== self.output) return;
  361. program.cols = program.output.columns;
  362. program.rows = program.output.rows;
  363. program.emit('resize');
  364. });
  365. }
  366. this.output.on('resize', this.output._resizeHandler = function() {
  367. Program.instances.forEach(function(program) {
  368. if (program.output !== self.output) return;
  369. if (!program.options.resizeTimeout) {
  370. return resize();
  371. }
  372. if (program._resizeTimer) {
  373. clearTimeout(program._resizeTimer);
  374. delete program._resizeTimer;
  375. }
  376. var time = typeof program.options.resizeTimeout === 'number'
  377. ? program.options.resizeTimeout
  378. : 300;
  379. program._resizeTimer = setTimeout(resize, time);
  380. });
  381. });
  382. };
  383. Program.prototype.destroy = function() {
  384. var index = Program.instances.indexOf(this);
  385. if (~index) {
  386. Program.instances.splice(index, 1);
  387. Program.total--;
  388. this.flush();
  389. this._exiting = true;
  390. Program.global = Program.instances[0];
  391. if (Program.total === 0) {
  392. Program.global = null;
  393. process.removeListener('exit', Program._exitHandler);
  394. delete Program._exitHandler;
  395. delete Program._bound;
  396. }
  397. this.input._blessedInput--;
  398. this.output._blessedOutput--;
  399. if (this.input._blessedInput === 0) {
  400. this.input.removeListener('keypress', this.input._keypressHandler);
  401. this.input.removeListener('data', this.input._dataHandler);
  402. delete this.input._keypressHandler;
  403. delete this.input._dataHandler;
  404. if (this.input.setRawMode) {
  405. if (this.input.isRaw) {
  406. this.input.setRawMode(false);
  407. }
  408. if (!this.input.destroyed) {
  409. this.input.pause();
  410. }
  411. }
  412. }
  413. if (this.output._blessedOutput === 0) {
  414. this.output.removeListener('resize', this.output._resizeHandler);
  415. delete this.output._resizeHandler;
  416. }
  417. this.removeListener('newListener', this._newHandler);
  418. delete this._newHandler;
  419. this.destroyed = true;
  420. this.emit('destroy');
  421. }
  422. };
  423. Program.prototype.key = function(key, listener) {
  424. if (typeof key === 'string') key = key.split(/\s*,\s*/);
  425. key.forEach(function(key) {
  426. return this.on('key ' + key, listener);
  427. }, this);
  428. };
  429. Program.prototype.onceKey = function(key, listener) {
  430. if (typeof key === 'string') key = key.split(/\s*,\s*/);
  431. key.forEach(function(key) {
  432. return this.once('key ' + key, listener);
  433. }, this);
  434. };
  435. Program.prototype.unkey =
  436. Program.prototype.removeKey = function(key, listener) {
  437. if (typeof key === 'string') key = key.split(/\s*,\s*/);
  438. key.forEach(function(key) {
  439. return this.removeListener('key ' + key, listener);
  440. }, this);
  441. };
  442. // XTerm mouse events
  443. // http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#Mouse%20Tracking
  444. // To better understand these
  445. // the xterm code is very helpful:
  446. // Relevant files:
  447. // button.c, charproc.c, misc.c
  448. // Relevant functions in xterm/button.c:
  449. // BtnCode, EmitButtonCode, EditorButton, SendMousePosition
  450. // send a mouse event:
  451. // regular/utf8: ^[[M Cb Cx Cy
  452. // urxvt: ^[[ Cb ; Cx ; Cy M
  453. // sgr: ^[[ Cb ; Cx ; Cy M/m
  454. // vt300: ^[[ 24(1/3/5)~ [ Cx , Cy ] \r
  455. // locator: CSI P e ; P b ; P r ; P c ; P p & w
  456. // motion example of a left click:
  457. // ^[[M 3<^[[M@4<^[[M@5<^[[M@6<^[[M@7<^[[M#7<
  458. // mouseup, mousedown, mousewheel
  459. // left click: ^[[M 3<^[[M#3<
  460. // mousewheel up: ^[[M`3>
  461. Program.prototype.bindMouse = function() {
  462. if (this._boundMouse) return;
  463. this._boundMouse = true;
  464. var decoder = new StringDecoder('utf8')
  465. , self = this;
  466. this.on('data', function(data) {
  467. var text = decoder.write(data);
  468. if (!text) return;
  469. self._bindMouse(text, data);
  470. });
  471. };
  472. Program.prototype._bindMouse = function(s, buf) {
  473. var self = this
  474. , key
  475. , parts
  476. , b
  477. , x
  478. , y
  479. , mod
  480. , params
  481. , down
  482. , page
  483. , button;
  484. key = {
  485. name: undefined,
  486. ctrl: false,
  487. meta: false,
  488. shift: false
  489. };
  490. if (Buffer.isBuffer(s)) {
  491. if (s[0] > 127 && s[1] === undefined) {
  492. s[0] -= 128;
  493. s = '\x1b' + s.toString('utf-8');
  494. } else {
  495. s = s.toString('utf-8');
  496. }
  497. }
  498. // if (this.8bit) {
  499. // s = s.replace(/\233/g, '\x1b[');
  500. // buf = new Buffer(s, 'utf8');
  501. // }
  502. // XTerm / X10 for buggy VTE
  503. // VTE can only send unsigned chars and no unicode for coords. This limits
  504. // them to 0xff. However, normally the x10 protocol does not allow a byte
  505. // under 0x20, but since VTE can have the bytes overflow, we can consider
  506. // bytes below 0x20 to be up to 0xff + 0x20. This gives a limit of 287. Since
  507. // characters ranging from 223 to 248 confuse javascript's utf parser, we
  508. // need to parse the raw binary. We can detect whether the terminal is using
  509. // a bugged VTE version by examining the coordinates and seeing whether they
  510. // are a value they would never otherwise be with a properly implemented x10
  511. // protocol. This method of detecting VTE is only 99% reliable because we
  512. // can't check if the coords are 0x00 (255) since that is a valid x10 coord
  513. // technically.
  514. var bx = s.charCodeAt(4);
  515. var by = s.charCodeAt(5);
  516. if (buf[0] === 0x1b && buf[1] === 0x5b && buf[2] === 0x4d
  517. && (this.isVTE
  518. || bx >= 65533 || by >= 65533
  519. || (bx > 0x00 && bx < 0x20)
  520. || (by > 0x00 && by < 0x20)
  521. || (buf[4] > 223 && buf[4] < 248 && buf.length === 6)
  522. || (buf[5] > 223 && buf[5] < 248 && buf.length === 6))) {
  523. b = buf[3];
  524. x = buf[4];
  525. y = buf[5];
  526. // unsigned char overflow.
  527. if (x < 0x20) x += 0xff;
  528. if (y < 0x20) y += 0xff;
  529. // Convert the coordinates into a
  530. // properly formatted x10 utf8 sequence.
  531. s = '\x1b[M'
  532. + String.fromCharCode(b)
  533. + String.fromCharCode(x)
  534. + String.fromCharCode(y);
  535. }
  536. // XTerm / X10
  537. if (parts = /^\x1b\[M([\x00\u0020-\uffff]{3})/.exec(s)) {
  538. b = parts[1].charCodeAt(0);
  539. x = parts[1].charCodeAt(1);
  540. y = parts[1].charCodeAt(2);
  541. key.name = 'mouse';
  542. key.type = 'X10';
  543. key.raw = [b, x, y, parts[0]];
  544. key.buf = buf;
  545. key.x = x - 32;
  546. key.y = y - 32;
  547. if (this.zero) key.x--, key.y--;
  548. if (x === 0) key.x = 255;
  549. if (y === 0) key.y = 255;
  550. mod = b >> 2;
  551. key.shift = !!(mod & 1);
  552. key.meta = !!((mod >> 1) & 1);
  553. key.ctrl = !!((mod >> 2) & 1);
  554. b -= 32;
  555. if ((b >> 6) & 1) {
  556. key.action = b & 1 ? 'wheeldown' : 'wheelup';
  557. key.button = 'middle';
  558. } else if (b === 3) {
  559. // NOTE: x10 and urxvt have no way
  560. // of telling which button mouseup used.
  561. key.action = 'mouseup';
  562. key.button = this._lastButton || 'unknown';
  563. delete this._lastButton;
  564. } else {
  565. key.action = 'mousedown';
  566. button = b & 3;
  567. key.button =
  568. button === 0 ? 'left'
  569. : button === 1 ? 'middle'
  570. : button === 2 ? 'right'
  571. : 'unknown';
  572. this._lastButton = key.button;
  573. }
  574. // Probably a movement.
  575. // The *newer* VTE gets mouse movements comepletely wrong.
  576. // This presents a problem: older versions of VTE that get it right might
  577. // be confused by the second conditional in the if statement.
  578. // NOTE: Possibly just switch back to the if statement below.
  579. // none, shift, ctrl, alt
  580. // gnome: 32, 36, 48, 40
  581. // xterm: 35, _, 51, _
  582. // urxvt: 35, _, _, _
  583. // if (key.action === 'mousedown' && key.button === 'unknown') {
  584. if (b === 35 || b === 39 || b === 51 || b === 43
  585. || (this.isVTE && (b === 32 || b === 36 || b === 48 || b === 40))) {
  586. delete key.button;
  587. key.action = 'mousemove';
  588. }
  589. self.emit('mouse', key);
  590. return;
  591. }
  592. // URxvt
  593. if (parts = /^\x1b\[(\d+;\d+;\d+)M/.exec(s)) {
  594. params = parts[1].split(';');
  595. b = +params[0];
  596. x = +params[1];
  597. y = +params[2];
  598. key.name = 'mouse';
  599. key.type = 'urxvt';
  600. key.raw = [b, x, y, parts[0]];
  601. key.buf = buf;
  602. key.x = x;
  603. key.y = y;
  604. if (this.zero) key.x--, key.y--;
  605. mod = b >> 2;
  606. key.shift = !!(mod & 1);
  607. key.meta = !!((mod >> 1) & 1);
  608. key.ctrl = !!((mod >> 2) & 1);
  609. // XXX Bug in urxvt after wheelup/down on mousemove
  610. // NOTE: This may be different than 128/129 depending
  611. // on mod keys.
  612. if (b === 128 || b === 129) {
  613. b = 67;
  614. }
  615. b -= 32;
  616. if ((b >> 6) & 1) {
  617. key.action = b & 1 ? 'wheeldown' : 'wheelup';
  618. key.button = 'middle';
  619. } else if (b === 3) {
  620. // NOTE: x10 and urxvt have no way
  621. // of telling which button mouseup used.
  622. key.action = 'mouseup';
  623. key.button = this._lastButton || 'unknown';
  624. delete this._lastButton;
  625. } else {
  626. key.action = 'mousedown';
  627. button = b & 3;
  628. key.button =
  629. button === 0 ? 'left'
  630. : button === 1 ? 'middle'
  631. : button === 2 ? 'right'
  632. : 'unknown';
  633. // NOTE: 0/32 = mousemove, 32/64 = mousemove with left down
  634. // if ((b >> 1) === 32)
  635. this._lastButton = key.button;
  636. }
  637. // Probably a movement.
  638. // The *newer* VTE gets mouse movements comepletely wrong.
  639. // This presents a problem: older versions of VTE that get it right might
  640. // be confused by the second conditional in the if statement.
  641. // NOTE: Possibly just switch back to the if statement below.
  642. // none, shift, ctrl, alt
  643. // urxvt: 35, _, _, _
  644. // gnome: 32, 36, 48, 40
  645. // if (key.action === 'mousedown' && key.button === 'unknown') {
  646. if (b === 35 || b === 39 || b === 51 || b === 43
  647. || (this.isVTE && (b === 32 || b === 36 || b === 48 || b === 40))) {
  648. delete key.button;
  649. key.action = 'mousemove';
  650. }
  651. self.emit('mouse', key);
  652. return;
  653. }
  654. // SGR
  655. if (parts = /^\x1b\[<(\d+;\d+;\d+)([mM])/.exec(s)) {
  656. down = parts[2] === 'M';
  657. params = parts[1].split(';');
  658. b = +params[0];
  659. x = +params[1];
  660. y = +params[2];
  661. key.name = 'mouse';
  662. key.type = 'sgr';
  663. key.raw = [b, x, y, parts[0]];
  664. key.buf = buf;
  665. key.x = x;
  666. key.y = y;
  667. if (this.zero) key.x--, key.y--;
  668. mod = b >> 2;
  669. key.shift = !!(mod & 1);
  670. key.meta = !!((mod >> 1) & 1);
  671. key.ctrl = !!((mod >> 2) & 1);
  672. if ((b >> 6) & 1) {
  673. key.action = b & 1 ? 'wheeldown' : 'wheelup';
  674. key.button = 'middle';
  675. } else {
  676. key.action = down
  677. ? 'mousedown'
  678. : 'mouseup';
  679. button = b & 3;
  680. key.button =
  681. button === 0 ? 'left'
  682. : button === 1 ? 'middle'
  683. : button === 2 ? 'right'
  684. : 'unknown';
  685. }
  686. // Probably a movement.
  687. // The *newer* VTE gets mouse movements comepletely wrong.
  688. // This presents a problem: older versions of VTE that get it right might
  689. // be confused by the second conditional in the if statement.
  690. // NOTE: Possibly just switch back to the if statement below.
  691. // none, shift, ctrl, alt
  692. // xterm: 35, _, 51, _
  693. // gnome: 32, 36, 48, 40
  694. // if (key.action === 'mousedown' && key.button === 'unknown') {
  695. if (b === 35 || b === 39 || b === 51 || b === 43
  696. || (this.isVTE && (b === 32 || b === 36 || b === 48 || b === 40))) {
  697. delete key.button;
  698. key.action = 'mousemove';
  699. }
  700. self.emit('mouse', key);
  701. return;
  702. }
  703. // DEC
  704. // The xterm mouse documentation says there is a
  705. // `<` prefix, the DECRQLP says there is no prefix.
  706. if (parts = /^\x1b\[<(\d+;\d+;\d+;\d+)&w/.exec(s)) {
  707. params = parts[1].split(';');
  708. b = +params[0];
  709. x = +params[1];
  710. y = +params[2];
  711. page = +params[3];
  712. key.name = 'mouse';
  713. key.type = 'dec';
  714. key.raw = [b, x, y, parts[0]];
  715. key.buf = buf;
  716. key.x = x;
  717. key.y = y;
  718. key.page = page;
  719. if (this.zero) key.x--, key.y--;
  720. key.action = b === 3
  721. ? 'mouseup'
  722. : 'mousedown';
  723. key.button =
  724. b === 2 ? 'left'
  725. : b === 4 ? 'middle'
  726. : b === 6 ? 'right'
  727. : 'unknown';
  728. self.emit('mouse', key);
  729. return;
  730. }
  731. // vt300
  732. if (parts = /^\x1b\[24([0135])~\[(\d+),(\d+)\]\r/.exec(s)) {
  733. b = +parts[1];
  734. x = +parts[2];
  735. y = +parts[3];
  736. key.name = 'mouse';
  737. key.type = 'vt300';
  738. key.raw = [b, x, y, parts[0]];
  739. key.buf = buf;
  740. key.x = x;
  741. key.y = y;
  742. if (this.zero) key.x--, key.y--;
  743. key.action = 'mousedown';
  744. key.button =
  745. b === 1 ? 'left'
  746. : b === 2 ? 'middle'
  747. : b === 5 ? 'right'
  748. : 'unknown';
  749. self.emit('mouse', key);
  750. return;
  751. }
  752. if (parts = /^\x1b\[(O|I)/.exec(s)) {
  753. key.action = parts[1] === 'I'
  754. ? 'focus'
  755. : 'blur';
  756. self.emit('mouse', key);
  757. self.emit(key.action);
  758. return;
  759. }
  760. };
  761. // gpm support for linux vc
  762. Program.prototype.enableGpm = function() {
  763. var self = this;
  764. var gpmclient = require('./gpmclient');
  765. if (this.gpm) return;
  766. this.gpm = gpmclient();
  767. this.gpm.on('btndown', function(btn, modifier, x, y) {
  768. x--, y--;
  769. var key = {
  770. name: 'mouse',
  771. type: 'GPM',
  772. action: 'mousedown',
  773. button: self.gpm.ButtonName(btn),
  774. raw: [btn, modifier, x, y],
  775. x: x,
  776. y: y,
  777. shift: self.gpm.hasShiftKey(modifier),
  778. meta: self.gpm.hasMetaKey(modifier),
  779. ctrl: self.gpm.hasCtrlKey(modifier)
  780. };
  781. self.emit('mouse', key);
  782. });
  783. this.gpm.on('btnup', function(btn, modifier, x, y) {
  784. x--, y--;
  785. var key = {
  786. name: 'mouse',
  787. type: 'GPM',
  788. action: 'mouseup',
  789. button: self.gpm.ButtonName(btn),
  790. raw: [btn, modifier, x, y],
  791. x: x,
  792. y: y,
  793. shift: self.gpm.hasShiftKey(modifier),
  794. meta: self.gpm.hasMetaKey(modifier),
  795. ctrl: self.gpm.hasCtrlKey(modifier)
  796. };
  797. self.emit('mouse', key);
  798. });
  799. this.gpm.on('move', function(btn, modifier, x, y) {
  800. x--, y--;
  801. var key = {
  802. name: 'mouse',
  803. type: 'GPM',
  804. action: 'mousemove',
  805. button: self.gpm.ButtonName(btn),
  806. raw: [btn, modifier, x, y],
  807. x: x,
  808. y: y,
  809. shift: self.gpm.hasShiftKey(modifier),
  810. meta: self.gpm.hasMetaKey(modifier),
  811. ctrl: self.gpm.hasCtrlKey(modifier)
  812. };
  813. self.emit('mouse', key);
  814. });
  815. this.gpm.on('drag', function(btn, modifier, x, y) {
  816. x--, y--;
  817. var key = {
  818. name: 'mouse',
  819. type: 'GPM',
  820. action: 'mousemove',
  821. button: self.gpm.ButtonName(btn),
  822. raw: [btn, modifier, x, y],
  823. x: x,
  824. y: y,
  825. shift: self.gpm.hasShiftKey(modifier),
  826. meta: self.gpm.hasMetaKey(modifier),
  827. ctrl: self.gpm.hasCtrlKey(modifier)
  828. };
  829. self.emit('mouse', key);
  830. });
  831. this.gpm.on('mousewheel', function(btn, modifier, x, y, dx, dy) {
  832. var key = {
  833. name: 'mouse',
  834. type: 'GPM',
  835. action: dy > 0 ? 'wheelup' : 'wheeldown',
  836. button: self.gpm.ButtonName(btn),
  837. raw: [btn, modifier, x, y, dx, dy],
  838. x: x,
  839. y: y,
  840. shift: self.gpm.hasShiftKey(modifier),
  841. meta: self.gpm.hasMetaKey(modifier),
  842. ctrl: self.gpm.hasCtrlKey(modifier)
  843. };
  844. self.emit('mouse', key);
  845. });
  846. };
  847. Program.prototype.disableGpm = function() {
  848. if (this.gpm) {
  849. this.gpm.stop();
  850. delete this.gpm;
  851. }
  852. };
  853. // All possible responses from the terminal
  854. Program.prototype.bindResponse = function() {
  855. if (this._boundResponse) return;
  856. this._boundResponse = true;
  857. var decoder = new StringDecoder('utf8')
  858. , self = this;
  859. this.on('data', function(data) {
  860. data = decoder.write(data);
  861. if (!data) return;
  862. self._bindResponse(data);
  863. });
  864. };
  865. Program.prototype._bindResponse = function(s) {
  866. var out = {}
  867. , parts;
  868. if (Buffer.isBuffer(s)) {
  869. if (s[0] > 127 && s[1] === undefined) {
  870. s[0] -= 128;
  871. s = '\x1b' + s.toString('utf-8');
  872. } else {
  873. s = s.toString('utf-8');
  874. }
  875. }
  876. // CSI P s c
  877. // Send Device Attributes (Primary DA).
  878. // CSI > P s c
  879. // Send Device Attributes (Secondary DA).
  880. if (parts = /^\x1b\[(\?|>)(\d*(?:;\d*)*)c/.exec(s)) {
  881. parts = parts[2].split(';').map(function(ch) {
  882. return +ch || 0;
  883. });
  884. out.event = 'device-attributes';
  885. out.code = 'DA';
  886. if (parts[1] === '?') {
  887. out.type = 'primary-attribute';
  888. // VT100-style params:
  889. if (parts[0] === 1 && parts[2] === 2) {
  890. out.term = 'vt100';
  891. out.advancedVideo = true;
  892. } else if (parts[0] === 1 && parts[2] === 0) {
  893. out.term = 'vt101';
  894. } else if (parts[0] === 6) {
  895. out.term = 'vt102';
  896. } else if (parts[0] === 60
  897. && parts[1] === 1 && parts[2] === 2
  898. && parts[3] === 6 && parts[4] === 8
  899. && parts[5] === 9 && parts[6] === 15) {
  900. out.term = 'vt220';
  901. } else {
  902. // VT200-style params:
  903. parts.forEach(function(attr) {
  904. switch (attr) {
  905. case 1:
  906. out.cols132 = true;
  907. break;
  908. case 2:
  909. out.printer = true;
  910. break;
  911. case 6:
  912. out.selectiveErase = true;
  913. break;
  914. case 8:
  915. out.userDefinedKeys = true;
  916. break;
  917. case 9:
  918. out.nationalReplacementCharsets = true;
  919. break;
  920. case 15:
  921. out.technicalCharacters = true;
  922. break;
  923. case 18:
  924. out.userWindows = true;
  925. break;
  926. case 21:
  927. out.horizontalScrolling = true;
  928. break;
  929. case 22:
  930. out.ansiColor = true;
  931. break;
  932. case 29:
  933. out.ansiTextLocator = true;
  934. break;
  935. }
  936. });
  937. }
  938. } else {
  939. out.type = 'secondary-attribute';
  940. switch (parts[0]) {
  941. case 0:
  942. out.term = 'vt100';
  943. break;
  944. case 1:
  945. out.term = 'vt220';
  946. break;
  947. case 2:
  948. out.term = 'vt240';
  949. break;
  950. case 18:
  951. out.term = 'vt330';
  952. break;
  953. case 19:
  954. out.term = 'vt340';
  955. break;
  956. case 24:
  957. out.term = 'vt320';
  958. break;
  959. case 41:
  960. out.term = 'vt420';
  961. break;
  962. case 61:
  963. out.term = 'vt510';
  964. break;
  965. case 64:
  966. out.term = 'vt520';
  967. break;
  968. case 65:
  969. out.term = 'vt525';
  970. break;
  971. }
  972. out.firmwareVersion = parts[1];
  973. out.romCartridgeRegistrationNumber = parts[2];
  974. }
  975. // LEGACY
  976. out.deviceAttributes = out;
  977. this.emit('response', out);
  978. this.emit('response ' + out.event, out);
  979. return;
  980. }
  981. // CSI Ps n Device Status Report (DSR).
  982. // Ps = 5 -> Status Report. Result (``OK'') is
  983. // CSI 0 n
  984. // CSI ? Ps n
  985. // Device Status Report (DSR, DEC-specific).
  986. // Ps = 1 5 -> Report Printer status as CSI ? 1 0 n (ready).
  987. // or CSI ? 1 1 n (not ready).
  988. // Ps = 2 5 -> Report UDK status as CSI ? 2 0 n (unlocked)
  989. // or CSI ? 2 1 n (locked).
  990. // Ps = 2 6 -> Report Keyboard status as
  991. // CSI ? 2 7 ; 1 ; 0 ; 0 n (North American).
  992. // The last two parameters apply to VT400 & up, and denote key-
  993. // board ready and LK01 respectively.
  994. // Ps = 5 3 -> Report Locator status as
  995. // CSI ? 5 3 n Locator available, if compiled-in, or
  996. // CSI ? 5 0 n No Locator, if not.
  997. if (parts = /^\x1b\[(\?)?(\d+)(?:;(\d+);(\d+);(\d+))?n/.exec(s)) {
  998. out.event = 'device-status';
  999. out.code = 'DSR';
  1000. if (!parts[1] && parts[2] === '0' && !parts[3]) {
  1001. out.type = 'device-status';
  1002. out.status = 'OK';
  1003. // LEGACY
  1004. out.deviceStatus = out.status;
  1005. this.emit('response', out);
  1006. this.emit('response ' + out.event, out);
  1007. return;
  1008. }
  1009. if (parts[1] && (parts[2] === '10' || parts[2] === '11') && !parts[3]) {
  1010. out.type = 'printer-status';
  1011. out.status = parts[2] === '10'
  1012. ? 'ready'
  1013. : 'not ready';
  1014. // LEGACY
  1015. out.printerStatus = out.status;
  1016. this.emit('response', out);
  1017. this.emit('response ' + out.event, out);
  1018. return;
  1019. }
  1020. if (parts[1] && (parts[2] === '20' || parts[2] === '21') && !parts[3]) {
  1021. out.type = 'udk-status';
  1022. out.status = parts[2] === '20'
  1023. ? 'unlocked'
  1024. : 'locked';
  1025. // LEGACY
  1026. out.UDKStatus = out.status;
  1027. this.emit('response', out);
  1028. this.emit('response ' + out.event, out);
  1029. return;
  1030. }
  1031. if (parts[1]
  1032. && parts[2] === '27'
  1033. && parts[3] === '1'
  1034. && parts[4] === '0'
  1035. && parts[5] === '0') {
  1036. out.type = 'keyboard-status';
  1037. out.status = 'OK';
  1038. // LEGACY
  1039. out.keyboardStatus = out.status;
  1040. this.emit('response', out);
  1041. this.emit('response ' + out.event, out);
  1042. return;
  1043. }
  1044. if (parts[1] && (parts[2] === '53' || parts[2] === '50') && !parts[3]) {
  1045. out.type = 'locator-status';
  1046. out.status = parts[2] === '53'
  1047. ? 'available'
  1048. : 'unavailable';
  1049. // LEGACY
  1050. out.locator = out.status;
  1051. this.emit('response', out);
  1052. this.emit('response ' + out.event, out);
  1053. return;
  1054. }
  1055. out.type = 'error';
  1056. out.text = 'Unhandled: ' + JSON.stringify(parts);
  1057. // LEGACY
  1058. out.error = out.text;
  1059. this.emit('response', out);
  1060. this.emit('response ' + out.event, out);
  1061. return;
  1062. }
  1063. // CSI Ps n Device Status Report (DSR).
  1064. // Ps = 6 -> Report Cursor Position (CPR) [row;column].
  1065. // Result is
  1066. // CSI r ; c R
  1067. // CSI ? Ps n
  1068. // Device Status Report (DSR, DEC-specific).
  1069. // Ps = 6 -> Report Cursor Position (CPR) [row;column] as CSI
  1070. // ? r ; c R (assumes page is zero).
  1071. if (parts = /^\x1b\[(\?)?(\d+);(\d+)R/.exec(s)) {
  1072. out.event = 'device-status';
  1073. out.code = 'DSR';
  1074. out.type = 'cursor-status';
  1075. out.status = {
  1076. x: +parts[3],
  1077. y: +parts[2],
  1078. page: !parts[1] ? undefined : 0
  1079. };
  1080. out.x = out.status.x;
  1081. out.y = out.status.y;
  1082. out.page = out.status.page;
  1083. // LEGACY
  1084. out.cursor = out.status;
  1085. this.emit('response', out);
  1086. this.emit('response ' + out.event, out);
  1087. return;
  1088. }
  1089. // CSI Ps ; Ps ; Ps t
  1090. // Window manipulation (from dtterm, as well as extensions).
  1091. // These controls may be disabled using the allowWindowOps
  1092. // resource. Valid values for the first (and any additional
  1093. // parameters) are:
  1094. // Ps = 1 1 -> Report xterm window state. If the xterm window
  1095. // is open (non-iconified), it returns CSI 1 t . If the xterm
  1096. // window is iconified, it returns CSI 2 t .
  1097. // Ps = 1 3 -> Report xterm window position. Result is CSI 3
  1098. // ; x ; y t
  1099. // Ps = 1 4 -> Report xterm window in pixels. Result is CSI
  1100. // 4 ; height ; width t
  1101. // Ps = 1 8 -> Report the size of the text area in characters.
  1102. // Result is CSI 8 ; height ; width t
  1103. // Ps = 1 9 -> Report the size of the screen in characters.
  1104. // Result is CSI 9 ; height ; width t
  1105. if (parts = /^\x1b\[(\d+)(?:;(\d+);(\d+))?t/.exec(s)) {
  1106. out.event = 'window-manipulation';
  1107. out.code = '';
  1108. if ((parts[1] === '1' || parts[1] === '2') && !parts[2]) {
  1109. out.type = 'window-state';
  1110. out.state = parts[1] === '1'
  1111. ? 'non-iconified'
  1112. : 'iconified';
  1113. // LEGACY
  1114. out.windowState = out.state;
  1115. this.emit('response', out);
  1116. this.emit('response ' + out.event, out);
  1117. return;
  1118. }
  1119. if (parts[1] === '3' && parts[2]) {
  1120. out.type = 'window-position';
  1121. out.position = {
  1122. x: +parts[2],
  1123. y: +parts[3]
  1124. };
  1125. out.x = out.position.x;
  1126. out.y = out.position.y;
  1127. // LEGACY
  1128. out.windowPosition = out.position;
  1129. this.emit('response', out);
  1130. this.emit('response ' + out.event, out);
  1131. return;
  1132. }
  1133. if (parts[1] === '4' && parts[2]) {
  1134. out.type = 'window-size-pixels';
  1135. out.size = {
  1136. height: +parts[2],
  1137. width: +parts[3]
  1138. };
  1139. out.height = out.size.height;
  1140. out.width = out.size.width;
  1141. // LEGACY
  1142. out.windowSizePixels = out.size;
  1143. this.emit('response', out);
  1144. this.emit('response ' + out.event, out);
  1145. return;
  1146. }
  1147. if (parts[1] === '8' && parts[2]) {
  1148. out.type = 'textarea-size';
  1149. out.size = {
  1150. height: +parts[2],
  1151. width: +parts[3]
  1152. };
  1153. out.height = out.size.height;
  1154. out.width = out.size.width;
  1155. // LEGACY
  1156. out.textAreaSizeCharacters = out.size;
  1157. this.emit('response', out);
  1158. this.emit('response ' + out.event, out);
  1159. return;
  1160. }
  1161. if (parts[1] === '9' && parts[2]) {
  1162. out.type = 'screen-size';
  1163. out.size = {
  1164. height: +parts[2],
  1165. width: +parts[3]
  1166. };
  1167. out.height = out.size.height;
  1168. out.width = out.size.width;
  1169. // LEGACY
  1170. out.screenSizeCharacters = out.size;
  1171. this.emit('response', out);
  1172. this.emit('response ' + out.event, out);
  1173. return;
  1174. }
  1175. out.type = 'error';
  1176. out.text = 'Unhandled: ' + JSON.stringify(parts);
  1177. // LEGACY
  1178. out.error = out.text;
  1179. this.emit('response', out);
  1180. this.emit('response ' + out.event, out);
  1181. return;
  1182. }
  1183. // rxvt-unicode does not support window manipulation
  1184. // Result Normal: OSC l/L 0xEF 0xBF 0xBD
  1185. // Result ASCII: OSC l/L 0x1c (file separator)
  1186. // Result UTF8->ASCII: OSC l/L 0xFD
  1187. // Test with:
  1188. // echo -ne '\ePtmux;\e\e[>3t\e\\'
  1189. // sleep 2 && echo -ne '\ePtmux;\e\e[21t\e\\' & cat -v
  1190. // -
  1191. // echo -ne '\e[>3t'
  1192. // sleep 2 && echo -ne '\e[21t' & cat -v
  1193. if (parts = /^\x1b\](l|L)([^\x07\x1b]*)$/.exec(s)) {
  1194. parts[2] = 'rxvt';
  1195. s = '\x1b]' + parts[1] + parts[2] + '\x1b\\';
  1196. }
  1197. // CSI Ps ; Ps ; Ps t
  1198. // Window manipulation (from dtterm, as well as extensions).
  1199. // These controls may be disabled using the allowWindowOps
  1200. // resource. Valid values for the first (and any additional
  1201. // parameters) are:
  1202. // Ps = 2 0 -> Report xterm window's icon label. Result is
  1203. // OSC L label ST
  1204. // Ps = 2 1 -> Report xterm window's title. Result is OSC l
  1205. // label ST
  1206. if (parts = /^\x1b\](l|L)([^\x07\x1b]*)(?:\x07|\x1b\\)/.exec(s)) {
  1207. out.event = 'window-manipulation';
  1208. out.code = '';
  1209. if (parts[1] === 'L') {
  1210. out.type = 'window-icon-label';
  1211. out.text = parts[2];
  1212. // LEGACY
  1213. out.windowIconLabel = out.text;
  1214. this.emit('response', out);
  1215. this.emit('response ' + out.event, out);
  1216. return;
  1217. }
  1218. if (parts[1] === 'l') {
  1219. out.type = 'window-title';
  1220. out.text = parts[2];
  1221. // LEGACY
  1222. out.windowTitle = out.text;
  1223. this.emit('response', out);
  1224. this.emit('response ' + out.event, out);
  1225. return;
  1226. }
  1227. out.type = 'error';
  1228. out.text = 'Unhandled: ' + JSON.stringify(parts);
  1229. // LEGACY
  1230. out.error = out.text;
  1231. this.emit('response', out);
  1232. this.emit('response ' + out.event, out);
  1233. return;
  1234. }
  1235. // CSI Ps ' |
  1236. // Request Locator Position (DECRQLP).
  1237. // -> CSI Pe ; Pb ; Pr ; Pc ; Pp & w
  1238. // Parameters are [event;button;row;column;page].
  1239. // Valid values for the event:
  1240. // Pe = 0 -> locator unavailable - no other parameters sent.
  1241. // Pe = 1 -> request - xterm received a DECRQLP.
  1242. // Pe = 2 -> left button down.
  1243. // Pe = 3 -> left button up.
  1244. // Pe = 4 -> middle button down.
  1245. // Pe = 5 -> middle button up.
  1246. // Pe = 6 -> right button down.
  1247. // Pe = 7 -> right button up.
  1248. // Pe = 8 -> M4 button down.
  1249. // Pe = 9 -> M4 button up.
  1250. // Pe = 1 0 -> locator outside filter rectangle.
  1251. // ``button'' parameter is a bitmask indicating which buttons are
  1252. // pressed:
  1253. // Pb = 0 <- no buttons down.
  1254. // Pb & 1 <- right button down.
  1255. // Pb & 2 <- middle button down.
  1256. // Pb & 4 <- left button down.
  1257. // Pb & 8 <- M4 button down.
  1258. // ``row'' and ``column'' parameters are the coordinates of the
  1259. // locator position in the xterm window, encoded as ASCII deci-
  1260. // mal.
  1261. // The ``page'' parameter is not used by xterm, and will be omit-
  1262. // ted.
  1263. // NOTE:
  1264. // This is already implemented in the _bindMouse
  1265. // method, but it might make more sense here.
  1266. // The xterm mouse documentation says there is a
  1267. // `<` prefix, the DECRQLP says there is no prefix.
  1268. if (parts = /^\x1b\[(\d+(?:;\d+){4})&w/.exec(s)) {
  1269. parts = parts[1].split(';').map(function(ch) {
  1270. return +ch;
  1271. });
  1272. out.event = 'locator-position';
  1273. out.code = 'DECRQLP';
  1274. switch (parts[0]) {
  1275. case 0:
  1276. out.status = 'locator-unavailable';
  1277. break;
  1278. case 1:
  1279. out.status = 'request';
  1280. break;
  1281. case 2:
  1282. out.status = 'left-button-down';
  1283. break;
  1284. case 3:
  1285. out.status = 'left-button-up';
  1286. break;
  1287. case 4:
  1288. out.status = 'middle-button-down';
  1289. break;
  1290. case 5:
  1291. out.status = 'middle-button-up';
  1292. break;
  1293. case 6:
  1294. out.status = 'right-button-down';
  1295. break;
  1296. case 7:
  1297. out.status = 'right-button-up';
  1298. break;
  1299. case 8:
  1300. out.status = 'm4-button-down';
  1301. break;
  1302. case 9:
  1303. out.status = 'm4-button-up';
  1304. break;
  1305. case 10:
  1306. out.status = 'locator-outside';
  1307. break;
  1308. }
  1309. out.mask = parts[1];
  1310. out.row = parts[2];
  1311. out.col = parts[3];
  1312. out.page = parts[4];
  1313. // LEGACY
  1314. out.locatorPosition = out;
  1315. this.emit('response', out);
  1316. this.emit('response ' + out.event, out);
  1317. return;
  1318. }
  1319. // OSC Ps ; Pt BEL
  1320. // OSC Ps ; Pt ST
  1321. // Set Text Parameters
  1322. if (parts = /^\x1b\](\d+);([^\x07\x1b]+)(?:\x07|\x1b\\)/.exec(s)) {
  1323. out.event = 'text-params';
  1324. out.code = 'Set Text Parameters';
  1325. out.ps = +s[1];
  1326. out.pt = s[2];
  1327. this.emit('response', out);
  1328. this.emit('response ' + out.event, out);
  1329. }
  1330. };
  1331. Program.prototype.response = function(name, text, callback, noBypass) {
  1332. var self = this;
  1333. if (arguments.length === 2) {
  1334. callback = text;
  1335. text = name;
  1336. name = null;
  1337. }
  1338. if (!callback) {
  1339. callback = function() {};
  1340. }
  1341. this.bindResponse();
  1342. name = name
  1343. ? 'response ' + name
  1344. : 'response';
  1345. var onresponse;
  1346. this.once(name, onresponse = function(event) {
  1347. if (timeout) clearTimeout(timeout);
  1348. if (event.type === 'error') {
  1349. return callback(new Error(event.event + ': ' + event.text));
  1350. }
  1351. return callback(null, event);
  1352. });
  1353. var timeout = setTimeout(function() {
  1354. self.removeListener(name, onresponse);
  1355. return callback(new Error('Timeout.'));
  1356. }, 2000);
  1357. return noBypass
  1358. ? this._write(text)
  1359. : this._twrite(text);
  1360. };
  1361. Program.prototype._owrite =
  1362. Program.prototype.write = function(text) {
  1363. if (!this.output.writable) return;
  1364. return this.output.write(text);
  1365. };
  1366. Program.prototype._buffer = function(text) {
  1367. if (this._exiting) {
  1368. this.flush();
  1369. this._owrite(text);
  1370. return;
  1371. }
  1372. if (this._buf) {
  1373. this._buf += text;
  1374. return;
  1375. }
  1376. this._buf = text;
  1377. nextTick(this._flush);
  1378. return true;
  1379. };
  1380. Program.prototype.flush = function() {
  1381. if (!this._buf) return;
  1382. this._owrite(this._buf);
  1383. this._buf = '';
  1384. };
  1385. Program.prototype._write = function(text) {
  1386. if (this.ret) return text;
  1387. if (this.useBuffer) {
  1388. return this._buffer(text);
  1389. }
  1390. return this._owrite(text);
  1391. };
  1392. // Example: `DCS tmux; ESC Pt ST`
  1393. // Real: `DCS tmux; ESC Pt ESC \`
  1394. Program.prototype._twrite = function(data) {
  1395. var self = this
  1396. , iterations = 0
  1397. , timer;
  1398. if (this.tmux) {
  1399. // Replace all STs with BELs so they can be nested within the DCS code.
  1400. data = data.replace(/\x1b\\/g, '\x07');
  1401. // Wrap in tmux forward DCS:
  1402. data = '\x1bPtmux;\x1b' + data + '\x1b\\';
  1403. // If we've never even flushed yet, it means we're still in
  1404. // the normal buffer. Wait for alt screen buffer.
  1405. if (this.output.bytesWritten === 0) {
  1406. timer = setInterval(function() {
  1407. if (self.output.bytesWritten > 0 || ++iterations === 50) {
  1408. clearInterval(timer);
  1409. self.flush();
  1410. self._owrite(data);
  1411. }
  1412. }, 100);
  1413. return true;
  1414. }
  1415. // NOTE: Flushing the buffer is required in some cases.
  1416. // The DCS code must be at the start of the output.
  1417. this.flush();
  1418. // Write out raw now that the buffer is flushed.
  1419. return this._owrite(data);
  1420. }
  1421. return this._write(data);
  1422. };
  1423. Program.prototype.echo =
  1424. Program.prototype.print = function(text, attr) {
  1425. return attr
  1426. ? this._write(this.text(text, attr))
  1427. : this._write(text);
  1428. };
  1429. Program.prototype._ncoords = function() {
  1430. if (this.x < 0) this.x = 0;
  1431. else if (this.x >= this.cols) this.x = this.cols - 1;
  1432. if (this.y < 0) this.y = 0;
  1433. else if (this.y >= this.rows) this.y = this.rows - 1;
  1434. };
  1435. Program.prototype.setx = function(x) {
  1436. return this.cursorCharAbsolute(x);
  1437. // return this.charPosAbsolute(x);
  1438. };
  1439. Program.prototype.sety = function(y) {
  1440. return this.linePosAbsolute(y);
  1441. };
  1442. Program.prototype.move = function(x, y) {
  1443. return this.cursorPos(y, x);
  1444. };
  1445. // TODO: Fix cud and cuu calls.
  1446. Program.prototype.omove = function(x, y) {
  1447. if (!this.zero) {
  1448. x = (x || 1) - 1;
  1449. y = (y || 1) - 1;
  1450. } else {
  1451. x = x || 0;
  1452. y = y || 0;
  1453. }
  1454. if (y === this.y && x === this.x) {
  1455. return;
  1456. }
  1457. if (y === this.y) {
  1458. if (x > this.x) {
  1459. this.cuf(x - this.x);
  1460. } else if (x < this.x) {
  1461. this.cub(this.x - x);
  1462. }
  1463. } else if (x === this.x) {
  1464. if (y > this.y) {
  1465. this.cud(y - this.y);
  1466. } else if (y < this.y) {
  1467. this.cuu(this.y - y);
  1468. }
  1469. } else {
  1470. if (!this.zero) x++, y++;
  1471. this.cup(y, x);
  1472. }
  1473. };
  1474. Program.prototype.rsetx = function(x) {
  1475. // return this.HPositionRelative(x);
  1476. if (!x) return;
  1477. return x > 0
  1478. ? this.forward(x)
  1479. : this.back(-x);
  1480. };
  1481. Program.prototype.rsety = function(y) {
  1482. // return this.VPositionRelative(y);
  1483. if (!y) return;
  1484. return y > 0
  1485. ? this.up(y)
  1486. : this.down(-y);
  1487. };
  1488. Program.prototype.rmove = function(x, y) {
  1489. this.rsetx(x);
  1490. this.rsety(y);
  1491. };
  1492. Program.prototype.simpleInsert = function(ch, i, attr) {
  1493. return this._write(this.repeat(ch, i), attr);
  1494. };
  1495. Program.prototype.repeat = function(ch, i) {
  1496. if (!i || i < 0) i = 0;
  1497. return Array(i + 1).join(ch);
  1498. };
  1499. Program.prototype.__defineGetter__('title', function() {
  1500. return this._title;
  1501. });
  1502. Program.prototype.__defineSetter__('title', function(title) {
  1503. this.setTitle(title);
  1504. return this._title;
  1505. });
  1506. // Specific to iTerm2, but I think it's really cool.
  1507. // Example:
  1508. // if (!screen.copyToClipboard(text)) {
  1509. // execClipboardProgram(text);
  1510. // }
  1511. Program.prototype.copyToClipboard = function(text) {
  1512. if (this.isiTerm2) {
  1513. this._twrite('\x1b]50;CopyToCliboard=' + text + '\x07');
  1514. return true;
  1515. }
  1516. return false;
  1517. };
  1518. // Only XTerm and iTerm2. If you know of any others, post them.
  1519. Program.prototype.cursorShape = function(shape, blink) {
  1520. if (this.isiTerm2) {
  1521. switch (shape) {
  1522. case 'block':
  1523. if (!blink) {
  1524. this._twrite('\x1b]50;CursorShape=0;BlinkingCursorEnabled=0\x07');
  1525. } else {
  1526. this._twrite('\x1b]50;CursorShape=0;BlinkingCursorEnabled=1\x07');
  1527. }
  1528. break;
  1529. case 'underline':
  1530. if (!blink) {
  1531. // this._twrite('\x1b]50;CursorShape=n;BlinkingCursorEnabled=0\x07');
  1532. } else {
  1533. // this._twrite('\x1b]50;CursorShape=n;BlinkingCursorEnabled=1\x07');
  1534. }
  1535. break;
  1536. case 'line':
  1537. if (!blink) {
  1538. this._twrite('\x1b]50;CursorShape=1;BlinkingCursorEnabled=0\x07');
  1539. } else {
  1540. this._twrite('\x1b]50;CursorShape=1;BlinkingCursorEnabled=1\x07');
  1541. }
  1542. break;
  1543. }
  1544. return true;
  1545. } else if (this.term('xterm') || this.term('screen')) {
  1546. switch (shape) {
  1547. case 'block':
  1548. if (!blink) {
  1549. this._twrite('\x1b[0 q');
  1550. } else {
  1551. this._twrite('\x1b[1 q');
  1552. }
  1553. break;
  1554. case 'underline':
  1555. if (!blink) {
  1556. this._twrite('\x1b[2 q');
  1557. } else {
  1558. this._twrite('\x1b[3 q');
  1559. }
  1560. break;
  1561. case 'line':
  1562. if (!blink) {
  1563. this._twrite('\x1b[4 q');
  1564. } else {
  1565. this._twrite('\x1b[5 q');
  1566. }
  1567. break;
  1568. }
  1569. return true;
  1570. }
  1571. return false;
  1572. };
  1573. Program.prototype.cursorColor = function(color) {
  1574. if (this.term('xterm') || this.term('rxvt') || this.term('screen')) {
  1575. this._twrite('\x1b]12;' + color + '\x07');
  1576. return true;
  1577. }
  1578. return false;
  1579. };
  1580. Program.prototype.cursorReset =
  1581. Program.prototype.resetCursor = function() {
  1582. if (this.term('xterm') || this.term('rxvt') || this.term('screen')) {
  1583. // XXX
  1584. // return this.resetColors();
  1585. this._twrite('\x1b[0 q');
  1586. this._twrite('\x1b]112\x07');
  1587. // urxvt doesnt support OSC 112
  1588. this._twrite('\x1b]12;white\x07');
  1589. return true;
  1590. }
  1591. return false;
  1592. };
  1593. Program.prototype.getTextParams = function(param, callback) {
  1594. return this.response('text-params', '\x1b]' + param + ';?\x07', function(err, data) {
  1595. if (err) return callback(err);
  1596. return callback(null, data.pt);
  1597. });
  1598. };
  1599. Program.prototype.getCursorColor = function(callback) {
  1600. return this.getTextParams(12, callback);
  1601. };
  1602. /**
  1603. * Normal
  1604. */
  1605. //Program.prototype.pad =
  1606. Program.prototype.nul = function() {
  1607. //if (this.has('pad')) return this.put.pad();
  1608. return this._write('\200');
  1609. };
  1610. Program.prototype.bel =
  1611. Program.prototype.bell = function() {
  1612. if (this.has('bel')) return this.put.bel();
  1613. return this._write('\x07');
  1614. };
  1615. Program.prototype.vtab = function() {
  1616. this.y++;
  1617. this._ncoords();
  1618. return this._write('\x0b');
  1619. };
  1620. Program.prototype.ff =
  1621. Program.prototype.form = function() {
  1622. if (this.has('ff')) return this.put.ff();
  1623. return this._write('\x0c');
  1624. };
  1625. Program.prototype.kbs =
  1626. Program.prototype.backspace = function() {
  1627. this.x--;
  1628. this._ncoords();
  1629. if (this.has('kbs')) return this.put.kbs();
  1630. return this._write('\x08');
  1631. };
  1632. Program.prototype.ht =
  1633. Program.prototype.tab = function() {
  1634. this.x += 8;
  1635. this._ncoords();
  1636. if (this.has('ht')) return this.put.ht();
  1637. return this._write('\t');
  1638. };
  1639. Program.prototype.shiftOut = function() {
  1640. // if (this.has('S2')) return this.put.S2();
  1641. return this._write('\x0e');
  1642. };
  1643. Program.prototype.shiftIn = function() {
  1644. // if (this.has('S3')) return this.put.S3();
  1645. return this._write('\x0f');
  1646. };
  1647. Program.prototype.cr =
  1648. Program.prototype.return = function() {
  1649. this.x = 0;
  1650. if (this.has('cr')) return this.put.cr();
  1651. return this._write('\r');
  1652. };
  1653. Program.prototype.nel =
  1654. Program.prototype.newline =
  1655. Program.prototype.feed = function() {
  1656. if (this.tput && this.tput.bools.eat_newline_glitch && this.x >= this.cols) {
  1657. return;
  1658. }
  1659. this.x = 0;
  1660. this.y++;
  1661. this._ncoords();
  1662. if (this.has('nel')) return this.put.nel();
  1663. return this._write('\n');
  1664. };
  1665. /**
  1666. * Esc
  1667. */
  1668. // ESC D Index (IND is 0x84).
  1669. Program.prototype.ind =
  1670. Program.prototype.index = function() {
  1671. this.y++;
  1672. this._ncoords();
  1673. if (this.tput) return this.put.ind();
  1674. return this._write('\x1bD');
  1675. };
  1676. // ESC M Reverse Index (RI is 0x8d).
  1677. Program.prototype.ri =
  1678. Program.prototype.reverse =
  1679. Program.prototype.reverseIndex = function() {
  1680. this.y--;
  1681. this._ncoords();
  1682. if (this.tput) return this.put.ri();
  1683. return this._write('\x1bM');
  1684. };
  1685. // ESC E Next Line (NEL is 0x85).
  1686. Program.prototype.nextLine = function() {
  1687. this.y++;
  1688. this.x = 0;
  1689. this._ncoords();
  1690. if (this.has('nel')) return this.put.nel();
  1691. return this._write('\x1bE');
  1692. };
  1693. // ESC c Full Reset (RIS).
  1694. Program.prototype.reset = function() {
  1695. this.x = this.y = 0;
  1696. if (this.has('rs1') || this.has('ris')) {
  1697. return this.has('rs1')
  1698. ? this.put.rs1()
  1699. : this.put.ris();
  1700. }
  1701. return this._write('\x1bc');
  1702. };
  1703. // ESC H Tab Set (HTS is 0x88).
  1704. Program.prototype.tabSet = function() {
  1705. if (this.tput) return this.put.hts();
  1706. return this._write('\x1bH');
  1707. };
  1708. // ESC 7 Save Cursor (DECSC).
  1709. Program.prototype.sc =
  1710. Program.prototype.saveCursor = function(key) {
  1711. if (key) return this.lsaveCursor(key);
  1712. this.savedX = this.x || 0;
  1713. this.savedY = this.y || 0;
  1714. if (this.tput) return this.put.sc();
  1715. return this._write('\x1b7');
  1716. };
  1717. // ESC 8 Restore Cursor (DECRC).
  1718. Program.prototype.rc =
  1719. Program.prototype.restoreCursor = function(key, hide) {
  1720. if (key) return this.lrestoreCursor(key, hide);
  1721. this.x = this.savedX || 0;
  1722. this.y = this.savedY || 0;
  1723. if (this.tput) return this.put.rc();
  1724. return this._write('\x1b8');
  1725. };
  1726. // Save Cursor Locally
  1727. Program.prototype.lsaveCursor = function(key) {
  1728. key = key || 'local';
  1729. this._saved = this._saved || {};
  1730. this._saved[key] = this._saved[key] || {};
  1731. this._saved[key].x = this.x;
  1732. this._saved[key].y = this.y;
  1733. this._saved[key].hidden = this.cursorHidden;
  1734. };
  1735. // Restore Cursor Locally
  1736. Program.prototype.lrestoreCursor = function(key, hide) {
  1737. var pos;
  1738. key = key || 'local';
  1739. if (!this._saved || !this._saved[key]) return;
  1740. pos = this._saved[key];
  1741. //delete this._saved[key];
  1742. this.cup(pos.y, pos.x);
  1743. if (hide && pos.hidden !== this.cursorHidden) {
  1744. if (pos.hidden) {
  1745. this.hideCursor();
  1746. } else {
  1747. this.showCursor();
  1748. }
  1749. }
  1750. };
  1751. // ESC # 3 DEC line height/width
  1752. Program.prototype.lineHeight = function() {
  1753. return this._write('\x1b#');
  1754. };
  1755. // ESC (,),*,+,-,. Designate G0-G2 Character Set.
  1756. Program.prototype.charset = function(val, level) {
  1757. level = level || 0;
  1758. // See also:
  1759. // acs_chars / acsc / ac
  1760. // enter_alt_charset_mode / smacs / as
  1761. // exit_alt_charset_mode / rmacs / ae
  1762. // enter_pc_charset_mode / smpch / S2
  1763. // exit_pc_charset_mode / rmpch / S3
  1764. switch (level) {
  1765. case 0:
  1766. level = '(';
  1767. break;
  1768. case 1:
  1769. level = ')';
  1770. break;
  1771. case 2:
  1772. level = '*';
  1773. break;
  1774. case 3:
  1775. level = '+';
  1776. break;
  1777. }
  1778. var name = typeof val === 'string'
  1779. ? val.toLowerCase()
  1780. : val;
  1781. switch (name) {
  1782. case 'acs':
  1783. case 'scld': // DEC Special Character and Line Drawing Set.
  1784. if (this.tput) return this.put.smacs();
  1785. val = '0';
  1786. break;
  1787. case 'uk': // UK
  1788. val = 'A';
  1789. break;
  1790. case 'us': // United States (USASCII).
  1791. case 'usascii':
  1792. case 'ascii':
  1793. if (this.tput) return this.put.rmacs();
  1794. val = 'B';
  1795. break;
  1796. case 'dutch': // Dutch
  1797. val = '4';
  1798. break;
  1799. case 'finnish': // Finnish
  1800. val = 'C';
  1801. val = '5';
  1802. break;
  1803. case 'french': // French
  1804. val = 'R';
  1805. break;
  1806. case 'frenchcanadian': // FrenchCanadian
  1807. val = 'Q';
  1808. break;
  1809. case 'german': // German
  1810. val = 'K';
  1811. break;
  1812. case 'italian': // Italian
  1813. val = 'Y';
  1814. break;
  1815. case 'norwegiandanish': // NorwegianDanish
  1816. val = 'E';
  1817. val = '6';
  1818. break;
  1819. case 'spanish': // Spanish
  1820. val = 'Z';
  1821. break;
  1822. case 'swedish': // Swedish
  1823. val = 'H';
  1824. val = '7';
  1825. break;
  1826. case 'swiss': // Swiss
  1827. val = '=';
  1828. break;
  1829. case 'isolatin': // ISOLatin (actually /A)
  1830. val = '/A';
  1831. break;
  1832. default: // Default
  1833. if (this.tput) return this.put.rmacs();
  1834. val = 'B';
  1835. break;
  1836. }
  1837. return this._write('\x1b(' + val);
  1838. };
  1839. Program.prototype.enter_alt_charset_mode =
  1840. Program.prototype.as =
  1841. Program.prototype.smacs = function() {
  1842. return this.charset('acs');
  1843. };
  1844. Program.prototype.exit_alt_charset_mode =
  1845. Program.prototype.ae =
  1846. Program.prototype.rmacs = function() {
  1847. return this.charset('ascii');
  1848. };
  1849. // ESC N
  1850. // Single Shift Select of G2 Character Set
  1851. // ( SS2 is 0x8e). This affects next character only.
  1852. // ESC O
  1853. // Single Shift Select of G3 Character Set
  1854. // ( SS3 is 0x8f). This affects next character only.
  1855. // ESC n
  1856. // Invoke the G2 Character Set as GL (LS2).
  1857. // ESC o
  1858. // Invoke the G3 Character Set as GL (LS3).
  1859. // ESC |
  1860. // Invoke the G3 Character Set as GR (LS3R).
  1861. // ESC }
  1862. // Invoke the G2 Character Set as GR (LS2R).
  1863. // ESC ~
  1864. // Invoke the G1 Character Set as GR (LS1R).
  1865. Program.prototype.setG = function(val) {
  1866. // if (this.tput) return this.put.S2();
  1867. // if (this.tput) return this.put.S3();
  1868. switch (val) {
  1869. case 1:
  1870. val = '~'; // GR
  1871. break;
  1872. case 2:
  1873. val = 'n'; // GL
  1874. val = '}'; // GR
  1875. val = 'N'; // Next Char Only
  1876. break;
  1877. case 3:
  1878. val = 'o'; // GL
  1879. val = '|'; // GR
  1880. val = 'O'; // Next Char Only
  1881. break;
  1882. }
  1883. return this._write('\x1b' + val);
  1884. };
  1885. /**
  1886. * OSC
  1887. */
  1888. // OSC Ps ; Pt ST
  1889. // OSC Ps ; Pt BEL
  1890. // Set Text Parameters.
  1891. Program.prototype.setTitle = function(title) {
  1892. this._title = title;
  1893. // if (this.term('screen')) {
  1894. // // Tmux pane
  1895. // // if (this.tmux) {
  1896. // // return this._write('\x1b]2;' + title + '\x1b\\');
  1897. // // }
  1898. // return this._write('\x1bk' + title + '\x1b\\');
  1899. // }
  1900. return this._twrite('\x1b]0;' + title + '\x07');
  1901. };
  1902. // OSC Ps ; Pt ST
  1903. // OSC Ps ; Pt BEL
  1904. // Reset colors
  1905. Program.prototype.resetColors = function(param) {
  1906. if (this.has('Cr')) {
  1907. return this.put.Cr(param);
  1908. }
  1909. return this._twrite('\x1b]112\x07');
  1910. //return this._twrite('\x1b]112;' + param + '\x07');
  1911. };
  1912. // OSC Ps ; Pt ST
  1913. // OSC Ps ; Pt BEL
  1914. // Change dynamic colors
  1915. Program.prototype.dynamicColors = function(param) {
  1916. if (this.has('Cs')) {
  1917. return this.put.Cs(param);
  1918. }
  1919. return this._twrite('\x1b]12;' + param + '\x07');
  1920. };
  1921. // OSC Ps ; Pt ST
  1922. // OSC Ps ; Pt BEL
  1923. // Sel data
  1924. Program.prototype.selData = function(a, b) {
  1925. if (this.has('Ms')) {
  1926. return this.put.Ms(a, b);
  1927. }
  1928. return this._twrite('\x1b]52;' + a + ';' + b + '\x07');
  1929. };
  1930. /**
  1931. * CSI
  1932. */
  1933. // CSI Ps A
  1934. // Cursor Up Ps Times (default = 1) (CUU).
  1935. Program.prototype.cuu =
  1936. Program.prototype.up =
  1937. Program.prototype.cursorUp = function(param) {
  1938. this.y -= param || 1;
  1939. this._ncoords();
  1940. if (this.tput) {
  1941. if (!this.tput.strings.parm_up_cursor) {
  1942. return this._write(this.repeat(this.tput.cuu1(), param));
  1943. }
  1944. return this.put.cuu(param);
  1945. }
  1946. return this._write('\x1b[' + (param || '') + 'A');
  1947. };
  1948. // CSI Ps B
  1949. // Cursor Down Ps Times (default = 1) (CUD).
  1950. Program.prototype.cud =
  1951. Program.prototype.down =
  1952. Program.prototype.cursorDown = function(param) {
  1953. this.y += param || 1;
  1954. this._ncoords();
  1955. if (this.tput) {
  1956. if (!this.tput.strings.parm_down_cursor) {
  1957. return this._write(this.repeat(this.tput.cud1(), param));
  1958. }
  1959. return this.put.cud(param);
  1960. }
  1961. return this._write('\x1b[' + (param || '') + 'B');
  1962. };
  1963. // CSI Ps C
  1964. // Cursor Forward Ps Times (default = 1) (CUF).
  1965. Program.prototype.cuf =
  1966. Program.prototype.right =
  1967. Program.prototype.forward =
  1968. Program.prototype.cursorForward = function(param) {
  1969. this.x += param || 1;
  1970. this._ncoords();
  1971. if (this.tput) {
  1972. if (!this.tput.strings.parm_right_cursor) {
  1973. return this._write(this.repeat(this.tput.cuf1(), param));
  1974. }
  1975. return this.put.cuf(param);
  1976. }
  1977. return this._write('\x1b[' + (param || '') + 'C');
  1978. };
  1979. // CSI Ps D
  1980. // Cursor Backward Ps Times (default = 1) (CUB).
  1981. Program.prototype.cub =
  1982. Program.prototype.left =
  1983. Program.prototype.back =
  1984. Program.prototype.cursorBackward = function(param) {
  1985. this.x -= param || 1;
  1986. this._ncoords();
  1987. if (this.tput) {
  1988. if (!this.tput.strings.parm_left_cursor) {
  1989. return this._write(this.repeat(this.tput.cub1(), param));
  1990. }
  1991. return this.put.cub(param);
  1992. }
  1993. return this._write('\x1b[' + (param || '') + 'D');
  1994. };
  1995. // CSI Ps ; Ps H
  1996. // Cursor Position [row;column] (default = [1,1]) (CUP).
  1997. Program.prototype.cup =
  1998. Program.prototype.pos =
  1999. Program.prototype.cursorPos = function(row, col) {
  2000. if (!this.zero) {
  2001. row = (row || 1) - 1;
  2002. col = (col || 1) - 1;
  2003. } else {
  2004. row = row || 0;
  2005. col = col || 0;
  2006. }
  2007. this.x = col;
  2008. this.y = row;
  2009. this._ncoords();
  2010. if (this.tput) return this.put.cup(row, col);
  2011. return this._write('\x1b[' + (row + 1) + ';' + (col + 1) + 'H');
  2012. };
  2013. // CSI Ps J Erase in Display (ED).
  2014. // Ps = 0 -> Erase Below (default).
  2015. // Ps = 1 -> Erase Above.
  2016. // Ps = 2 -> Erase All.
  2017. // Ps = 3 -> Erase Saved Lines (xterm).
  2018. // CSI ? Ps J
  2019. // Erase in Display (DECSED).
  2020. // Ps = 0 -> Selective Erase Below (default).
  2021. // Ps = 1 -> Selective Erase Above.
  2022. // Ps = 2 -> Selective Erase All.
  2023. Program.prototype.ed =
  2024. Program.prototype.eraseInDisplay = function(param) {
  2025. if (this.tput) {
  2026. switch (param) {
  2027. case 'above':
  2028. param = 1;
  2029. break;
  2030. case 'all':
  2031. param = 2;
  2032. break;
  2033. case 'saved':
  2034. param = 3;
  2035. break;
  2036. case 'below':
  2037. default:
  2038. param = 0;
  2039. break;
  2040. }
  2041. // extended tput.E3 = ^[[3;J
  2042. return this.put.ed(param);
  2043. }
  2044. switch (param) {
  2045. case 'above':
  2046. return this._write('\X1b[1J');
  2047. case 'all':
  2048. return this._write('\x1b[2J');
  2049. case 'saved':
  2050. return this._write('\x1b[3J');
  2051. case 'below':
  2052. default:
  2053. return this._write('\x1b[J');
  2054. }
  2055. };
  2056. Program.prototype.clear = function() {
  2057. this.x = 0;
  2058. this.y = 0;
  2059. if (this.tput) return this.put.clear();
  2060. return this._write('\x1b[H\x1b[J');
  2061. };
  2062. // CSI Ps K Erase in Line (EL).
  2063. // Ps = 0 -> Erase to Right (default).
  2064. // Ps = 1 -> Erase to Left.
  2065. // Ps = 2 -> Erase All.
  2066. // CSI ? Ps K
  2067. // Erase in Line (DECSEL).
  2068. // Ps = 0 -> Selective Erase to Right (default).
  2069. // Ps = 1 -> Selective Erase to Left.
  2070. // Ps = 2 -> Selective Erase All.
  2071. Program.prototype.el =
  2072. Program.prototype.eraseInLine = function(param) {
  2073. if (this.tput) {
  2074. //if (this.tput.back_color_erase) ...
  2075. switch (param) {
  2076. case 'left':
  2077. param = 1;
  2078. break;
  2079. case 'all':
  2080. param = 2;
  2081. break;
  2082. case 'right':
  2083. default:
  2084. param = 0;
  2085. break;
  2086. }
  2087. return this.put.el(param);
  2088. }
  2089. switch (param) {
  2090. case 'left':
  2091. return this._write('\x1b[1K');
  2092. case 'all':
  2093. return this._write('\x1b[2K');
  2094. case 'right':
  2095. default:
  2096. return this._write('\x1b[K');
  2097. }
  2098. };
  2099. // CSI Pm m Character Attributes (SGR).
  2100. // Ps = 0 -> Normal (default).
  2101. // Ps = 1 -> Bold.
  2102. // Ps = 4 -> Underlined.
  2103. // Ps = 5 -> Blink (appears as Bold).
  2104. // Ps = 7 -> Inverse.
  2105. // Ps = 8 -> Invisible, i.e., hidden (VT300).
  2106. // Ps = 2 2 -> Normal (neither bold nor faint).
  2107. // Ps = 2 4 -> Not underlined.
  2108. // Ps = 2 5 -> Steady (not blinking).
  2109. // Ps = 2 7 -> Positive (not inverse).
  2110. // Ps = 2 8 -> Visible, i.e., not hidden (VT300).
  2111. // Ps = 3 0 -> Set foreground color to Black.
  2112. // Ps = 3 1 -> Set foreground color to Red.
  2113. // Ps = 3 2 -> Set foreground color to Green.
  2114. // Ps = 3 3 -> Set foreground color to Yellow.
  2115. // Ps = 3 4 -> Set foreground color to Blue.
  2116. // Ps = 3 5 -> Set foreground color to Magenta.
  2117. // Ps = 3 6 -> Set foreground color to Cyan.
  2118. // Ps = 3 7 -> Set foreground color to White.
  2119. // Ps = 3 9 -> Set foreground color to default (original).
  2120. // Ps = 4 0 -> Set background color to Black.
  2121. // Ps = 4 1 -> Set background color to Red.
  2122. // Ps = 4 2 -> Set background color to Green.
  2123. // Ps = 4 3 -> Set background color to Yellow.
  2124. // Ps = 4 4 -> Set background color to Blue.
  2125. // Ps = 4 5 -> Set background color to Magenta.
  2126. // Ps = 4 6 -> Set background color to Cyan.
  2127. // Ps = 4 7 -> Set background color to White.
  2128. // Ps = 4 9 -> Set background color to default (original).
  2129. // If 16-color support is compiled, the following apply. Assume
  2130. // that xterm's resources are set so that the ISO color codes are
  2131. // the first 8 of a set of 16. Then the aixterm colors are the
  2132. // bright versions of the ISO colors:
  2133. // Ps = 9 0 -> Set foreground color to Black.
  2134. // Ps = 9 1 -> Set foreground color to Red.
  2135. // Ps = 9 2 -> Set foreground color to Green.
  2136. // Ps = 9 3 -> Set foreground color to Yellow.
  2137. // Ps = 9 4 -> Set foreground color to Blue.
  2138. // Ps = 9 5 -> Set foreground color to Magenta.
  2139. // Ps = 9 6 -> Set foreground color to Cyan.
  2140. // Ps = 9 7 -> Set foreground color to White.
  2141. // Ps = 1 0 0 -> Set background color to Black.
  2142. // Ps = 1 0 1 -> Set background color to Red.
  2143. // Ps = 1 0 2 -> Set background color to Green.
  2144. // Ps = 1 0 3 -> Set background color to Yellow.
  2145. // Ps = 1 0 4 -> Set background color to Blue.
  2146. // Ps = 1 0 5 -> Set background color to Magenta.
  2147. // Ps = 1 0 6 -> Set background color to Cyan.
  2148. // Ps = 1 0 7 -> Set background color to White.
  2149. // If xterm is compiled with the 16-color support disabled, it
  2150. // supports the following, from rxvt:
  2151. // Ps = 1 0 0 -> Set foreground and background color to
  2152. // default.
  2153. // If 88- or 256-color support is compiled, the following apply.
  2154. // Ps = 3 8 ; 5 ; Ps -> Set foreground color to the second
  2155. // Ps.
  2156. // Ps = 4 8 ; 5 ; Ps -> Set background color to the second
  2157. // Ps.
  2158. Program.prototype.sgr =
  2159. Program.prototype.attr =
  2160. Program.prototype.charAttributes = function(param, val) {
  2161. return this._write(this._attr(param, val));
  2162. };
  2163. Program.prototype.text = function(text, attr) {
  2164. return this._attr(attr, true) + text + this._attr(attr, false);
  2165. };
  2166. // NOTE: sun-color may not allow multiple params for SGR.
  2167. Program.prototype._attr = function(param, val) {
  2168. var self = this
  2169. , parts
  2170. , color
  2171. , m;
  2172. if (Array.isArray(param)) {
  2173. parts = param;
  2174. param = parts[0] || 'normal';
  2175. } else {
  2176. param = param || 'normal';
  2177. parts = param.split(/\s*[,;]\s*/);
  2178. }
  2179. if (parts.length > 1) {
  2180. var used = {}
  2181. , out = [];
  2182. parts.forEach(function(part) {
  2183. part = self._attr(part, val).slice(2, -1);
  2184. if (part === '') return;
  2185. if (used[part]) return;
  2186. used[part] = true;
  2187. out.push(part);
  2188. });
  2189. return '\x1b[' + out.join(';') + 'm';
  2190. }
  2191. if (param.indexOf('no ') === 0) {
  2192. param = param.substring(3);
  2193. val = false;
  2194. } else if (param.indexOf('!') === 0) {
  2195. param = param.substring(1);
  2196. val = false;
  2197. }
  2198. switch (param) {
  2199. // attributes
  2200. case 'normal':
  2201. case 'default':
  2202. if (val === false) return '';
  2203. return '\x1b[m';
  2204. case 'bold':
  2205. return val === false
  2206. ? '\x1b[22m'
  2207. : '\x1b[1m';
  2208. case 'ul':
  2209. case 'underline':
  2210. case 'underlined':
  2211. return val === false
  2212. ? '\x1b[24m'
  2213. : '\x1b[4m';
  2214. case 'blink':
  2215. return val === false
  2216. ? '\x1b[25m'
  2217. : '\x1b[5m';
  2218. case 'inverse':
  2219. return val === false
  2220. ? '\x1b[27m'
  2221. : '\x1b[7m';
  2222. case 'invisible':
  2223. return val === false
  2224. ? '\x1b[28m'
  2225. : '\x1b[8m';
  2226. // 8-color foreground
  2227. case 'black fg':
  2228. return val === false
  2229. ? '\x1b[39m'
  2230. : '\x1b[30m';
  2231. case 'red fg':
  2232. return val === false
  2233. ? '\x1b[39m'
  2234. : '\x1b[31m';
  2235. case 'green fg':
  2236. return val === false
  2237. ? '\x1b[39m'
  2238. : '\x1b[32m';
  2239. case 'yellow fg':
  2240. return val === false
  2241. ? '\x1b[39m'
  2242. : '\x1b[33m';
  2243. case 'blue fg':
  2244. return val === false
  2245. ? '\x1b[39m'
  2246. : '\x1b[34m';
  2247. case 'magenta fg':
  2248. return val === false
  2249. ? '\x1b[39m'
  2250. : '\x1b[35m';
  2251. case 'cyan fg':
  2252. return val === false
  2253. ? '\x1b[39m'
  2254. : '\x1b[36m';
  2255. case 'white fg':
  2256. case 'light grey fg':
  2257. case 'light gray fg':
  2258. case 'bright grey fg':
  2259. case 'bright gray fg':
  2260. return val === false
  2261. ? '\x1b[39m'
  2262. : '\x1b[37m';
  2263. case 'default fg':
  2264. if (val === false) return '';
  2265. return '\x1b[39m';
  2266. // 8-color background
  2267. case 'black bg':
  2268. return val === false
  2269. ? '\x1b[49m'
  2270. : '\x1b[40m';
  2271. case 'red bg':
  2272. return val === false
  2273. ? '\x1b[49m'
  2274. : '\x1b[41m';
  2275. case 'green bg':
  2276. return val === false
  2277. ? '\x1b[49m'
  2278. : '\x1b[42m';
  2279. case 'yellow bg':
  2280. return val === false
  2281. ? '\x1b[49m'
  2282. : '\x1b[43m';
  2283. case 'blue bg':
  2284. return val === false
  2285. ? '\x1b[49m'
  2286. : '\x1b[44m';
  2287. case 'magenta bg':
  2288. return val === false
  2289. ? '\x1b[49m'
  2290. : '\x1b[45m';
  2291. case 'cyan bg':
  2292. return val === false
  2293. ? '\x1b[49m'
  2294. : '\x1b[46m';
  2295. case 'white bg':
  2296. case 'light grey bg':
  2297. case 'light gray bg':
  2298. case 'bright grey bg':
  2299. case 'bright gray bg':
  2300. return val === false
  2301. ? '\x1b[49m'
  2302. : '\x1b[47m';
  2303. case 'default bg':
  2304. if (val === false) return '';
  2305. return '\x1b[49m';
  2306. // 16-color foreground
  2307. case 'light black fg':
  2308. case 'bright black fg':
  2309. case 'grey fg':
  2310. case 'gray fg':
  2311. return val === false
  2312. ? '\x1b[39m'
  2313. : '\x1b[90m';
  2314. case 'light red fg':
  2315. case 'bright red fg':
  2316. return val === false
  2317. ? '\x1b[39m'
  2318. : '\x1b[91m';
  2319. case 'light green fg':
  2320. case 'bright green fg':
  2321. return val === false
  2322. ? '\x1b[39m'
  2323. : '\x1b[92m';
  2324. case 'light yellow fg':
  2325. case 'bright yellow fg':
  2326. return val === false
  2327. ? '\x1b[39m'
  2328. : '\x1b[93m';
  2329. case 'light blue fg':
  2330. case 'bright blue fg':
  2331. return val === false
  2332. ? '\x1b[39m'
  2333. : '\x1b[94m';
  2334. case 'light magenta fg':
  2335. case 'bright magenta fg':
  2336. return val === false
  2337. ? '\x1b[39m'
  2338. : '\x1b[95m';
  2339. case 'light cyan fg':
  2340. case 'bright cyan fg':
  2341. return val === false
  2342. ? '\x1b[39m'
  2343. : '\x1b[96m';
  2344. case 'light white fg':
  2345. case 'bright white fg':
  2346. return val === false
  2347. ? '\x1b[39m'
  2348. : '\x1b[97m';
  2349. // 16-color background
  2350. case 'light black bg':
  2351. case 'bright black bg':
  2352. case 'grey bg':
  2353. case 'gray bg':
  2354. return val === false
  2355. ? '\x1b[49m'
  2356. : '\x1b[100m';
  2357. case 'light red bg':
  2358. case 'bright red bg':
  2359. return val === false
  2360. ? '\x1b[49m'
  2361. : '\x1b[101m';
  2362. case 'light green bg':
  2363. case 'bright green bg':
  2364. return val === false
  2365. ? '\x1b[49m'
  2366. : '\x1b[102m';
  2367. case 'light yellow bg':
  2368. case 'bright yellow bg':
  2369. return val === false
  2370. ? '\x1b[49m'
  2371. : '\x1b[103m';
  2372. case 'light blue bg':
  2373. case 'bright blue bg':
  2374. return val === false
  2375. ? '\x1b[49m'
  2376. : '\x1b[104m';
  2377. case 'light magenta bg':
  2378. case 'bright magenta bg':
  2379. return val === false
  2380. ? '\x1b[49m'
  2381. : '\x1b[105m';
  2382. case 'light cyan bg':
  2383. case 'bright cyan bg':
  2384. return val === false
  2385. ? '\x1b[49m'
  2386. : '\x1b[106m';
  2387. case 'light white bg':
  2388. case 'bright white bg':
  2389. return val === false
  2390. ? '\x1b[49m'
  2391. : '\x1b[107m';
  2392. // non-16-color rxvt default fg and bg
  2393. case 'default fg bg':
  2394. if (val === false) return '';
  2395. return this.term('rxvt')
  2396. ? '\x1b[100m'
  2397. : '\x1b[39;49m';
  2398. default:
  2399. // 256-color fg and bg
  2400. if (param[0] === '#') {
  2401. param = param.replace(/#(?:[0-9a-f]{3}){1,2}/i, colors.match);
  2402. }
  2403. m = /^(-?\d+) (fg|bg)$/.exec(param);
  2404. if (m) {
  2405. color = +m[1];
  2406. if (val === false || color === -1) {
  2407. return this._attr('default ' + m[2]);
  2408. }
  2409. color = colors.reduce(color, this.tput.colors);
  2410. if (color < 16 || (this.tput && this.tput.colors <= 16)) {
  2411. if (m[2] === 'fg') {
  2412. if (color < 8) {
  2413. color += 30;
  2414. } else if (color < 16) {
  2415. color -= 8;
  2416. color += 90;
  2417. }
  2418. } else if (m[2] === 'bg') {
  2419. if (color < 8) {
  2420. color += 40;
  2421. } else if (color < 16) {
  2422. color -= 8;
  2423. color += 100;
  2424. }
  2425. }
  2426. return '\x1b[' + color + 'm';
  2427. }
  2428. if (m[2] === 'fg') {
  2429. return '\x1b[38;5;' + color + 'm';
  2430. }
  2431. if (m[2] === 'bg') {
  2432. return '\x1b[48;5;' + color + 'm';
  2433. }
  2434. }
  2435. if (/^[\d;]*$/.test(param)) {
  2436. return '\x1b[' + param + 'm';
  2437. }
  2438. return null;
  2439. }
  2440. };
  2441. Program.prototype.fg =
  2442. Program.prototype.setForeground = function(color, val) {
  2443. color = color.split(/\s*[,;]\s*/).join(' fg, ') + ' fg';
  2444. return this.attr(color, val);
  2445. };
  2446. Program.prototype.bg =
  2447. Program.prototype.setBackground = function(color, val) {
  2448. color = color.split(/\s*[,;]\s*/).join(' bg, ') + ' bg';
  2449. return this.attr(color, val);
  2450. };
  2451. // CSI Ps n Device Status Report (DSR).
  2452. // Ps = 5 -> Status Report. Result (``OK'') is
  2453. // CSI 0 n
  2454. // Ps = 6 -> Report Cursor Position (CPR) [row;column].
  2455. // Result is
  2456. // CSI r ; c R
  2457. // CSI ? Ps n
  2458. // Device Status Report (DSR, DEC-specific).
  2459. // Ps = 6 -> Report Cursor Position (CPR) [row;column] as CSI
  2460. // ? r ; c R (assumes page is zero).
  2461. // Ps = 1 5 -> Report Printer status as CSI ? 1 0 n (ready).
  2462. // or CSI ? 1 1 n (not ready).
  2463. // Ps = 2 5 -> Report UDK status as CSI ? 2 0 n (unlocked)
  2464. // or CSI ? 2 1 n (locked).
  2465. // Ps = 2 6 -> Report Keyboard status as
  2466. // CSI ? 2 7 ; 1 ; 0 ; 0 n (North American).
  2467. // The last two parameters apply to VT400 & up, and denote key-
  2468. // board ready and LK01 respectively.
  2469. // Ps = 5 3 -> Report Locator status as
  2470. // CSI ? 5 3 n Locator available, if compiled-in, or
  2471. // CSI ? 5 0 n No Locator, if not.
  2472. Program.prototype.dsr =
  2473. Program.prototype.deviceStatus = function(param, callback, dec, noBypass) {
  2474. if (dec) {
  2475. return this.response('device-status',
  2476. '\x1b[?' + (param || '0') + 'n', callback, noBypass);
  2477. }
  2478. return this.response('device-status',
  2479. '\x1b[' + (param || '0') + 'n', callback, noBypass);
  2480. };
  2481. Program.prototype.getCursor = function(callback) {
  2482. return this.deviceStatus(6, callback, false, true);
  2483. };
  2484. Program.prototype.saveReportedCursor = function(callback) {
  2485. var self = this;
  2486. if (this.tput.strings.user7 === '\x1b[6n' || this.term('screen')) {
  2487. return this.getCursor(function(err, data) {
  2488. if (data) {
  2489. self._rx = data.status.x;
  2490. self._ry = data.status.y;
  2491. }
  2492. if (!callback) return;
  2493. return callback(err);
  2494. });
  2495. }
  2496. if (!callback) return;
  2497. return callback();
  2498. };
  2499. Program.prototype.restoreReportedCursor = function() {
  2500. if (this._rx == null) return;
  2501. return this.cup(this._ry, this._rx);
  2502. // return this.nel();
  2503. };
  2504. /**
  2505. * Additions
  2506. */
  2507. // CSI Ps @
  2508. // Insert Ps (Blank) Character(s) (default = 1) (ICH).
  2509. Program.prototype.ich =
  2510. Program.prototype.insertChars = function(param) {
  2511. this.x += param || 1;
  2512. this._ncoords();
  2513. if (this.tput) return this.put.ich(param);
  2514. return this._write('\x1b[' + (param || 1) + '@');
  2515. };
  2516. // CSI Ps E
  2517. // Cursor Next Line Ps Times (default = 1) (CNL).
  2518. // same as CSI Ps B ?
  2519. Program.prototype.cnl =
  2520. Program.prototype.cursorNextLine = function(param) {
  2521. this.y += param || 1;
  2522. this._ncoords();
  2523. return this._write('\x1b[' + (param || '') + 'E');
  2524. };
  2525. // CSI Ps F
  2526. // Cursor Preceding Line Ps Times (default = 1) (CNL).
  2527. // reuse CSI Ps A ?
  2528. Program.prototype.cpl =
  2529. Program.prototype.cursorPrecedingLine = function(param) {
  2530. this.y -= param || 1;
  2531. this._ncoords();
  2532. return this._write('\x1b[' + (param || '') + 'F');
  2533. };
  2534. // CSI Ps G
  2535. // Cursor Character Absolute [column] (default = [row,1]) (CHA).
  2536. Program.prototype.cha =
  2537. Program.prototype.cursorCharAbsolute = function(param) {
  2538. if (!this.zero) {
  2539. param = (param || 1) - 1;
  2540. } else {
  2541. param = param || 0;
  2542. }
  2543. this.x = param;
  2544. this.y = 0;
  2545. this._ncoords();
  2546. if (this.tput) return this.put.hpa(param);
  2547. return this._write('\x1b[' + (param + 1) + 'G');
  2548. };
  2549. // CSI Ps L
  2550. // Insert Ps Line(s) (default = 1) (IL).
  2551. Program.prototype.il =
  2552. Program.prototype.insertLines = function(param) {
  2553. if (this.tput) return this.put.il(param);
  2554. return this._write('\x1b[' + (param || '') + 'L');
  2555. };
  2556. // CSI Ps M
  2557. // Delete Ps Line(s) (default = 1) (DL).
  2558. Program.prototype.dl =
  2559. Program.prototype.deleteLines = function(param) {
  2560. if (this.tput) return this.put.dl(param);
  2561. return this._write('\x1b[' + (param || '') + 'M');
  2562. };
  2563. // CSI Ps P
  2564. // Delete Ps Character(s) (default = 1) (DCH).
  2565. Program.prototype.dch =
  2566. Program.prototype.deleteChars = function(param) {
  2567. if (this.tput) return this.put.dch(param);
  2568. return this._write('\x1b[' + (param || '') + 'P');
  2569. };
  2570. // CSI Ps X
  2571. // Erase Ps Character(s) (default = 1) (ECH).
  2572. Program.prototype.ech =
  2573. Program.prototype.eraseChars = function(param) {
  2574. if (this.tput) return this.put.ech(param);
  2575. return this._write('\x1b[' + (param || '') + 'X');
  2576. };
  2577. // CSI Pm ` Character Position Absolute
  2578. // [column] (default = [row,1]) (HPA).
  2579. Program.prototype.hpa =
  2580. Program.prototype.charPosAbsolute = function(param) {
  2581. this.x = param || 0;
  2582. this._ncoords();
  2583. if (this.tput) {
  2584. return this.put.hpa.apply(this.put, arguments);
  2585. }
  2586. param = slice.call(arguments).join(';');
  2587. return this._write('\x1b[' + (param || '') + '`');
  2588. };
  2589. // 141 61 a * HPR -
  2590. // Horizontal Position Relative
  2591. // reuse CSI Ps C ?
  2592. Program.prototype.hpr =
  2593. Program.prototype.HPositionRelative = function(param) {
  2594. if (this.tput) return this.cuf(param);
  2595. this.x += param || 1;
  2596. this._ncoords();
  2597. // Does not exist:
  2598. // if (this.tput) return this.put.hpr(param);
  2599. return this._write('\x1b[' + (param || '') + 'a');
  2600. };
  2601. // CSI Ps c Send Device Attributes (Primary DA).
  2602. // Ps = 0 or omitted -> request attributes from terminal. The
  2603. // response depends on the decTerminalID resource setting.
  2604. // -> CSI ? 1 ; 2 c (``VT100 with Advanced Video Option'')
  2605. // -> CSI ? 1 ; 0 c (``VT101 with No Options'')
  2606. // -> CSI ? 6 c (``VT102'')
  2607. // -> CSI ? 6 0 ; 1 ; 2 ; 6 ; 8 ; 9 ; 1 5 ; c (``VT220'')
  2608. // The VT100-style response parameters do not mean anything by
  2609. // themselves. VT220 parameters do, telling the host what fea-
  2610. // tures the terminal supports:
  2611. // Ps = 1 -> 132-columns.
  2612. // Ps = 2 -> Printer.
  2613. // Ps = 6 -> Selective erase.
  2614. // Ps = 8 -> User-defined keys.
  2615. // Ps = 9 -> National replacement character sets.
  2616. // Ps = 1 5 -> Technical characters.
  2617. // Ps = 2 2 -> ANSI color, e.g., VT525.
  2618. // Ps = 2 9 -> ANSI text locator (i.e., DEC Locator mode).
  2619. // CSI > Ps c
  2620. // Send Device Attributes (Secondary DA).
  2621. // Ps = 0 or omitted -> request the terminal's identification
  2622. // code. The response depends on the decTerminalID resource set-
  2623. // ting. It should apply only to VT220 and up, but xterm extends
  2624. // this to VT100.
  2625. // -> CSI > Pp ; Pv ; Pc c
  2626. // where Pp denotes the terminal type
  2627. // Pp = 0 -> ``VT100''.
  2628. // Pp = 1 -> ``VT220''.
  2629. // and Pv is the firmware version (for xterm, this was originally
  2630. // the XFree86 patch number, starting with 95). In a DEC termi-
  2631. // nal, Pc indicates the ROM cartridge registration number and is
  2632. // always zero.
  2633. // More information:
  2634. // xterm/charproc.c - line 2012, for more information.
  2635. // vim responds with ^[[?0c or ^[[?1c after the terminal's response (?)
  2636. Program.prototype.da =
  2637. Program.prototype.sendDeviceAttributes = function(param, callback) {
  2638. return this.response('device-attributes',
  2639. '\x1b[' + (param || '') + 'c', callback);
  2640. };
  2641. // CSI Pm d
  2642. // Line Position Absolute [row] (default = [1,column]) (VPA).
  2643. // NOTE: Can't find in terminfo, no idea why it has multiple params.
  2644. Program.prototype.vpa =
  2645. Program.prototype.linePosAbsolute = function(param) {
  2646. this.y = param || 1;
  2647. this._ncoords();
  2648. if (this.tput) {
  2649. return this.put.vpa.apply(this.put, arguments);
  2650. }
  2651. param = slice.call(arguments).join(';');
  2652. return this._write('\x1b[' + (param || '') + 'd');
  2653. };
  2654. // 145 65 e * VPR - Vertical Position Relative
  2655. // reuse CSI Ps B ?
  2656. Program.prototype.vpr =
  2657. Program.prototype.VPositionRelative = function(param) {
  2658. if (this.tput) return this.cud(param);
  2659. this.y += param || 1;
  2660. this._ncoords();
  2661. // Does not exist:
  2662. // if (this.tput) return this.put.vpr(param);
  2663. return this._write('\x1b[' + (param || '') + 'e');
  2664. };
  2665. // CSI Ps ; Ps f
  2666. // Horizontal and Vertical Position [row;column] (default =
  2667. // [1,1]) (HVP).
  2668. Program.prototype.hvp =
  2669. Program.prototype.HVPosition = function(row, col) {
  2670. if (!this.zero) {
  2671. row = (row || 1) - 1;
  2672. col = (col || 1) - 1;
  2673. } else {
  2674. row = row || 0;
  2675. col = col || 0;
  2676. }
  2677. this.y = row;
  2678. this.x = col;
  2679. this._ncoords();
  2680. // Does not exist (?):
  2681. // if (this.tput) return this.put.hvp(row, col);
  2682. if (this.tput) return this.put.cup(row, col);
  2683. return this._write('\x1b[' + (row + 1) + ';' + (col + 1) + 'f');
  2684. };
  2685. // CSI Pm h Set Mode (SM).
  2686. // Ps = 2 -> Keyboard Action Mode (AM).
  2687. // Ps = 4 -> Insert Mode (IRM).
  2688. // Ps = 1 2 -> Send/receive (SRM).
  2689. // Ps = 2 0 -> Automatic Newline (LNM).
  2690. // CSI ? Pm h
  2691. // DEC Private Mode Set (DECSET).
  2692. // Ps = 1 -> Application Cursor Keys (DECCKM).
  2693. // Ps = 2 -> Designate USASCII for character sets G0-G3
  2694. // (DECANM), and set VT100 mode.
  2695. // Ps = 3 -> 132 Column Mode (DECCOLM).
  2696. // Ps = 4 -> Smooth (Slow) Scroll (DECSCLM).
  2697. // Ps = 5 -> Reverse Video (DECSCNM).
  2698. // Ps = 6 -> Origin Mode (DECOM).
  2699. // Ps = 7 -> Wraparound Mode (DECAWM).
  2700. // Ps = 8 -> Auto-repeat Keys (DECARM).
  2701. // Ps = 9 -> Send Mouse X & Y on button press. See the sec-
  2702. // tion Mouse Tracking.
  2703. // Ps = 1 0 -> Show toolbar (rxvt).
  2704. // Ps = 1 2 -> Start Blinking Cursor (att610).
  2705. // Ps = 1 8 -> Print form feed (DECPFF).
  2706. // Ps = 1 9 -> Set print extent to full screen (DECPEX).
  2707. // Ps = 2 5 -> Show Cursor (DECTCEM).
  2708. // Ps = 3 0 -> Show scrollbar (rxvt).
  2709. // Ps = 3 5 -> Enable font-shifting functions (rxvt).
  2710. // Ps = 3 8 -> Enter Tektronix Mode (DECTEK).
  2711. // Ps = 4 0 -> Allow 80 -> 132 Mode.
  2712. // Ps = 4 1 -> more(1) fix (see curses resource).
  2713. // Ps = 4 2 -> Enable Nation Replacement Character sets (DECN-
  2714. // RCM).
  2715. // Ps = 4 4 -> Turn On Margin Bell.
  2716. // Ps = 4 5 -> Reverse-wraparound Mode.
  2717. // Ps = 4 6 -> Start Logging. This is normally disabled by a
  2718. // compile-time option.
  2719. // Ps = 4 7 -> Use Alternate Screen Buffer. (This may be dis-
  2720. // abled by the titeInhibit resource).
  2721. // Ps = 6 6 -> Application keypad (DECNKM).
  2722. // Ps = 6 7 -> Backarrow key sends backspace (DECBKM).
  2723. // Ps = 1 0 0 0 -> Send Mouse X & Y on button press and
  2724. // release. See the section Mouse Tracking.
  2725. // Ps = 1 0 0 1 -> Use Hilite Mouse Tracking.
  2726. // Ps = 1 0 0 2 -> Use Cell Motion Mouse Tracking.
  2727. // Ps = 1 0 0 3 -> Use All Motion Mouse Tracking.
  2728. // Ps = 1 0 0 4 -> Send FocusIn/FocusOut events.
  2729. // Ps = 1 0 0 5 -> Enable Extended Mouse Mode.
  2730. // Ps = 1 0 1 0 -> Scroll to bottom on tty output (rxvt).
  2731. // Ps = 1 0 1 1 -> Scroll to bottom on key press (rxvt).
  2732. // Ps = 1 0 3 4 -> Interpret "meta" key, sets eighth bit.
  2733. // (enables the eightBitInput resource).
  2734. // Ps = 1 0 3 5 -> Enable special modifiers for Alt and Num-
  2735. // Lock keys. (This enables the numLock resource).
  2736. // Ps = 1 0 3 6 -> Send ESC when Meta modifies a key. (This
  2737. // enables the metaSendsEscape resource).
  2738. // Ps = 1 0 3 7 -> Send DEL from the editing-keypad Delete
  2739. // key.
  2740. // Ps = 1 0 3 9 -> Send ESC when Alt modifies a key. (This
  2741. // enables the altSendsEscape resource).
  2742. // Ps = 1 0 4 0 -> Keep selection even if not highlighted.
  2743. // (This enables the keepSelection resource).
  2744. // Ps = 1 0 4 1 -> Use the CLIPBOARD selection. (This enables
  2745. // the selectToClipboard resource).
  2746. // Ps = 1 0 4 2 -> Enable Urgency window manager hint when
  2747. // Control-G is received. (This enables the bellIsUrgent
  2748. // resource).
  2749. // Ps = 1 0 4 3 -> Enable raising of the window when Control-G
  2750. // is received. (enables the popOnBell resource).
  2751. // Ps = 1 0 4 7 -> Use Alternate Screen Buffer. (This may be
  2752. // disabled by the titeInhibit resource).
  2753. // Ps = 1 0 4 8 -> Save cursor as in DECSC. (This may be dis-
  2754. // abled by the titeInhibit resource).
  2755. // Ps = 1 0 4 9 -> Save cursor as in DECSC and use Alternate
  2756. // Screen Buffer, clearing it first. (This may be disabled by
  2757. // the titeInhibit resource). This combines the effects of the 1
  2758. // 0 4 7 and 1 0 4 8 modes. Use this with terminfo-based
  2759. // applications rather than the 4 7 mode.
  2760. // Ps = 1 0 5 0 -> Set terminfo/termcap function-key mode.
  2761. // Ps = 1 0 5 1 -> Set Sun function-key mode.
  2762. // Ps = 1 0 5 2 -> Set HP function-key mode.
  2763. // Ps = 1 0 5 3 -> Set SCO function-key mode.
  2764. // Ps = 1 0 6 0 -> Set legacy keyboard emulation (X11R6).
  2765. // Ps = 1 0 6 1 -> Set VT220 keyboard emulation.
  2766. // Ps = 2 0 0 4 -> Set bracketed paste mode.
  2767. // Modes:
  2768. // http://vt100.net/docs/vt220-rm/chapter4.html
  2769. Program.prototype.sm =
  2770. Program.prototype.setMode = function() {
  2771. var param = slice.call(arguments).join(';');
  2772. return this._write('\x1b[' + (param || '') + 'h');
  2773. };
  2774. Program.prototype.decset = function() {
  2775. var param = slice.call(arguments).join(';');
  2776. return this.setMode('?' + param);
  2777. };
  2778. Program.prototype.dectcem =
  2779. Program.prototype.cnorm =
  2780. Program.prototype.cvvis =
  2781. Program.prototype.showCursor = function() {
  2782. this.cursorHidden = false;
  2783. // NOTE: In xterm terminfo:
  2784. // cnorm stops blinking cursor
  2785. // cvvis starts blinking cursor
  2786. if (this.tput) return this.put.cnorm();
  2787. //if (this.tput) return this.put.cvvis();
  2788. // return this._write('\x1b[?12l\x1b[?25h'); // cursor_normal
  2789. // return this._write('\x1b[?12;25h'); // cursor_visible
  2790. return this.setMode('?25');
  2791. };
  2792. Program.prototype.alternate =
  2793. Program.prototype.smcup =
  2794. Program.prototype.alternateBuffer = function() {
  2795. this.isAlt = true;
  2796. if (this.tput) return this.put.smcup();
  2797. if (this.term('vt') || this.term('linux')) return;
  2798. this.setMode('?47');
  2799. return this.setMode('?1049');
  2800. };
  2801. // CSI Pm l Reset Mode (RM).
  2802. // Ps = 2 -> Keyboard Action Mode (AM).
  2803. // Ps = 4 -> Replace Mode (IRM).
  2804. // Ps = 1 2 -> Send/receive (SRM).
  2805. // Ps = 2 0 -> Normal Linefeed (LNM).
  2806. // CSI ? Pm l
  2807. // DEC Private Mode Reset (DECRST).
  2808. // Ps = 1 -> Normal Cursor Keys (DECCKM).
  2809. // Ps = 2 -> Designate VT52 mode (DECANM).
  2810. // Ps = 3 -> 80 Column Mode (DECCOLM).
  2811. // Ps = 4 -> Jump (Fast) Scroll (DECSCLM).
  2812. // Ps = 5 -> Normal Video (DECSCNM).
  2813. // Ps = 6 -> Normal Cursor Mode (DECOM).
  2814. // Ps = 7 -> No Wraparound Mode (DECAWM).
  2815. // Ps = 8 -> No Auto-repeat Keys (DECARM).
  2816. // Ps = 9 -> Don't send Mouse X & Y on button press.
  2817. // Ps = 1 0 -> Hide toolbar (rxvt).
  2818. // Ps = 1 2 -> Stop Blinking Cursor (att610).
  2819. // Ps = 1 8 -> Don't print form feed (DECPFF).
  2820. // Ps = 1 9 -> Limit print to scrolling region (DECPEX).
  2821. // Ps = 2 5 -> Hide Cursor (DECTCEM).
  2822. // Ps = 3 0 -> Don't show scrollbar (rxvt).
  2823. // Ps = 3 5 -> Disable font-shifting functions (rxvt).
  2824. // Ps = 4 0 -> Disallow 80 -> 132 Mode.
  2825. // Ps = 4 1 -> No more(1) fix (see curses resource).
  2826. // Ps = 4 2 -> Disable Nation Replacement Character sets (DEC-
  2827. // NRCM).
  2828. // Ps = 4 4 -> Turn Off Margin Bell.
  2829. // Ps = 4 5 -> No Reverse-wraparound Mode.
  2830. // Ps = 4 6 -> Stop Logging. (This is normally disabled by a
  2831. // compile-time option).
  2832. // Ps = 4 7 -> Use Normal Screen Buffer.
  2833. // Ps = 6 6 -> Numeric keypad (DECNKM).
  2834. // Ps = 6 7 -> Backarrow key sends delete (DECBKM).
  2835. // Ps = 1 0 0 0 -> Don't send Mouse X & Y on button press and
  2836. // release. See the section Mouse Tracking.
  2837. // Ps = 1 0 0 1 -> Don't use Hilite Mouse Tracking.
  2838. // Ps = 1 0 0 2 -> Don't use Cell Motion Mouse Tracking.
  2839. // Ps = 1 0 0 3 -> Don't use All Motion Mouse Tracking.
  2840. // Ps = 1 0 0 4 -> Don't send FocusIn/FocusOut events.
  2841. // Ps = 1 0 0 5 -> Disable Extended Mouse Mode.
  2842. // Ps = 1 0 1 0 -> Don't scroll to bottom on tty output
  2843. // (rxvt).
  2844. // Ps = 1 0 1 1 -> Don't scroll to bottom on key press (rxvt).
  2845. // Ps = 1 0 3 4 -> Don't interpret "meta" key. (This disables
  2846. // the eightBitInput resource).
  2847. // Ps = 1 0 3 5 -> Disable special modifiers for Alt and Num-
  2848. // Lock keys. (This disables the numLock resource).
  2849. // Ps = 1 0 3 6 -> Don't send ESC when Meta modifies a key.
  2850. // (This disables the metaSendsEscape resource).
  2851. // Ps = 1 0 3 7 -> Send VT220 Remove from the editing-keypad
  2852. // Delete key.
  2853. // Ps = 1 0 3 9 -> Don't send ESC when Alt modifies a key.
  2854. // (This disables the altSendsEscape resource).
  2855. // Ps = 1 0 4 0 -> Do not keep selection when not highlighted.
  2856. // (This disables the keepSelection resource).
  2857. // Ps = 1 0 4 1 -> Use the PRIMARY selection. (This disables
  2858. // the selectToClipboard resource).
  2859. // Ps = 1 0 4 2 -> Disable Urgency window manager hint when
  2860. // Control-G is received. (This disables the bellIsUrgent
  2861. // resource).
  2862. // Ps = 1 0 4 3 -> Disable raising of the window when Control-
  2863. // G is received. (This disables the popOnBell resource).
  2864. // Ps = 1 0 4 7 -> Use Normal Screen Buffer, clearing screen
  2865. // first if in the Alternate Screen. (This may be disabled by
  2866. // the titeInhibit resource).
  2867. // Ps = 1 0 4 8 -> Restore cursor as in DECRC. (This may be
  2868. // disabled by the titeInhibit resource).
  2869. // Ps = 1 0 4 9 -> Use Normal Screen Buffer and restore cursor
  2870. // as in DECRC. (This may be disabled by the titeInhibit
  2871. // resource). This combines the effects of the 1 0 4 7 and 1 0
  2872. // 4 8 modes. Use this with terminfo-based applications rather
  2873. // than the 4 7 mode.
  2874. // Ps = 1 0 5 0 -> Reset terminfo/termcap function-key mode.
  2875. // Ps = 1 0 5 1 -> Reset Sun function-key mode.
  2876. // Ps = 1 0 5 2 -> Reset HP function-key mode.
  2877. // Ps = 1 0 5 3 -> Reset SCO function-key mode.
  2878. // Ps = 1 0 6 0 -> Reset legacy keyboard emulation (X11R6).
  2879. // Ps = 1 0 6 1 -> Reset keyboard emulation to Sun/PC style.
  2880. // Ps = 2 0 0 4 -> Reset bracketed paste mode.
  2881. Program.prototype.rm =
  2882. Program.prototype.resetMode = function() {
  2883. var param = slice.call(arguments).join(';');
  2884. return this._write('\x1b[' + (param || '') + 'l');
  2885. };
  2886. Program.prototype.decrst = function() {
  2887. var param = slice.call(arguments).join(';');
  2888. return this.resetMode('?' + param);
  2889. };
  2890. Program.prototype.dectcemh =
  2891. Program.prototype.cursor_invisible =
  2892. Program.prototype.vi =
  2893. Program.prototype.civis =
  2894. Program.prototype.hideCursor = function() {
  2895. this.cursorHidden = true;
  2896. if (this.tput) return this.put.civis();
  2897. return this.resetMode('?25');
  2898. };
  2899. Program.prototype.rmcup =
  2900. Program.prototype.normalBuffer = function() {
  2901. this.isAlt = false;
  2902. if (this.tput) return this.put.rmcup();
  2903. this.resetMode('?47');
  2904. return this.resetMode('?1049');
  2905. };
  2906. Program.prototype.enableMouse = function() {
  2907. if (process.env.BLESSED_FORCE_MODES) {
  2908. var modes = process.env.BLESSED_FORCE_MODES.split(',');
  2909. var options = {};
  2910. for (var n = 0; n < modes.length; ++n) {
  2911. var pair = modes[n].split('=');
  2912. var v = pair[1] !== '0';
  2913. switch (pair[0].toUpperCase()) {
  2914. case 'SGRMOUSE':
  2915. options.sgrMouse = v;
  2916. break;
  2917. case 'UTFMOUSE':
  2918. options.utfMouse = v;
  2919. break;
  2920. case 'VT200MOUSE':
  2921. options.vt200Mouse = v;
  2922. break;
  2923. case 'URXVTMOUSE':
  2924. options.urxvtMouse = v;
  2925. break;
  2926. case 'X10MOUSE':
  2927. options.x10Mouse = v;
  2928. break;
  2929. case 'DECMOUSE':
  2930. options.decMouse = v;
  2931. break;
  2932. case 'PTERMMOUSE':
  2933. options.ptermMouse = v;
  2934. break;
  2935. case 'JSBTERMMOUSE':
  2936. options.jsbtermMouse = v;
  2937. break;
  2938. case 'VT200HILITE':
  2939. options.vt200Hilite = v;
  2940. break;
  2941. case 'GPMMOUSE':
  2942. options.gpmMouse = v;
  2943. break;
  2944. case 'CELLMOTION':
  2945. options.cellMotion = v;
  2946. break;
  2947. case 'ALLMOTION':
  2948. options.allMotion = v;
  2949. break;
  2950. case 'SENDFOCUS':
  2951. options.sendFocus = v;
  2952. break;
  2953. }
  2954. }
  2955. return this.setMouse(options, true);
  2956. }
  2957. // NOTE:
  2958. // Cell Motion isn't normally need for anything below here, but we'll
  2959. // activate it for tmux (whether using it or not) in case our all-motion
  2960. // passthrough does not work. It can't hurt.
  2961. if (this.term('rxvt-unicode')) {
  2962. return this.setMouse({
  2963. urxvtMouse: true,
  2964. cellMotion: true,
  2965. allMotion: true
  2966. }, true);
  2967. }
  2968. // rxvt does not support the X10 UTF extensions
  2969. if (this.term('rxvt')) {
  2970. return this.setMouse({
  2971. vt200Mouse: true,
  2972. x10Mouse: true,
  2973. cellMotion: true,
  2974. allMotion: true
  2975. }, true);
  2976. }
  2977. // libvte is broken. Older versions do not support the
  2978. // X10 UTF extension. However, later versions do support
  2979. // SGR/URXVT.
  2980. if (this.isVTE) {
  2981. return this.setMouse({
  2982. // NOTE: Could also use urxvtMouse here.
  2983. sgrMouse: true,
  2984. cellMotion: true,
  2985. allMotion: true
  2986. }, true);
  2987. }
  2988. if (this.term('linux')) {
  2989. return this.setMouse({
  2990. vt200Mouse: true,
  2991. gpmMouse: true
  2992. }, true);
  2993. }
  2994. if (this.term('xterm')
  2995. || this.term('screen')
  2996. || (this.tput && this.tput.strings.key_mouse)) {
  2997. return this.setMouse({
  2998. vt200Mouse: true,
  2999. utfMouse: true,
  3000. cellMotion: true,
  3001. allMotion: true
  3002. }, true);
  3003. }
  3004. };
  3005. Program.prototype.disableMouse = function() {
  3006. if (!this._currentMouse) return;
  3007. var obj = {};
  3008. Object.keys(this._currentMouse).forEach(function(key) {
  3009. obj[key] = false;
  3010. });
  3011. return this.setMouse(obj, false);
  3012. };
  3013. // Set Mouse
  3014. Program.prototype.setMouse = function(opt, enable) {
  3015. if (opt.normalMouse != null) {
  3016. opt.vt200Mouse = opt.normalMouse;
  3017. opt.allMotion = opt.normalMouse;
  3018. }
  3019. if (opt.hiliteTracking != null) {
  3020. opt.vt200Hilite = opt.hiliteTracking;
  3021. }
  3022. if (enable === true) {
  3023. if (this._currentMouse) {
  3024. this.setMouse(opt);
  3025. Object.keys(opt).forEach(function(key) {
  3026. this._currentMouse[key] = opt[key];
  3027. }, this);
  3028. return;
  3029. }
  3030. this._currentMouse = opt;
  3031. this.mouseEnabled = true;
  3032. } else if (enable === false) {
  3033. delete this._currentMouse;
  3034. this.mouseEnabled = false;
  3035. }
  3036. // Ps = 9 -> Send Mouse X & Y on button press. See the sec-
  3037. // tion Mouse Tracking.
  3038. // Ps = 9 -> Don't send Mouse X & Y on button press.
  3039. // x10 mouse
  3040. if (opt.x10Mouse != null) {
  3041. if (opt.x10Mouse) this.setMode('?9');
  3042. else this.resetMode('?9');
  3043. }
  3044. // Ps = 1 0 0 0 -> Send Mouse X & Y on button press and
  3045. // release. See the section Mouse Tracking.
  3046. // Ps = 1 0 0 0 -> Don't send Mouse X & Y on button press and
  3047. // release. See the section Mouse Tracking.
  3048. // vt200 mouse
  3049. if (opt.vt200Mouse != null) {
  3050. if (opt.vt200Mouse) this.setMode('?1000');
  3051. else this.resetMode('?1000');
  3052. }
  3053. // Ps = 1 0 0 1 -> Use Hilite Mouse Tracking.
  3054. // Ps = 1 0 0 1 -> Don't use Hilite Mouse Tracking.
  3055. if (opt.vt200Hilite != null) {
  3056. if (opt.vt200Hilite) this.setMode('?1001');
  3057. else this.resetMode('?1001');
  3058. }
  3059. // Ps = 1 0 0 2 -> Use Cell Motion Mouse Tracking.
  3060. // Ps = 1 0 0 2 -> Don't use Cell Motion Mouse Tracking.
  3061. // button event mouse
  3062. if (opt.cellMotion != null) {
  3063. if (opt.cellMotion) this.setMode('?1002');
  3064. else this.resetMode('?1002');
  3065. }
  3066. // Ps = 1 0 0 3 -> Use All Motion Mouse Tracking.
  3067. // Ps = 1 0 0 3 -> Don't use All Motion Mouse Tracking.
  3068. // any event mouse
  3069. if (opt.allMotion != null) {
  3070. // NOTE: Latest versions of tmux seem to only support cellMotion (not
  3071. // allMotion). We pass all motion through to the terminal.
  3072. if (this.tmux && this.tmuxVersion >= 2) {
  3073. if (opt.allMotion) this._twrite('\x1b[?1003h');
  3074. else this._twrite('\x1b[?1003l');
  3075. } else {
  3076. if (opt.allMotion) this.setMode('?1003');
  3077. else this.resetMode('?1003');
  3078. }
  3079. }
  3080. // Ps = 1 0 0 4 -> Send FocusIn/FocusOut events.
  3081. // Ps = 1 0 0 4 -> Don't send FocusIn/FocusOut events.
  3082. if (opt.sendFocus != null) {
  3083. if (opt.sendFocus) this.setMode('?1004');
  3084. else this.resetMode('?1004');
  3085. }
  3086. // Ps = 1 0 0 5 -> Enable Extended Mouse Mode.
  3087. // Ps = 1 0 0 5 -> Disable Extended Mouse Mode.
  3088. if (opt.utfMouse != null) {
  3089. if (opt.utfMouse) this.setMode('?1005');
  3090. else this.resetMode('?1005');
  3091. }
  3092. // sgr mouse
  3093. if (opt.sgrMouse != null) {
  3094. if (opt.sgrMouse) this.setMode('?1006');
  3095. else this.resetMode('?1006');
  3096. }
  3097. // urxvt mouse
  3098. if (opt.urxvtMouse != null) {
  3099. if (opt.urxvtMouse) this.setMode('?1015');
  3100. else this.resetMode('?1015');
  3101. }
  3102. // dec mouse
  3103. if (opt.decMouse != null) {
  3104. if (opt.decMouse) this._write('\x1b[1;2\'z\x1b[1;3\'{');
  3105. else this._write('\x1b[\'z');
  3106. }
  3107. // pterm mouse
  3108. if (opt.ptermMouse != null) {
  3109. if (opt.ptermMouse) this._write('\x1b[>1h\x1b[>6h\x1b[>7h\x1b[>1h\x1b[>9l');
  3110. else this._write('\x1b[>1l\x1b[>6l\x1b[>7l\x1b[>1l\x1b[>9h');
  3111. }
  3112. // jsbterm mouse
  3113. if (opt.jsbtermMouse != null) {
  3114. // + = advanced mode
  3115. if (opt.jsbtermMouse) this._write('\x1b[0~ZwLMRK+1Q\x1b\\');
  3116. else this._write('\x1b[0~ZwQ\x1b\\');
  3117. }
  3118. // gpm mouse
  3119. if (opt.gpmMouse != null) {
  3120. if (opt.gpmMouse) this.enableGpm();
  3121. else this.disableGpm();
  3122. }
  3123. };
  3124. // CSI Ps ; Ps r
  3125. // Set Scrolling Region [top;bottom] (default = full size of win-
  3126. // dow) (DECSTBM).
  3127. // CSI ? Pm r
  3128. Program.prototype.decstbm =
  3129. Program.prototype.csr =
  3130. Program.prototype.setScrollRegion = function(top, bottom) {
  3131. if (!this.zero) {
  3132. top = (top || 1) - 1;
  3133. bottom = (bottom || this.rows) - 1;
  3134. } else {
  3135. top = top || 0;
  3136. bottom = bottom || (this.rows - 1);
  3137. }
  3138. this.scrollTop = top;
  3139. this.scrollBottom = bottom;
  3140. this.x = 0;
  3141. this.y = 0;
  3142. this._ncoords();
  3143. if (this.tput) return this.put.csr(top, bottom);
  3144. return this._write('\x1b[' + (top + 1) + ';' + (bottom + 1) + 'r');
  3145. };
  3146. // CSI s
  3147. // Save cursor (ANSI.SYS).
  3148. Program.prototype.scA =
  3149. Program.prototype.saveCursorA = function() {
  3150. this.savedX = this.x;
  3151. this.savedY = this.y;
  3152. if (this.tput) return this.put.sc();
  3153. return this._write('\x1b[s');
  3154. };
  3155. // CSI u
  3156. // Restore cursor (ANSI.SYS).
  3157. Program.prototype.rcA =
  3158. Program.prototype.restoreCursorA = function() {
  3159. this.x = this.savedX || 0;
  3160. this.y = this.savedY || 0;
  3161. if (this.tput) return this.put.rc();
  3162. return this._write('\x1b[u');
  3163. };
  3164. /**
  3165. * Lesser Used
  3166. */
  3167. // CSI Ps I
  3168. // Cursor Forward Tabulation Ps tab stops (default = 1) (CHT).
  3169. Program.prototype.cht =
  3170. Program.prototype.cursorForwardTab = function(param) {
  3171. this.x += 8;
  3172. this._ncoords();
  3173. if (this.tput) return this.put.tab(param);
  3174. return this._write('\x1b[' + (param || 1) + 'I');
  3175. };
  3176. // CSI Ps S Scroll up Ps lines (default = 1) (SU).
  3177. Program.prototype.su =
  3178. Program.prototype.scrollUp = function(param) {
  3179. this.y -= param || 1;
  3180. this._ncoords();
  3181. if (this.tput) return this.put.parm_index(param);
  3182. return this._write('\x1b[' + (param || 1) + 'S');
  3183. };
  3184. // CSI Ps T Scroll down Ps lines (default = 1) (SD).
  3185. Program.prototype.sd =
  3186. Program.prototype.scrollDown = function(param) {
  3187. this.y += param || 1;
  3188. this._ncoords();
  3189. if (this.tput) return this.put.parm_rindex(param);
  3190. return this._write('\x1b[' + (param || 1) + 'T');
  3191. };
  3192. // CSI Ps ; Ps ; Ps ; Ps ; Ps T
  3193. // Initiate highlight mouse tracking. Parameters are
  3194. // [func;startx;starty;firstrow;lastrow]. See the section Mouse
  3195. // Tracking.
  3196. Program.prototype.initMouseTracking = function() {
  3197. return this._write('\x1b[' + slice.call(arguments).join(';') + 'T');
  3198. };
  3199. // CSI > Ps; Ps T
  3200. // Reset one or more features of the title modes to the default
  3201. // value. Normally, "reset" disables the feature. It is possi-
  3202. // ble to disable the ability to reset features by compiling a
  3203. // different default for the title modes into xterm.
  3204. // Ps = 0 -> Do not set window/icon labels using hexadecimal.
  3205. // Ps = 1 -> Do not query window/icon labels using hexadeci-
  3206. // mal.
  3207. // Ps = 2 -> Do not set window/icon labels using UTF-8.
  3208. // Ps = 3 -> Do not query window/icon labels using UTF-8.
  3209. // (See discussion of "Title Modes").
  3210. Program.prototype.resetTitleModes = function() {
  3211. return this._write('\x1b[>' + slice.call(arguments).join(';') + 'T');
  3212. };
  3213. // CSI Ps Z Cursor Backward Tabulation Ps tab stops (default = 1) (CBT).
  3214. Program.prototype.cbt =
  3215. Program.prototype.cursorBackwardTab = function(param) {
  3216. this.x -= 8;
  3217. this._ncoords();
  3218. if (this.tput) return this.put.cbt(param);
  3219. return this._write('\x1b[' + (param || 1) + 'Z');
  3220. };
  3221. // CSI Ps b Repeat the preceding graphic character Ps times (REP).
  3222. Program.prototype.rep =
  3223. Program.prototype.repeatPrecedingCharacter = function(param) {
  3224. this.x += param || 1;
  3225. this._ncoords();
  3226. if (this.tput) return this.put.rep(param);
  3227. return this._write('\x1b[' + (param || 1) + 'b');
  3228. };
  3229. // CSI Ps g Tab Clear (TBC).
  3230. // Ps = 0 -> Clear Current Column (default).
  3231. // Ps = 3 -> Clear All.
  3232. // Potentially:
  3233. // Ps = 2 -> Clear Stops on Line.
  3234. // http://vt100.net/annarbor/aaa-ug/section6.html
  3235. Program.prototype.tbc =
  3236. Program.prototype.tabClear = function(param) {
  3237. if (this.tput) return this.put.tbc(param);
  3238. return this._write('\x1b[' + (param || 0) + 'g');
  3239. };
  3240. // CSI Pm i Media Copy (MC).
  3241. // Ps = 0 -> Print screen (default).
  3242. // Ps = 4 -> Turn off printer controller mode.
  3243. // Ps = 5 -> Turn on printer controller mode.
  3244. // CSI ? Pm i
  3245. // Media Copy (MC, DEC-specific).
  3246. // Ps = 1 -> Print line containing cursor.
  3247. // Ps = 4 -> Turn off autoprint mode.
  3248. // Ps = 5 -> Turn on autoprint mode.
  3249. // Ps = 1 0 -> Print composed display, ignores DECPEX.
  3250. // Ps = 1 1 -> Print all pages.
  3251. Program.prototype.mc =
  3252. Program.prototype.mediaCopy = function() {
  3253. return this._write('\x1b[' + slice.call(arguments).join(';') + 'i');
  3254. };
  3255. Program.prototype.print_screen =
  3256. Program.prototype.ps =
  3257. Program.prototype.mc0 = function() {
  3258. if (this.tput) return this.put.mc0();
  3259. return this.mc('0');
  3260. };
  3261. Program.prototype.prtr_on =
  3262. Program.prototype.po =
  3263. Program.prototype.mc5 = function() {
  3264. if (this.tput) return this.put.mc5();
  3265. return this.mc('5');
  3266. };
  3267. Program.prototype.prtr_off =
  3268. Program.prototype.pf =
  3269. Program.prototype.mc4 = function() {
  3270. if (this.tput) return this.put.mc4();
  3271. return this.mc('4');
  3272. };
  3273. Program.prototype.prtr_non =
  3274. Program.prototype.pO =
  3275. Program.prototype.mc5p = function() {
  3276. if (this.tput) return this.put.mc5p();
  3277. return this.mc('?5');
  3278. };
  3279. // CSI > Ps; Ps m
  3280. // Set or reset resource-values used by xterm to decide whether
  3281. // to construct escape sequences holding information about the
  3282. // modifiers pressed with a given key. The first parameter iden-
  3283. // tifies the resource to set/reset. The second parameter is the
  3284. // value to assign to the resource. If the second parameter is
  3285. // omitted, the resource is reset to its initial value.
  3286. // Ps = 1 -> modifyCursorKeys.
  3287. // Ps = 2 -> modifyFunctionKeys.
  3288. // Ps = 4 -> modifyOtherKeys.
  3289. // If no parameters are given, all resources are reset to their
  3290. // initial values.
  3291. Program.prototype.setResources = function() {
  3292. return this._write('\x1b[>' + slice.call(arguments).join(';') + 'm');
  3293. };
  3294. // CSI > Ps n
  3295. // Disable modifiers which may be enabled via the CSI > Ps; Ps m
  3296. // sequence. This corresponds to a resource value of "-1", which
  3297. // cannot be set with the other sequence. The parameter identi-
  3298. // fies the resource to be disabled:
  3299. // Ps = 1 -> modifyCursorKeys.
  3300. // Ps = 2 -> modifyFunctionKeys.
  3301. // Ps = 4 -> modifyOtherKeys.
  3302. // If the parameter is omitted, modifyFunctionKeys is disabled.
  3303. // When modifyFunctionKeys is disabled, xterm uses the modifier
  3304. // keys to make an extended sequence of functions rather than
  3305. // adding a parameter to each function key to denote the modi-
  3306. // fiers.
  3307. Program.prototype.disableModifiers = function(param) {
  3308. return this._write('\x1b[>' + (param || '') + 'n');
  3309. };
  3310. // CSI > Ps p
  3311. // Set resource value pointerMode. This is used by xterm to
  3312. // decide whether to hide the pointer cursor as the user types.
  3313. // Valid values for the parameter:
  3314. // Ps = 0 -> never hide the pointer.
  3315. // Ps = 1 -> hide if the mouse tracking mode is not enabled.
  3316. // Ps = 2 -> always hide the pointer. If no parameter is
  3317. // given, xterm uses the default, which is 1 .
  3318. Program.prototype.setPointerMode = function(param) {
  3319. return this._write('\x1b[>' + (param || '') + 'p');
  3320. };
  3321. // CSI ! p Soft terminal reset (DECSTR).
  3322. // http://vt100.net/docs/vt220-rm/table4-10.html
  3323. Program.prototype.decstr =
  3324. Program.prototype.rs2 =
  3325. Program.prototype.softReset = function() {
  3326. //if (this.tput) return this.put.init_2string();
  3327. //if (this.tput) return this.put.reset_2string();
  3328. if (this.tput) return this.put.rs2();
  3329. //return this._write('\x1b[!p');
  3330. //return this._write('\x1b[!p\x1b[?3;4l\x1b[4l\x1b>'); // init
  3331. return this._write('\x1b[!p\x1b[?3;4l\x1b[4l\x1b>'); // reset
  3332. };
  3333. // CSI Ps$ p
  3334. // Request ANSI mode (DECRQM). For VT300 and up, reply is
  3335. // CSI Ps; Pm$ y
  3336. // where Ps is the mode number as in RM, and Pm is the mode
  3337. // value:
  3338. // 0 - not recognized
  3339. // 1 - set
  3340. // 2 - reset
  3341. // 3 - permanently set
  3342. // 4 - permanently reset
  3343. Program.prototype.decrqm =
  3344. Program.prototype.requestAnsiMode = function(param) {
  3345. return this._write('\x1b[' + (param || '') + '$p');
  3346. };
  3347. // CSI ? Ps$ p
  3348. // Request DEC private mode (DECRQM). For VT300 and up, reply is
  3349. // CSI ? Ps; Pm$ p
  3350. // where Ps is the mode number as in DECSET, Pm is the mode value
  3351. // as in the ANSI DECRQM.
  3352. Program.prototype.decrqmp =
  3353. Program.prototype.requestPrivateMode = function(param) {
  3354. return this._write('\x1b[?' + (param || '') + '$p');
  3355. };
  3356. // CSI Ps ; Ps " p
  3357. // Set conformance level (DECSCL). Valid values for the first
  3358. // parameter:
  3359. // Ps = 6 1 -> VT100.
  3360. // Ps = 6 2 -> VT200.
  3361. // Ps = 6 3 -> VT300.
  3362. // Valid values for the second parameter:
  3363. // Ps = 0 -> 8-bit controls.
  3364. // Ps = 1 -> 7-bit controls (always set for VT100).
  3365. // Ps = 2 -> 8-bit controls.
  3366. Program.prototype.decscl =
  3367. Program.prototype.setConformanceLevel = function() {
  3368. return this._write('\x1b[' + slice.call(arguments).join(';') + '"p');
  3369. };
  3370. // CSI Ps q Load LEDs (DECLL).
  3371. // Ps = 0 -> Clear all LEDS (default).
  3372. // Ps = 1 -> Light Num Lock.
  3373. // Ps = 2 -> Light Caps Lock.
  3374. // Ps = 3 -> Light Scroll Lock.
  3375. // Ps = 2 1 -> Extinguish Num Lock.
  3376. // Ps = 2 2 -> Extinguish Caps Lock.
  3377. // Ps = 2 3 -> Extinguish Scroll Lock.
  3378. Program.prototype.decll =
  3379. Program.prototype.loadLEDs = function(param) {
  3380. return this._write('\x1b[' + (param || '') + 'q');
  3381. };
  3382. // CSI Ps SP q
  3383. // Set cursor style (DECSCUSR, VT520).
  3384. // Ps = 0 -> blinking block.
  3385. // Ps = 1 -> blinking block (default).
  3386. // Ps = 2 -> steady block.
  3387. // Ps = 3 -> blinking underline.
  3388. // Ps = 4 -> steady underline.
  3389. Program.prototype.decscusr =
  3390. Program.prototype.setCursorStyle = function(param) {
  3391. switch (param) {
  3392. case 'blinking block':
  3393. param = 1;
  3394. break;
  3395. case 'block':
  3396. case 'steady block':
  3397. param = 2;
  3398. break;
  3399. case 'blinking underline':
  3400. param = 3;
  3401. break;
  3402. case 'underline':
  3403. case 'steady underline':
  3404. param = 4;
  3405. break;
  3406. case 'blinking bar':
  3407. param = 5;
  3408. break;
  3409. case 'bar':
  3410. case 'steady bar':
  3411. param = 6;
  3412. break;
  3413. }
  3414. if (param === 2 && this.has('Se')) {
  3415. return this.put.Se();
  3416. }
  3417. if (this.has('Ss')) {
  3418. return this.put.Ss(param);
  3419. }
  3420. return this._write('\x1b[' + (param || 1) + ' q');
  3421. };
  3422. // CSI Ps " q
  3423. // Select character protection attribute (DECSCA). Valid values
  3424. // for the parameter:
  3425. // Ps = 0 -> DECSED and DECSEL can erase (default).
  3426. // Ps = 1 -> DECSED and DECSEL cannot erase.
  3427. // Ps = 2 -> DECSED and DECSEL can erase.
  3428. Program.prototype.decsca =
  3429. Program.prototype.setCharProtectionAttr = function(param) {
  3430. return this._write('\x1b[' + (param || 0) + '"q');
  3431. };
  3432. // CSI ? Pm r
  3433. // Restore DEC Private Mode Values. The value of Ps previously
  3434. // saved is restored. Ps values are the same as for DECSET.
  3435. Program.prototype.restorePrivateValues = function() {
  3436. return this._write('\x1b[?' + slice.call(arguments).join(';') + 'r');
  3437. };
  3438. // CSI Pt; Pl; Pb; Pr; Ps$ r
  3439. // Change Attributes in Rectangular Area (DECCARA), VT400 and up.
  3440. // Pt; Pl; Pb; Pr denotes the rectangle.
  3441. // Ps denotes the SGR attributes to change: 0, 1, 4, 5, 7.
  3442. // NOTE: xterm doesn't enable this code by default.
  3443. Program.prototype.deccara =
  3444. Program.prototype.setAttrInRectangle = function() {
  3445. return this._write('\x1b[' + slice.call(arguments).join(';') + '$r');
  3446. };
  3447. // CSI ? Pm s
  3448. // Save DEC Private Mode Values. Ps values are the same as for
  3449. // DECSET.
  3450. Program.prototype.savePrivateValues = function() {
  3451. return this._write('\x1b[?' + slice.call(arguments).join(';') + 's');
  3452. };
  3453. // CSI Ps ; Ps ; Ps t
  3454. // Window manipulation (from dtterm, as well as extensions).
  3455. // These controls may be disabled using the allowWindowOps
  3456. // resource. Valid values for the first (and any additional
  3457. // parameters) are:
  3458. // Ps = 1 -> De-iconify window.
  3459. // Ps = 2 -> Iconify window.
  3460. // Ps = 3 ; x ; y -> Move window to [x, y].
  3461. // Ps = 4 ; height ; width -> Resize the xterm window to
  3462. // height and width in pixels.
  3463. // Ps = 5 -> Raise the xterm window to the front of the stack-
  3464. // ing order.
  3465. // Ps = 6 -> Lower the xterm window to the bottom of the
  3466. // stacking order.
  3467. // Ps = 7 -> Refresh the xterm window.
  3468. // Ps = 8 ; height ; width -> Resize the text area to
  3469. // [height;width] in characters.
  3470. // Ps = 9 ; 0 -> Restore maximized window.
  3471. // Ps = 9 ; 1 -> Maximize window (i.e., resize to screen
  3472. // size).
  3473. // Ps = 1 0 ; 0 -> Undo full-screen mode.
  3474. // Ps = 1 0 ; 1 -> Change to full-screen.
  3475. // Ps = 1 1 -> Report xterm window state. If the xterm window
  3476. // is open (non-iconified), it returns CSI 1 t . If the xterm
  3477. // window is iconified, it returns CSI 2 t .
  3478. // Ps = 1 3 -> Report xterm window position. Result is CSI 3
  3479. // ; x ; y t
  3480. // Ps = 1 4 -> Report xterm window in pixels. Result is CSI
  3481. // 4 ; height ; width t
  3482. // Ps = 1 8 -> Report the size of the text area in characters.
  3483. // Result is CSI 8 ; height ; width t
  3484. // Ps = 1 9 -> Report the size of the screen in characters.
  3485. // Result is CSI 9 ; height ; width t
  3486. // Ps = 2 0 -> Report xterm window's icon label. Result is
  3487. // OSC L label ST
  3488. // Ps = 2 1 -> Report xterm window's title. Result is OSC l
  3489. // label ST
  3490. // Ps = 2 2 ; 0 -> Save xterm icon and window title on
  3491. // stack.
  3492. // Ps = 2 2 ; 1 -> Save xterm icon title on stack.
  3493. // Ps = 2 2 ; 2 -> Save xterm window title on stack.
  3494. // Ps = 2 3 ; 0 -> Restore xterm icon and window title from
  3495. // stack.
  3496. // Ps = 2 3 ; 1 -> Restore xterm icon title from stack.
  3497. // Ps = 2 3 ; 2 -> Restore xterm window title from stack.
  3498. // Ps >= 2 4 -> Resize to Ps lines (DECSLPP).
  3499. Program.prototype.manipulateWindow = function() {
  3500. var args = slice.call(arguments);
  3501. var callback = typeof args[args.length - 1] === 'function'
  3502. ? args.pop()
  3503. : function() {};
  3504. return this.response('window-manipulation',
  3505. '\x1b[' + args.join(';') + 't', callback);
  3506. };
  3507. Program.prototype.getWindowSize = function(callback) {
  3508. return this.manipulateWindow(18, callback);
  3509. };
  3510. // CSI Pt; Pl; Pb; Pr; Ps$ t
  3511. // Reverse Attributes in Rectangular Area (DECRARA), VT400 and
  3512. // up.
  3513. // Pt; Pl; Pb; Pr denotes the rectangle.
  3514. // Ps denotes the attributes to reverse, i.e., 1, 4, 5, 7.
  3515. // NOTE: xterm doesn't enable this code by default.
  3516. Program.prototype.decrara =
  3517. Program.prototype.reverseAttrInRectangle = function() {
  3518. return this._write('\x1b[' + slice.call(arguments).join(';') + '$t');
  3519. };
  3520. // CSI > Ps; Ps t
  3521. // Set one or more features of the title modes. Each parameter
  3522. // enables a single feature.
  3523. // Ps = 0 -> Set window/icon labels using hexadecimal.
  3524. // Ps = 1 -> Query window/icon labels using hexadecimal.
  3525. // Ps = 2 -> Set window/icon labels using UTF-8.
  3526. // Ps = 3 -> Query window/icon labels using UTF-8. (See dis-
  3527. // cussion of "Title Modes")
  3528. // XXX VTE bizarelly echos this:
  3529. Program.prototype.setTitleModeFeature = function() {
  3530. return this._twrite('\x1b[>' + slice.call(arguments).join(';') + 't');
  3531. };
  3532. // CSI Ps SP t
  3533. // Set warning-bell volume (DECSWBV, VT520).
  3534. // Ps = 0 or 1 -> off.
  3535. // Ps = 2 , 3 or 4 -> low.
  3536. // Ps = 5 , 6 , 7 , or 8 -> high.
  3537. Program.prototype.decswbv =
  3538. Program.prototype.setWarningBellVolume = function(param) {
  3539. return this._write('\x1b[' + (param || '') + ' t');
  3540. };
  3541. // CSI Ps SP u
  3542. // Set margin-bell volume (DECSMBV, VT520).
  3543. // Ps = 1 -> off.
  3544. // Ps = 2 , 3 or 4 -> low.
  3545. // Ps = 0 , 5 , 6 , 7 , or 8 -> high.
  3546. Program.prototype.decsmbv =
  3547. Program.prototype.setMarginBellVolume = function(param) {
  3548. return this._write('\x1b[' + (param || '') + ' u');
  3549. };
  3550. // CSI Pt; Pl; Pb; Pr; Pp; Pt; Pl; Pp$ v
  3551. // Copy Rectangular Area (DECCRA, VT400 and up).
  3552. // Pt; Pl; Pb; Pr denotes the rectangle.
  3553. // Pp denotes the source page.
  3554. // Pt; Pl denotes the target location.
  3555. // Pp denotes the target page.
  3556. // NOTE: xterm doesn't enable this code by default.
  3557. Program.prototype.deccra =
  3558. Program.prototype.copyRectangle = function() {
  3559. return this._write('\x1b[' + slice.call(arguments).join(';') + '$v');
  3560. };
  3561. // CSI Pt ; Pl ; Pb ; Pr ' w
  3562. // Enable Filter Rectangle (DECEFR), VT420 and up.
  3563. // Parameters are [top;left;bottom;right].
  3564. // Defines the coordinates of a filter rectangle and activates
  3565. // it. Anytime the locator is detected outside of the filter
  3566. // rectangle, an outside rectangle event is generated and the
  3567. // rectangle is disabled. Filter rectangles are always treated
  3568. // as "one-shot" events. Any parameters that are omitted default
  3569. // to the current locator position. If all parameters are omit-
  3570. // ted, any locator motion will be reported. DECELR always can-
  3571. // cels any prevous rectangle definition.
  3572. Program.prototype.decefr =
  3573. Program.prototype.enableFilterRectangle = function() {
  3574. return this._write('\x1b[' + slice.call(arguments).join(';') + '\'w');
  3575. };
  3576. // CSI Ps x Request Terminal Parameters (DECREQTPARM).
  3577. // if Ps is a "0" (default) or "1", and xterm is emulating VT100,
  3578. // the control sequence elicits a response of the same form whose
  3579. // parameters describe the terminal:
  3580. // Ps -> the given Ps incremented by 2.
  3581. // Pn = 1 <- no parity.
  3582. // Pn = 1 <- eight bits.
  3583. // Pn = 1 <- 2 8 transmit 38.4k baud.
  3584. // Pn = 1 <- 2 8 receive 38.4k baud.
  3585. // Pn = 1 <- clock multiplier.
  3586. // Pn = 0 <- STP flags.
  3587. Program.prototype.decreqtparm =
  3588. Program.prototype.requestParameters = function(param) {
  3589. return this._write('\x1b[' + (param || 0) + 'x');
  3590. };
  3591. // CSI Ps x Select Attribute Change Extent (DECSACE).
  3592. // Ps = 0 -> from start to end position, wrapped.
  3593. // Ps = 1 -> from start to end position, wrapped.
  3594. // Ps = 2 -> rectangle (exact).
  3595. Program.prototype.decsace =
  3596. Program.prototype.selectChangeExtent = function(param) {
  3597. return this._write('\x1b[' + (param || 0) + 'x');
  3598. };
  3599. // CSI Pc; Pt; Pl; Pb; Pr$ x
  3600. // Fill Rectangular Area (DECFRA), VT420 and up.
  3601. // Pc is the character to use.
  3602. // Pt; Pl; Pb; Pr denotes the rectangle.
  3603. // NOTE: xterm doesn't enable this code by default.
  3604. Program.prototype.decfra =
  3605. Program.prototype.fillRectangle = function() {
  3606. return this._write('\x1b[' + slice.call(arguments).join(';') + '$x');
  3607. };
  3608. // CSI Ps ; Pu ' z
  3609. // Enable Locator Reporting (DECELR).
  3610. // Valid values for the first parameter:
  3611. // Ps = 0 -> Locator disabled (default).
  3612. // Ps = 1 -> Locator enabled.
  3613. // Ps = 2 -> Locator enabled for one report, then disabled.
  3614. // The second parameter specifies the coordinate unit for locator
  3615. // reports.
  3616. // Valid values for the second parameter:
  3617. // Pu = 0 <- or omitted -> default to character cells.
  3618. // Pu = 1 <- device physical pixels.
  3619. // Pu = 2 <- character cells.
  3620. Program.prototype.decelr =
  3621. Program.prototype.enableLocatorReporting = function() {
  3622. return this._write('\x1b[' + slice.call(arguments).join(';') + '\'z');
  3623. };
  3624. // CSI Pt; Pl; Pb; Pr$ z
  3625. // Erase Rectangular Area (DECERA), VT400 and up.
  3626. // Pt; Pl; Pb; Pr denotes the rectangle.
  3627. // NOTE: xterm doesn't enable this code by default.
  3628. Program.prototype.decera =
  3629. Program.prototype.eraseRectangle = function() {
  3630. return this._write('\x1b[' + slice.call(arguments).join(';') + '$z');
  3631. };
  3632. // CSI Pm ' {
  3633. // Select Locator Events (DECSLE).
  3634. // Valid values for the first (and any additional parameters)
  3635. // are:
  3636. // Ps = 0 -> only respond to explicit host requests (DECRQLP).
  3637. // (This is default). It also cancels any filter
  3638. // rectangle.
  3639. // Ps = 1 -> report button down transitions.
  3640. // Ps = 2 -> do not report button down transitions.
  3641. // Ps = 3 -> report button up transitions.
  3642. // Ps = 4 -> do not report button up transitions.
  3643. Program.prototype.decsle =
  3644. Program.prototype.setLocatorEvents = function() {
  3645. return this._write('\x1b[' + slice.call(arguments).join(';') + '\'{');
  3646. };
  3647. // CSI Pt; Pl; Pb; Pr$ {
  3648. // Selective Erase Rectangular Area (DECSERA), VT400 and up.
  3649. // Pt; Pl; Pb; Pr denotes the rectangle.
  3650. Program.prototype.decsera =
  3651. Program.prototype.selectiveEraseRectangle = function() {
  3652. return this._write('\x1b[' + slice.call(arguments).join(';') + '${');
  3653. };
  3654. // CSI Ps ' |
  3655. // Request Locator Position (DECRQLP).
  3656. // Valid values for the parameter are:
  3657. // Ps = 0 , 1 or omitted -> transmit a single DECLRP locator
  3658. // report.
  3659. // If Locator Reporting has been enabled by a DECELR, xterm will
  3660. // respond with a DECLRP Locator Report. This report is also
  3661. // generated on button up and down events if they have been
  3662. // enabled with a DECSLE, or when the locator is detected outside
  3663. // of a filter rectangle, if filter rectangles have been enabled
  3664. // with a DECEFR.
  3665. // -> CSI Pe ; Pb ; Pr ; Pc ; Pp & w
  3666. // Parameters are [event;button;row;column;page].
  3667. // Valid values for the event:
  3668. // Pe = 0 -> locator unavailable - no other parameters sent.
  3669. // Pe = 1 -> request - xterm received a DECRQLP.
  3670. // Pe = 2 -> left button down.
  3671. // Pe = 3 -> left button up.
  3672. // Pe = 4 -> middle button down.
  3673. // Pe = 5 -> middle button up.
  3674. // Pe = 6 -> right button down.
  3675. // Pe = 7 -> right button up.
  3676. // Pe = 8 -> M4 button down.
  3677. // Pe = 9 -> M4 button up.
  3678. // Pe = 1 0 -> locator outside filter rectangle.
  3679. // ``button'' parameter is a bitmask indicating which buttons are
  3680. // pressed:
  3681. // Pb = 0 <- no buttons down.
  3682. // Pb & 1 <- right button down.
  3683. // Pb & 2 <- middle button down.
  3684. // Pb & 4 <- left button down.
  3685. // Pb & 8 <- M4 button down.
  3686. // ``row'' and ``column'' parameters are the coordinates of the
  3687. // locator position in the xterm window, encoded as ASCII deci-
  3688. // mal.
  3689. // The ``page'' parameter is not used by xterm, and will be omit-
  3690. // ted.
  3691. Program.prototype.decrqlp =
  3692. Program.prototype.req_mouse_pos =
  3693. Program.prototype.reqmp =
  3694. Program.prototype.requestLocatorPosition = function(param, callback) {
  3695. // See also:
  3696. // get_mouse / getm / Gm
  3697. // mouse_info / minfo / Mi
  3698. // Correct for tput?
  3699. if (this.has('req_mouse_pos')) {
  3700. var code = this.tput.req_mouse_pos(param);
  3701. return this.response('locator-position', code, callback);
  3702. }
  3703. return this.response('locator-position',
  3704. '\x1b[' + (param || '') + '\'|', callback);
  3705. };
  3706. // CSI P m SP }
  3707. // Insert P s Column(s) (default = 1) (DECIC), VT420 and up.
  3708. // NOTE: xterm doesn't enable this code by default.
  3709. Program.prototype.decic =
  3710. Program.prototype.insertColumns = function() {
  3711. return this._write('\x1b[' + slice.call(arguments).join(';') + ' }');
  3712. };
  3713. // CSI P m SP ~
  3714. // Delete P s Column(s) (default = 1) (DECDC), VT420 and up
  3715. // NOTE: xterm doesn't enable this code by default.
  3716. Program.prototype.decdc =
  3717. Program.prototype.deleteColumns = function() {
  3718. return this._write('\x1b[' + slice.call(arguments).join(';') + ' ~');
  3719. };
  3720. Program.prototype.out = function(name) {
  3721. var args = Array.prototype.slice.call(arguments, 1);
  3722. this.ret = true;
  3723. var out = this[name].apply(this, args);
  3724. this.ret = false;
  3725. return out;
  3726. };
  3727. Program.prototype.sigtstp = function(callback) {
  3728. var resume = this.pause();
  3729. process.once('SIGCONT', function() {
  3730. resume();
  3731. if (callback) callback();
  3732. });
  3733. process.kill(process.pid, 'SIGTSTP');
  3734. };
  3735. Program.prototype.pause = function(callback) {
  3736. var self = this
  3737. , isAlt = this.isAlt
  3738. , mouseEnabled = this.mouseEnabled;
  3739. this.lsaveCursor('pause');
  3740. //this.csr(0, screen.height - 1);
  3741. if (isAlt) this.normalBuffer();
  3742. this.showCursor();
  3743. if (mouseEnabled) this.disableMouse();
  3744. var write = this.output.write;
  3745. this.output.write = function() {};
  3746. if (this.input.setRawMode) {
  3747. this.input.setRawMode(false);
  3748. }
  3749. this.input.pause();
  3750. return this._resume = function() {
  3751. delete self._resume;
  3752. if (self.input.setRawMode) {
  3753. self.input.setRawMode(true);
  3754. }
  3755. self.input.resume();
  3756. self.output.write = write;
  3757. if (isAlt) self.alternateBuffer();
  3758. //self.csr(0, screen.height - 1);
  3759. if (mouseEnabled) self.enableMouse();
  3760. self.lrestoreCursor('pause', true);
  3761. if (callback) callback();
  3762. };
  3763. };
  3764. Program.prototype.resume = function() {
  3765. if (this._resume) return this._resume();
  3766. };
  3767. /**
  3768. * Helpers
  3769. */
  3770. // We could do this easier by just manipulating the _events object, or for
  3771. // older versions of node, manipulating the array returned by listeners(), but
  3772. // neither of these methods are guaranteed to work in future versions of node.
  3773. function unshiftEvent(obj, event, listener) {
  3774. var listeners = obj.listeners(event);
  3775. obj.removeAllListeners(event);
  3776. obj.on(event, listener);
  3777. listeners.forEach(function(listener) {
  3778. obj.on(event, listener);
  3779. });
  3780. }
  3781. function merge(out) {
  3782. slice.call(arguments, 1).forEach(function(obj) {
  3783. Object.keys(obj).forEach(function(key) {
  3784. out[key] = obj[key];
  3785. });
  3786. });
  3787. return out;
  3788. }
  3789. /**
  3790. * Expose
  3791. */
  3792. module.exports = Program;