Terminal.swift 173 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859
  1. //
  2. // Terminal.swift
  3. // SwiftTerm
  4. //
  5. // Created by Miguel de Icaza on 3/27/19.
  6. // Copyright © 2019 Miguel de Icaza. All rights reserved.
  7. //
  8. // TODO: review every place that sets cursor to use setCursor
  9. // TODO: audit every location to use restrictCursor
  10. import Foundation
  11. /**
  12. * The terminal delegate is a protocol that must be implemented by a class
  13. * that would provide a user interface for the terminal, and it is used by the
  14. * `Terminal` to notify of important changes on the underlying terminal
  15. */
  16. public protocol TerminalDelegate: AnyObject {
  17. /**
  18. * Invoked to request that the cursor be shown
  19. */
  20. func showCursor (source: Terminal)
  21. /**
  22. * Invoked to request that the cursor be shown
  23. */
  24. func hideCursor (source: Terminal)
  25. /**
  26. * This method is invoked when the terminal needs to set the title for the window,
  27. * a UI toolkit would react by setting the terminal title in the window or any other
  28. * user visible element.
  29. *
  30. * The default implementation does nothing.
  31. */
  32. func setTerminalTitle (source: Terminal, title: String)
  33. /**
  34. * This method is invoked when the terminal needs to set the title for the minimized icon,
  35. * a UI toolkit would react by setting the terminal title in the icon or any other
  36. * user visible element
  37. *
  38. * The default implementation does nothing.
  39. */
  40. func setTerminalIconTitle (source: Terminal, title: String)
  41. /**
  42. * These are various commands that are sent by the client. They are rare,
  43. * and if you do not know what to return, just return nil, the terminal
  44. * will return a suitable value.
  45. *
  46. * The response string needs to be suitable for the Xterm CSI Ps ; Ps ; Ps t command
  47. * see the WindowManipulationCommand enumeration for those that need to return values
  48. *
  49. * The default implementation does nothing.
  50. */
  51. @discardableResult
  52. func windowCommand (source: Terminal, command: Terminal.WindowManipulationCommand) -> [UInt8]?
  53. /**
  54. * This method is invoked when the terminal dimensions have changed in response
  55. * to an escape sequence that triggers a terminal resize, the user interface toolkit
  56. * should attempt to accomodate the new window size
  57. *
  58. * TODO: This is not wired up
  59. *
  60. * The default implementation does nothing.
  61. */
  62. func sizeChanged (source: Terminal)
  63. /**
  64. * Sends the byte data to the client connected to the terminal (in terminal emulation
  65. * documentation, this is the "host")
  66. */
  67. func send (source: Terminal, data: ArraySlice<UInt8>)
  68. // callbacks
  69. /// Callback - the window was scrolled, new yDisplay passed
  70. /// The default implementation does nothing.
  71. func scrolled (source: Terminal, yDisp: Int)
  72. /// Callback a newline was generated
  73. /// The default implementation does nothing.
  74. func linefeed (source: Terminal)
  75. /// This method is invoked when the buffer changes from Normal to Alternate, or Alternate to Normal
  76. /// The default implementation does nothing.
  77. func bufferActivated (source: Terminal)
  78. /// Should raise the bell
  79. /// The default implementation does nothing.
  80. func bell (source: Terminal)
  81. /**
  82. * This is invoked when the selection has changed, or has been turned on. The status is
  83. * available in `terminal.selection.active`, and the range relative to the buffer is
  84. * in `terminal.selection.start` and `terminal.selection.end`
  85. *
  86. * The default implementation does nothing.
  87. */
  88. func selectionChanged (source: Terminal)
  89. /**
  90. * This method should return `true` if operations that can read the buffer back should be allowed,
  91. * otherwise, return false. This is useful to run some applications that attempt to checksum the
  92. * contents of the screen (unit tests)
  93. *
  94. * The default implementation returns `true`
  95. */
  96. func isProcessTrusted (source: Terminal) -> Bool
  97. /**
  98. * This method is invoked when the `mouseMode` property has changed, and gives the UI
  99. * a chance to update any tracking capabilities that are required in the toolkit or no longer
  100. * required to provide the events.
  101. *
  102. * The default implementation ignores the mouse change
  103. */
  104. func mouseModeChanged (source: Terminal)
  105. /**
  106. * This method is invoked when a request to change the cursor style has been issued
  107. * by client application.
  108. */
  109. func cursorStyleChanged (source: Terminal, newStyle: CursorStyle)
  110. /**
  111. * This method is invoked when the client application has issued a command to report
  112. * its current working directory (this is done with the OSC 7 command). The value can be
  113. * read by accessing the `hostCurrentDirectory` property.
  114. *
  115. * The default implementaiton does nothing.
  116. */
  117. func hostCurrentDirectoryUpdated (source: Terminal)
  118. /**
  119. * This method is invoked when the client application has issued a command to report
  120. * its current document (this is done with the OSC 6 command). The value can be
  121. * read by accessing the `hostCurrentDocument` property.
  122. *
  123. * The default implementaiton does nothing.
  124. */
  125. func hostCurrentDocumentUpdated (source: Terminal)
  126. /**
  127. * This method is invoked when a color in the 0..255 palette has been redefined, if the
  128. * front-end keeps a cache or uses indexed rendering, it should update the color
  129. * with the new values. If the value of idx is nil, this means all the ansi colors changed
  130. */
  131. func colorChanged (source: Terminal, idx: Int?)
  132. /**
  133. * The view should try to set the foreground color to the provided color
  134. */
  135. func setForegroundColor (source: Terminal, color: Color)
  136. /**
  137. * The view should try to set the background color to the provided color
  138. */
  139. func setBackgroundColor (source: Terminal, color: Color)
  140. /**
  141. * This should return the current foreground and background colors to
  142. * report.
  143. */
  144. func getColors (source: Terminal) -> (foreground: Color, background: Color)
  145. /**
  146. * This method is invoked when the client application (iTerm2) has issued a OSC 1337 and
  147. * SwiftTerm did not handle a handler for it.
  148. *
  149. * The default implementaiton does nothing.
  150. */
  151. func iTermContent (source: Terminal, content: ArraySlice<UInt8>)
  152. /**
  153. * This method is invoked when the client application has issued a OSC 52
  154. * to put data on the clipboard.
  155. *
  156. * - Parameters:
  157. * - source: identifies the instance of the terminal that sent this request
  158. * - content: the data to place on the clipboard
  159. * The default implementation does nothing.
  160. */
  161. func clipboardCopy(source: Terminal, content: Data)
  162. /**
  163. * Invoked when client application issues OSC 777 to show notification.
  164. *
  165. * The default implementation does nothing.
  166. * - Parameters:
  167. * - source: identifies the instance of the terminal that sent this request
  168. * - title: the title to show for the notification
  169. * - body: the body of the notification
  170. */
  171. func notify(source: Terminal, title: String, body: String)
  172. /**
  173. * Invoked to create an image from an RGBA buffer at the current cursor position
  174. *
  175. * The default implementation does nothing.
  176. * - Parameters:
  177. * - source: identifies the instance of the terminal that sent this request
  178. * - bytes: Image buffer in RGBA format, using 8 bits per channel.
  179. * - width: the width in pixels of the image
  180. * - height: the height in pixels of the image
  181. */
  182. func createImageFromBitmap (source: Terminal, bytes: inout [UInt8], width: Int, height: Int)
  183. /**
  184. * Invoked to create an image from a byte blob that might be encoded in one of the various
  185. * compressed file formats (unlike the other option that gets an RGBA buffer already decoded).
  186. * It also included requests for the desired dimensions.
  187. * - Parameters:
  188. * - source: identifies the instance of the terminal that sent this request
  189. * - data: Binary blob containing the image data, which is typically encoded as a PNG or JPEG file
  190. * - widthRequest: the width requested, it contains an enumeration describing what the request was
  191. * - height: the height requested, it contains an enumeration describing what the request was
  192. * - preserveAspectRatio: if set, one of the dimensions will track the hardcoded setting set for the other.
  193. */
  194. func createImage (source: Terminal, data: Data, width: ImageSizeRequest, height: ImageSizeRequest, preserveAspectRatio: Bool)
  195. }
  196. /// Enumeration passed to the TerminalDelegate.createImage to configure
  197. /// the desired values for width and height.
  198. public enum ImageSizeRequest {
  199. /// Make the best decision based on the image data
  200. case auto
  201. /// Occupy exactly the number of cells
  202. case cells(Int)
  203. /// Occupy exactly the pixels listed
  204. case pixels(Int)
  205. /// Occupy a percentange size relative to the dimension of the visible region
  206. case percent(Int)
  207. }
  208. public protocol TerminalImage {
  209. /// The width of the image in pixels
  210. var pixelWidth: Int { get }
  211. /// The height of the image in pixels
  212. var pixelHeight: Int { get }
  213. /// Column where the image was attached
  214. var col: Int { get set }
  215. }
  216. /**
  217. * The `Terminal` class provides the terminal emulation engine, and can be used to feed data to the
  218. * terminal emulator. Typically users will intereact with a higher-level implementation that provides a
  219. * UI toolkit-specific rendering and connects the input to the UI toolkit.
  220. *
  221. * A front-end would draw the contents of the terminal, and take input from the user, which is in turn
  222. * either mapped to one of the public APIs here, or if it is user input is passed to the `feed` methods here.
  223. *
  224. * The terminal is also connected to a backend that is conneted to the client, and data from this
  225. * client is fed into the emulator by calling the `sendResponse method`
  226. *
  227. * The behavior of the terminal is configured by implementing the `TerminalDelegate` protocol
  228. * that is provided in the constructor call.
  229. */
  230. open class Terminal {
  231. let MINIMUM_COLS = 2
  232. let MINIMUM_ROWS = 1
  233. /// The current terminal columns (counting from 1)
  234. public private(set) var cols: Int = 80
  235. /// The current terminal rows (counting from 1)
  236. public private(set) var rows: Int = 25
  237. var tabStopWidth : Int = 8
  238. var options: TerminalOptions
  239. // The current buffers
  240. var buffers : BufferSet!
  241. // Whether the terminal is operating in application keypad mode
  242. var applicationKeypad : Bool = false
  243. // Whether the terminal is operating in application cursor mode
  244. public var applicationCursor : Bool = false
  245. // You can ignore most of the defaults set here, the function
  246. // reset() will do that again
  247. var sendFocus: Bool = false
  248. var cursorHidden : Bool = false
  249. /// Controls the origin mode (DECOM), when set, the screen is limited to the top and bottom margins
  250. var originMode: Bool = false
  251. /// Controls whether it is possible to set left and right margin modes
  252. var marginMode: Bool = false
  253. var insertMode: Bool = false
  254. /// Indicates that the application has toggled bracketed paste mode, which means that when content is pasted into
  255. /// the terminal, the content will be wrapped in "ESC [ 200 ~" to start, and "ESC [ 201 ~" to end.
  256. public private(set) var bracketedPasteMode: Bool = false
  257. var charset: [UInt8:String]? = nil
  258. var gcharset: Int = 0
  259. var wraparound: Bool = false
  260. var reverseWraparound: Bool = false
  261. weak var tdel: TerminalDelegate?
  262. var curAttr: Attribute = CharData.defaultAttr
  263. var gLevel: UInt8 = 0
  264. var cursorBlink: Bool = false
  265. var allow80To132 = true
  266. var parser: EscapeSequenceParser
  267. var refreshStart = Int.max
  268. var refreshEnd = -1
  269. var scrollInvariantRefreshStart = Int.max
  270. var scrollInvariantRefreshEnd = -1
  271. var userScrolling = false
  272. var lineFeedMode = false
  273. // Installed colors are the 16 values that can be changed dynamically by the host
  274. var installedColors: [Color]
  275. // The blueprint for the colors, computed based on the installed colors
  276. var defaultAnsiColors: [Color]
  277. // The active set of colors (based on the blueprint)
  278. var ansiColors: [Color]
  279. // Control codes provides an API to send either 8bit sequences or 7bit sequences for C0 and C1 depending on the terminal state
  280. var cc: CC
  281. /// This variable if set, contains an URI representing the host and directory of the process running in the terminal
  282. /// it is often used by applciations to track the working directory. It might be nil, or might not be correct, the
  283. /// contents are entirely under the control of the remote application, and require the terminal to be trusted
  284. /// (see the `isProcessTrusted` method in the `TerminalDelegate`). When this is set the
  285. /// `hostCurrentDirectoryUpdated` method on the delegate is invoked.
  286. public private(set) var hostCurrentDirectory: String? = nil
  287. /// This variable if set, contains an URI representing the host and current document of the process
  288. /// running in the terminal. It might be nil, or might not be correct, the
  289. /// contents are entirely under the control of the remote application, and require the terminal to be trusted
  290. /// (see the `isProcessTrusted` method in the `TerminalDelegate`). When this is set the
  291. /// `hostCurrentDocumentUpdated` method on the delegate is invoked.
  292. public private(set) var hostCurrentDocument: String? = nil
  293. /// The current attribute used by the terminal by default
  294. public var currentAttribute: Attribute {
  295. get { return curAttr }
  296. }
  297. // The requested conformance from DECSCL command
  298. enum TerminalConformance {
  299. case vt100
  300. case vt200
  301. case vt300
  302. case vt400
  303. case vt500
  304. }
  305. // The mouse coordinates can be encoded in a number of ways, and obey to historical
  306. // upgrades to the protocol, but also attempts at fixing limitations of the different
  307. // encodings.
  308. enum MouseProtocolEncoding {
  309. // The default x10 mode is limited to coordinates up to 223.
  310. // (255-32). The other modes solve this limitaion
  311. case x10
  312. // Extends the range of a coordinate to 2015 by using UTF-8 encoding of the
  313. // coordinate value. This encoding is troublesome for applications that
  314. // do not support utf8 input.
  315. case utf8
  316. // The response uses CSI < ButtonValue ; Px ; Py [Mm]
  317. case sgr
  318. // Different response style, with possible ambiguities, not recommended
  319. case urxvt
  320. }
  321. // The protocol encoding for the terminal
  322. private var mouseProtocol: MouseProtocolEncoding = .x10
  323. // This is used to track if we are setting the colors, to prevent a
  324. // recursive invocation (nativeForegroundColor sets the terminal
  325. // color, which in turn broadcasts the request for a change)
  326. var settingFgColor = false, settingBgColor = false
  327. /// This tracks the current foreground color for the application.
  328. public var foregroundColor: Color = Color.defaultForeground {
  329. didSet {
  330. if settingFgColor {
  331. return
  332. }
  333. settingFgColor = true
  334. tdel?.setForegroundColor(source: self, color: foregroundColor)
  335. settingFgColor = false
  336. }
  337. }
  338. /// This tracks the current background color for the application.
  339. public var backgroundColor: Color = Color.defaultBackground {
  340. didSet {
  341. if settingBgColor {
  342. return
  343. }
  344. settingBgColor = true
  345. tdel?.setBackgroundColor(source: self, color: backgroundColor)
  346. settingBgColor = false
  347. }
  348. }
  349. ///
  350. /// Represents the mouse operation mode that the terminal is currently using and higher level
  351. /// implementations should use the functions in this enumeration to determine what events to
  352. /// send
  353. public enum MouseMode {
  354. /// No mouse events are reported
  355. case off
  356. /// X10 Compatibility mode - only sends events in button press
  357. case x10
  358. /// VT200, also known as Normal Tracking Mode - sends both press and release events
  359. case vt200
  360. /// ButtonEventTracking - In addition to sending button press and release events, it sends motion events when the button is pressed
  361. case buttonEventTracking
  362. /// Sends button presses, button releases, and motion events regardless of the button state
  363. case anyEvent
  364. // Unsupported modes:
  365. // - vt200Highlight, this can deadlock the terminal
  366. // - declocator, rarely used
  367. /// Returns true if you should send a button press event (separate from release)
  368. func sendButtonPress () -> Bool
  369. {
  370. self == .vt200 || self == .buttonEventTracking || self == .anyEvent
  371. }
  372. /// Returns true if you should send the button release event
  373. func sendButtonRelease () -> Bool
  374. {
  375. self != .off
  376. }
  377. /// Returns true if you should send a motion event when a button is pressed
  378. func sendButtonTracking () -> Bool
  379. {
  380. self == .buttonEventTracking || self == .anyEvent
  381. }
  382. /// Returns true if you should send a motion event, regardless of button state
  383. public func sendMotionEvent () -> Bool
  384. {
  385. self == .anyEvent
  386. }
  387. /// Returns true if the modifiers should be encoded
  388. public func sendsModifiers() -> Bool {
  389. self == .vt200 || self == .buttonEventTracking || self == .anyEvent
  390. }
  391. }
  392. public private(set) var mouseMode: MouseMode = .off {
  393. didSet {
  394. tdel?.mouseModeChanged (source: self)
  395. }
  396. }
  397. // The next four variables determine whether setting/querying should be done using utf8 or latin1
  398. // and whether the values should be set or queried using hex digits, rather than actual byte streams
  399. var xtermTitleSetUtf = false
  400. var xtermTitleSetHex = false
  401. var xtermTitleQueryUtf = false
  402. var xtermTitleQueryHex = false
  403. var conformance: TerminalConformance = .vt500
  404. /**
  405. * Returns true if we should respect the left/right margins, which is based on the originMode and marginMode setting
  406. */
  407. func usingMargins() ->Bool
  408. {
  409. return originMode && marginMode
  410. }
  411. /// Returns the terminal dimensions 1-based values
  412. public func getDims () -> (cols: Int,rows: Int)
  413. {
  414. return (cols, rows)
  415. }
  416. public init (delegate : TerminalDelegate, options: TerminalOptions = TerminalOptions.default)
  417. {
  418. installedColors = Color.defaultInstalledColors
  419. defaultAnsiColors = Color.setupDefaultAnsiColors (initialColors: installedColors)
  420. ansiColors = defaultAnsiColors
  421. tdel = delegate
  422. self.options = options
  423. // This duplicates the setup above, but
  424. parser = EscapeSequenceParser ()
  425. cc = CC(send8bit: false)
  426. configureParser (parser)
  427. setup ()
  428. }
  429. /// Installs the new colors as the default colors and recomputes the
  430. /// current and ansi palette. This will not change the UI layer, for that it is better
  431. /// to call the `installColors` method on `TerminalView`, which will
  432. /// both call this method, and update the display appropriately.
  433. ///
  434. /// - Parameter colors: this should be an array of 16 values that correspond to the 16 ANSI colors,
  435. /// if the array does not contain 16 elements, it will not do anything
  436. public func installPalette (colors: [Color])
  437. {
  438. if colors.count != 16 {
  439. return
  440. }
  441. installedColors = colors
  442. defaultAnsiColors = Color.setupDefaultAnsiColors (initialColors: installedColors)
  443. ansiColors = defaultAnsiColors
  444. }
  445. /**
  446. * Returns the active buffer (either the normal buffer or the alternative buffer)
  447. */
  448. var buffer: Buffer {
  449. get {
  450. buffers!.active
  451. }
  452. }
  453. /// Returns the CharData at the specified column and row from the visible portion of the buffer, these are zero-based
  454. ///
  455. /// - Parameter col: column to retrieve, starts at 0
  456. /// - Parameter row: row to retrieve, starts at 0
  457. /// - Returns: nil if the col or row are out of bounds, or the CharData contained in that cell otherwise
  458. ///
  459. public func getCharData (col: Int, row: Int) -> CharData?
  460. {
  461. if col < 0 || col >= cols {
  462. return nil
  463. }
  464. if let l = getLine (row: row) {
  465. return l [col]
  466. }
  467. return nil
  468. }
  469. /// Returns the contents of a line as a BufferLine, or nil if the requested line is out of range
  470. ///
  471. /// The line is counted from start of scroll back, not what the terminal has visible right now.
  472. /// - Parameter row: the row to retrieve, relative to the scroll buffer, not the visible display
  473. /// - Returns: nil if the col or row are out of bounds, or the BufferLine otherwise
  474. public func getLine (row: Int) -> BufferLine? {
  475. if row < 0 || row >= rows {
  476. return nil
  477. }
  478. return buffer.lines [row + buffer.yDisp]
  479. }
  480. /// Returns the contents of a line as a BufferLine counting from the begging of the scroll buffer.
  481. ///
  482. /// The line is counted from start of scroll back, not what the terminal has visible right now.
  483. /// - Parameter row: the row to retrieve, relative to the scroll buffer, not the visible display
  484. /// - Returns: nil if the col or row are out of bounds, or the BufferLine otherwise
  485. public func getScrollInvariantLine (row: Int) -> BufferLine? {
  486. if row < buffer.linesTop || row >= buffer.lines.count + buffer.linesTop {
  487. return nil
  488. }
  489. return buffer.lines [row-buffer.linesTop]
  490. }
  491. /// Returns the character at the specified column and row, these are zero-based
  492. /// - Parameter col: column to retrieve, starts at 0
  493. /// - Parameter row: row to retrieve, starts at 0
  494. /// - Returns: nil if the col or row are out of bounds, or the Character contained in that cell otherwise
  495. public func getCharacter (col: Int, row: Int) -> Character?
  496. {
  497. return getCharData(col: col, row: row)?.getCharacter()
  498. }
  499. func setup (isReset: Bool = false)
  500. {
  501. // Sadly a duplicate of much of what lives in init() due to Swift not allowing me to
  502. // call this
  503. cols = max (options.cols, MINIMUM_COLS)
  504. rows = max (options.rows, MINIMUM_ROWS)
  505. if buffers != nil && isReset {
  506. buffers.resetNormal ()
  507. buffers.activateNormalBuffer(clearAlt: false)
  508. } else if buffers == nil {
  509. buffers = BufferSet(self)
  510. }
  511. cursorHidden = false
  512. // modes
  513. applicationKeypad = false
  514. applicationCursor = false
  515. originMode = false
  516. marginMode = false
  517. insertMode = false
  518. wraparound = true
  519. bracketedPasteMode = false
  520. // charset'
  521. charset = nil
  522. gcharset = 0
  523. gLevel = 0
  524. curAttr = CharData.defaultAttr
  525. mouseMode = .off
  526. buffer.scrollTop = 0
  527. buffer.scrollBottom = rows-1
  528. buffer.marginLeft = 0
  529. buffer.marginRight = cols-1
  530. cc.send8bit = false
  531. conformance = .vt500
  532. allow80To132 = true
  533. xtermTitleSetUtf = false
  534. xtermTitleQueryUtf = false
  535. xtermTitleSetHex = false
  536. xtermTitleQueryHex = false
  537. hyperLinkTracking = nil
  538. cursorBlink = false
  539. hostCurrentDirectory = nil
  540. lineFeedMode = options.convertEol
  541. }
  542. // DCS $ q Pt ST
  543. // DECRQSS (https://vt100.net/docs/vt510-rm/DECRQSS.html)
  544. // Request Status String (DECRQSS), VT420 and up.
  545. // Response: DECRPSS (https://vt100.net/docs/vt510-rm/DECRPSS.html)
  546. class DECRQSS : DcsHandler {
  547. var data: [UInt8]
  548. var terminal: Terminal
  549. public init (terminal: Terminal)
  550. {
  551. self.terminal = terminal
  552. data = []
  553. }
  554. func hook (collect: cstring, parameters: [Int], flag: UInt8)
  555. {
  556. data = []
  557. }
  558. func put (data : ArraySlice<UInt8>)
  559. {
  560. for x in data {
  561. self.data.append(x)
  562. }
  563. }
  564. func unhook ()
  565. {
  566. let newData = String (bytes: data, encoding: .ascii)
  567. var ok = 1 // 0 means the request is valid according to docs, but tests expect 0?
  568. var result: String
  569. switch (newData) {
  570. case "\"q": // DECCSA - Set Character Attribute
  571. result = "\"q"
  572. case "\"p": // DECSCL - conformance level
  573. result = "65;1\"p"
  574. case "r": // DECSTBM - the top and bottom margins
  575. result = "\(terminal.buffer.scrollTop + 1);\(terminal.buffer.scrollBottom + 1)r"
  576. case "m": // SGR - the set graphic rendition
  577. // TODO: report real settings instead of 0m
  578. result = terminal.curAttr.toSgr ()
  579. case "s": // DECSLRM - the current left and right margins
  580. result = "\(terminal.buffer.marginLeft+1);\(terminal.buffer.marginRight+1)s"
  581. case " q": // DECSCUSR - the set cursor style
  582. // TODO this should send a number for the current cursor style 2 for block, 4 for underline and 6 for bar
  583. let style = "2" // block
  584. result = "\(style) q"
  585. default:
  586. ok = 0 // this means the request is not valid, report that to the host.
  587. // invalid: DCS 0 $ r Pt ST (xterm)
  588. terminal.log ("Unknown DCS + \(newData!)")
  589. result = newData ?? ""
  590. }
  591. terminal.sendResponse (terminal.cc.DCS, "\(ok)$r\(result)", terminal.cc.ST)
  592. }
  593. }
  594. // Configures the EscapeSequenceParser
  595. func configureParser (_ parser: EscapeSequenceParser)
  596. {
  597. parser.csiHandlerFallback = { (pars: [Int], collect: cstring, code: UInt8) -> () in
  598. let ch = Character(UnicodeScalar(code))
  599. self.log ("Unknown CSI Code (collect=\(collect) code=\(ch) pars=\(pars))")
  600. }
  601. parser.escHandlerFallback = { (txt: cstring, flag: UInt8) in
  602. self.log ("Unknown ESC Code: ESC + \(Character(Unicode.Scalar (flag))) txt=\(txt)")
  603. }
  604. parser.executeHandlerFallback = {
  605. self.log ("Unknown EXECUTE code")
  606. }
  607. parser.oscHandlerFallback = { (code: Int) in
  608. self.log ("Unknown OSC code: \(code)")
  609. }
  610. parser.printHandler = handlePrint
  611. parser.printStateReset = printStateReset
  612. // CSI handler
  613. parser.csiHandlers [UInt8 (ascii: "@")] = cmdInsertChars
  614. parser.csiHandlers [UInt8 (ascii: "A")] = cmdCursorUp
  615. parser.csiHandlers [UInt8 (ascii: "B")] = cmdCursorDown
  616. parser.csiHandlers [UInt8 (ascii: "C")] = cmdCursorForward
  617. parser.csiHandlers [UInt8 (ascii: "D")] = cmdCursorBackward
  618. parser.csiHandlers [UInt8 (ascii: "E")] = cmdCursorNextLine
  619. parser.csiHandlers [UInt8 (ascii: "F")] = cmdCursorPrecedingLine
  620. parser.csiHandlers [UInt8 (ascii: "G")] = cmdCursorCharAbsolute
  621. parser.csiHandlers [UInt8 (ascii: "H")] = cmdCursorPosition
  622. parser.csiHandlers [UInt8 (ascii: "I")] = cmdCursorForwardTab
  623. parser.csiHandlers [UInt8 (ascii: "J")] = cmdEraseInDisplay
  624. parser.csiHandlers [UInt8 (ascii: "K")] = cmdEraseInLine
  625. parser.csiHandlers [UInt8 (ascii: "L")] = cmdInsertLines
  626. parser.csiHandlers [UInt8 (ascii: "M")] = cmdDeleteLines
  627. parser.csiHandlers [UInt8 (ascii: "P")] = cmdDeleteChars
  628. parser.csiHandlers [UInt8 (ascii: "S")] = cmdScrollUp
  629. parser.csiHandlers [UInt8 (ascii: "T")] = csiT
  630. parser.csiHandlers [UInt8 (ascii: "X")] = cmdEraseChars
  631. parser.csiHandlers [UInt8 (ascii: "Z")] = cmdCursorBackwardTab
  632. parser.csiHandlers [UInt8 (ascii: "`")] = cmdCharPosAbsolute
  633. parser.csiHandlers [UInt8 (ascii: "a")] = cmdHPositionRelative
  634. parser.csiHandlers [UInt8 (ascii: "b")] = cmdRepeatPrecedingCharacter
  635. parser.csiHandlers [UInt8 (ascii: "c")] = cmdSendDeviceAttributes
  636. parser.csiHandlers [UInt8 (ascii: "d")] = cmdLinePosAbsolute
  637. parser.csiHandlers [UInt8 (ascii: "e")] = cmdVPositionRelative
  638. parser.csiHandlers [UInt8 (ascii: "f")] = cmdHVPosition
  639. parser.csiHandlers [UInt8 (ascii: "g")] = cmdTabClear
  640. parser.csiHandlers [UInt8 (ascii: "h")] = cmdSetMode
  641. parser.csiHandlers [UInt8 (ascii: "l")] = cmdResetMode
  642. parser.csiHandlers [UInt8 (ascii: "m")] = cmdCharAttributes
  643. parser.csiHandlers [UInt8 (ascii: "n")] = cmdDeviceStatus
  644. parser.csiHandlers [UInt8 (ascii: "p")] = csiPHandler
  645. parser.csiHandlers [UInt8 (ascii: "q")] = cmdSetCursorStyle
  646. parser.csiHandlers [UInt8 (ascii: "r")] = cmdSetScrollRegion
  647. parser.csiHandlers [UInt8 (ascii: "s")] = { [weak self] args, cstring in
  648. // "CSI s" is overloaded, can mean save cursor, but also set the margins with DECSLRM
  649. if self!.marginMode {
  650. self!.cmdSetMargins (args, cstring)
  651. } else {
  652. self!.cmdSaveCursor (args, cstring)
  653. }
  654. }
  655. parser.csiHandlers [UInt8 (ascii: "t")] = csit
  656. parser.csiHandlers [UInt8 (ascii: "u")] = cmdRestoreCursor
  657. parser.csiHandlers [UInt8 (ascii: "v")] = csiCopyRectangularArea
  658. parser.csiHandlers [UInt8 (ascii: "x")] = csiX /* x DECFRA - could be overloaded */
  659. parser.csiHandlers [UInt8 (ascii: "y")] = cmdDECRQCRA /* y - Checksum Region */
  660. parser.csiHandlers [UInt8 (ascii: "z")] = csiZ /* DECERA */
  661. parser.csiHandlers [UInt8 (ascii: "{")] = csiOpenBrace
  662. parser.csiHandlers [UInt8 (ascii: "}")] = csiCloseBrace
  663. parser.csiHandlers [UInt8 (ascii: "~")] = cmdDeleteColumns
  664. parser.executeHandlers [7] = { [weak self] in self!.tdel?.bell (source: self!) }
  665. parser.executeHandlers [10] = cmdLineFeed
  666. parser.executeHandlers [11] = cmdLineFeedBasic // VT Vertical Tab - ignores auto-new-line behavior in ConvertEOL
  667. parser.executeHandlers [12] = cmdLineFeedBasic
  668. parser.executeHandlers [13] = cmdCarriageReturn
  669. parser.executeHandlers [8] = cmdBackspace
  670. parser.executeHandlers [9] = cmdTab
  671. parser.executeHandlers [14] = cmdShiftOut
  672. parser.executeHandlers [15] = cmdShiftIn
  673. parser.executeHandlers [0x84] = cmdIndex
  674. parser.executeHandlers [0x85] = cmdNextLine
  675. parser.executeHandlers [0x88] = cmdTabSet
  676. //
  677. // OSC handler
  678. //
  679. // 0 - icon name + title
  680. parser.oscHandlers [0] = { data in self.setTitle(text: String (bytes: data, encoding: .utf8) ?? "")}
  681. // 1 - icon name
  682. parser.oscHandlers [1] = { data in self.setIconTitle(text: String (bytes: data, encoding: .utf8) ?? "") }
  683. // 2 - title
  684. parser.oscHandlers [2] = { data in self.setTitle(text: String (bytes: data, encoding: .utf8) ?? "")}
  685. // 3 - set property X in the form "prop=value"
  686. // 4 - Change Color Number()
  687. parser.oscHandlers [4] = oscChangeOrQueryColorIndex
  688. // 5 - Change Special Color Number
  689. // 6 - Enable/disable Special Color Number c
  690. // 6 - current document:
  691. parser.oscHandlers [6] = oscSetCurrentDocument
  692. // 7 - current directory? (not in xterm spec, see https://gitlab.com/gnachman/iterm2/issues/3939)
  693. parser.oscHandlers [7] = oscSetCurrentDirectory
  694. parser.oscHandlers [8] = oscHyperlink
  695. // 10 - Change VT100 text foreground color to Pt.
  696. parser.oscHandlers [10] = oscSetTextForeground
  697. // 11 - Change VT100 text background color to Pt.
  698. parser.oscHandlers [11] = oscSetTextBackground
  699. // 12 - Change text cursor color to Pt.
  700. // 13 - Change mouse foreground color to Pt.
  701. // 14 - Change mouse background color to Pt.
  702. // 15 - Change Tektronix foreground color to Pt.
  703. // 16 - Change Tektronix background color to Pt.
  704. // 17 - Change highlight background color to Pt.
  705. // 18 - Change Tektronix cursor color to Pt.
  706. // 19 - Change highlight foreground color to Pt.
  707. // 46 - Change Log File to Pt.
  708. // 50 - Set Font to Pt.
  709. // 51 - reserved for Emacs shell.
  710. // 52 - Clipboard operations
  711. parser.oscHandlers [52] = oscClipboard
  712. // 104 ; c - Reset Color Number c.
  713. parser.oscHandlers [104] = oscResetColor
  714. // 105 ; c - Reset Special Color Number c.
  715. // 106 ; c; f - Enable/disable Special Color Number c.
  716. // 110 - Reset VT100 text foreground color.
  717. // 111 - Reset VT100 text background color.
  718. // 112 - Reset text cursor color.
  719. // 113 - Reset mouse foreground color.
  720. // 114 - Reset mouse background color.
  721. // 115 - Reset Tektronix foreground color.
  722. // 116 - Reset Tektronix background color.
  723. parser.oscHandlers [777] = oscNotification
  724. parser.oscHandlers [1337] = osciTerm2
  725. //
  726. // ESC handlers
  727. //
  728. parser.setEscHandler("6", { collect, flags in self.columnIndex (back: true) })
  729. parser.setEscHandler ("7", { collect, flag in self.cmdSaveCursor ([], []) })
  730. parser.setEscHandler ("8", { collect, flag in self.cmdRestoreCursor ([], []) })
  731. parser.setEscHandler ("9", { collect, flag in self.columnIndex(back: false) })
  732. parser.setEscHandler ("D", { collect, flag in self.cmdIndex() })
  733. parser.setEscHandler ("E", { collect, flag in self.cmdNextLine () })
  734. parser.setEscHandler ("H", { collect, flag in self.cmdTabSet ()})
  735. parser.setEscHandler ("M", { collect, flag in self.reverseIndex() })
  736. parser.setEscHandler ("=", { collect, flags in self.cmdKeypadApplicationMode ()})
  737. parser.setEscHandler (">", { collect, flags in self.cmdKeypadNumericMode ()})
  738. parser.setEscHandler ("c", { collect, flags in self.cmdReset () })
  739. parser.setEscHandler ("n", { collect, flag in self.setgLevel (2) })
  740. parser.setEscHandler ("o", { collect, flag in self.setgLevel (3) })
  741. parser.setEscHandler ("|", { collect, flag in self.setgLevel (3) })
  742. parser.setEscHandler ("}", { collect, flag in self.setgLevel (2) })
  743. parser.setEscHandler ("~", { collect, flag in self.setgLevel (1) })
  744. parser.setEscHandler ("%@", { collect, flag in self.cmdSelectDefaultCharset () })
  745. parser.setEscHandler ("%G", { collect, flag in self.cmdSelectDefaultCharset () })
  746. parser.setEscHandler ("#8", { collect, flag in self.cmdScreenAlignmentPattern () })
  747. parser.setEscHandler (" G") { collect, flags in self.cmdSet8BitControls () }
  748. parser.setEscHandler (" F") { collect, flags in self.cmdSet7BitControls () }
  749. for bflag in CharSets.all.keys {
  750. let flag = String (UnicodeScalar (bflag))
  751. parser.setEscHandler ("(" + flag, { code, f in self.selectCharset ([UInt8 (ascii: "(")] + [f]) })
  752. parser.setEscHandler (")" + flag, { code, f in self.selectCharset ([UInt8 (ascii: ")")] + [f]) })
  753. parser.setEscHandler ("*" + flag, { code, f in self.selectCharset ([UInt8 (ascii: "*")] + [f]) })
  754. parser.setEscHandler ("+" + flag, { code, f in self.selectCharset ([UInt8 (ascii: "+")] + [f]) })
  755. parser.setEscHandler ("-" + flag, { code, f in self.selectCharset ([UInt8 (ascii: "-")] + [f]) })
  756. parser.setEscHandler ("." + flag, { code, f in self.selectCharset ([UInt8 (ascii: ".")] + [f]) })
  757. parser.setEscHandler ("/" + flag, { code, f in self.selectCharset ([UInt8 (ascii: "/")] + [f]) })
  758. }
  759. // Error handler
  760. parser.errorHandler = { state in
  761. self.log ("Parsing error, state: \(state)")
  762. return state
  763. }
  764. // DCS Handler
  765. parser.setDcsHandler ("$q", DECRQSS (terminal: self))
  766. parser.setDcsHandler ("q", SixelDcsHandler (terminal: self))
  767. parser.dscHandlerFallback = { code, parameters in }
  768. }
  769. func cmdSet8BitControls ()
  770. {
  771. cc.send8bit = true
  772. }
  773. func cmdSet7BitControls ()
  774. {
  775. cc.send8bit = false
  776. }
  777. func emitScroll (_ x: Int)
  778. {
  779. // In the original code, it is mediocre accessibility, so likely will remove this
  780. }
  781. func emitChar (_ ch: Character)
  782. {
  783. // In the original code, it is mediocre accessibility, so likely will remove this
  784. }
  785. //
  786. // Because data might not be complete, we need to put back data that we read to process on
  787. // a future read. To prepare for reading, on every call to parse, the prepare method is
  788. // given the new ArraySlice to read from.
  789. //
  790. // the `hasNext` describes whether there is more data left on the buffer, and `bytesLeft`
  791. // returnes the number of bytes left. The `getNext` method fetches either the next
  792. // value from the putback buffer, or when it is empty, it returns it from the buffer that
  793. // was passed during prepare.
  794. //
  795. // Additionally, the terminal parser needs to reset the parser state on demand, and
  796. // that is surfaced via reset
  797. //
  798. struct ReadingBuffer {
  799. var putbackBuffer: [UInt8] = []
  800. var rest:ArraySlice<UInt8> = [][...]
  801. var idx = 0
  802. var count:Int = 0
  803. // Invoke this method at the beginning of parse
  804. mutating func prepare (_ data: ArraySlice<UInt8>)
  805. {
  806. assert (rest.count == 0)
  807. rest = data
  808. count = putbackBuffer.count + data.count
  809. idx = 0
  810. }
  811. func hasNext () -> Bool {
  812. idx < count
  813. }
  814. func bytesLeft () -> Int
  815. {
  816. count-idx
  817. }
  818. mutating func getNext () -> UInt8
  819. {
  820. if idx < putbackBuffer.count {
  821. let v = putbackBuffer [idx]
  822. idx += 1
  823. return v
  824. }
  825. let v = rest [idx-putbackBuffer.count+rest.startIndex]
  826. idx += 1
  827. return v
  828. }
  829. // Puts back the code, and everything that was pending
  830. mutating func putback (_ code: UInt8)
  831. {
  832. var newPutback: [UInt8] = [code]
  833. let left = bytesLeft()
  834. for _ in 0..<left {
  835. newPutback.append (getNext ())
  836. }
  837. putbackBuffer = newPutback
  838. rest = [][...]
  839. }
  840. mutating func done ()
  841. {
  842. if idx < putbackBuffer.count {
  843. putbackBuffer.removeFirst(idx)
  844. } else {
  845. putbackBuffer = []
  846. }
  847. rest = [][...]
  848. }
  849. mutating func reset ()
  850. {
  851. putbackBuffer = []
  852. idx = 0
  853. }
  854. }
  855. var readingBuffer = ReadingBuffer ()
  856. func printStateReset ()
  857. {
  858. readingBuffer.reset ()
  859. }
  860. // This variable holds the last location that we poked a Character on. This is required
  861. // because combining unicode characters come after the character, so we need to poke back
  862. // at this location. We track the buffer (so we can distinguish Alt/Normal), the buffer line
  863. // that we fetched, and the column.
  864. var lastBufferStorage: (buffer: Buffer, y: Int, x: Int, cols: Int, rows: Int)? = nil
  865. var lastBufferCol: Int = 0
  866. func handlePrint (_ data: ArraySlice<UInt8>)
  867. {
  868. let buffer = self.buffer
  869. readingBuffer.prepare(data)
  870. updateRange (buffer.y)
  871. while readingBuffer.hasNext() {
  872. var ch: Character = " "
  873. var chWidth: Int = 0
  874. let code = readingBuffer.getNext()
  875. let n = UnicodeUtil.expectedSizeFromFirstByte(code)
  876. if n == -1 || n == 1 {
  877. // n == -1 means an Invalid UTF-8 sequence, client sent us some junk, happens if we run
  878. // with the wrong locale set for example if LANG=en, still we handle it here
  879. // get charset replacement character
  880. // charset are only defined for ASCII, therefore we only
  881. // search for an replacement char if code < 127
  882. var chSet = false
  883. if code < 127 && charset != nil {
  884. // Notice that the charset mapping can contain the dutch unicode sequence for "ij",
  885. // so it is not a simple byte, it is a Character
  886. if let str = charset! [UInt8 (code)] {
  887. ch = str.first!
  888. // Every single mapping in the charset only takes one slot
  889. chWidth = 1
  890. chSet = true
  891. }
  892. }
  893. if chSet == false {
  894. let rune = UnicodeScalar (code)
  895. chWidth = UnicodeUtil.columnWidth(rune: rune)
  896. ch = Character (rune)
  897. }
  898. } else if readingBuffer.bytesLeft() >= (n-1) {
  899. var x : [UInt8] = [code]
  900. for _ in 1..<n {
  901. x.append (readingBuffer.getNext())
  902. }
  903. x.append(0)
  904. x.withUnsafeBytes { ptr in
  905. let unsafeBound = ptr.bindMemory(to: UInt8.self)
  906. let unsafePointer = unsafeBound.baseAddress!
  907. let s = String (cString: unsafePointer)
  908. ch = s.first ?? Character (" ")
  909. // Now the challenge is that we have a character, not a rune, and we want to compute
  910. // the width of it.
  911. if ch.unicodeScalars.count == 1 {
  912. chWidth = UnicodeUtil.columnWidth(rune: ch.unicodeScalars.first!)
  913. } else {
  914. chWidth = 0
  915. for scalar in ch.unicodeScalars {
  916. chWidth = max (chWidth, UnicodeUtil.columnWidth(rune: scalar))
  917. }
  918. }
  919. }
  920. } else {
  921. readingBuffer.putback (code)
  922. return
  923. }
  924. if let firstScalar = ch.unicodeScalars.first {
  925. // If this is a Unicode combining character
  926. if firstScalar.properties.canonicalCombiningClass != .notReordered {
  927. // Determine if the last time we poked at a character is still valid
  928. if let last = lastBufferStorage {
  929. if last.buffer === buffers.active && last.cols == cols && last.rows == rows {
  930. // Fetch the old character, and attempt to combine it:
  931. let existingLine = buffer.lines [last.y]
  932. let lastx = last.x >= cols ? cols-1 : last.x
  933. var cd = existingLine [lastx]
  934. // Attemp the combination
  935. let newStr = String ([cd.getCharacter (), ch])
  936. // If the resulting string is 1 grapheme cluster, then it combined properly
  937. if newStr.count == 1 {
  938. if let newCh = newStr.first {
  939. cd.setValue(char: newCh, size: Int32 (cd.width))
  940. existingLine [lastx] = cd
  941. updateRange (last.y)
  942. continue
  943. }
  944. }
  945. }
  946. }
  947. }
  948. }
  949. // The accessibility stack might not need this
  950. //let screenReaderMode = options.screenReaderMode
  951. //if screenReaderMode {
  952. // emitChar (ch)
  953. //}
  954. let charData = CharData (attribute: curAttr, char: ch, size: Int8 (chWidth))
  955. insertCharacter (charData)
  956. }
  957. updateRange (buffer.y)
  958. readingBuffer.done ()
  959. }
  960. // Inserts the specified character with the computed width into the next cell, following
  961. // the rules for wrapping around, scrolling and overflow expected in the terminal.
  962. func insertCharacter (_ charData: CharData)
  963. {
  964. let buffer = self.buffer
  965. var chWidth = Int (charData.width)
  966. var bufferRow = buffer.lines [buffer.y + buffer.yBase]
  967. let right = marginMode ? buffer.marginRight : cols - 1
  968. // goto next line if ch would overflow
  969. // TODO: needs a global min terminal width of 2
  970. // FIXME: additionally ensure chWidth fits into a line
  971. // --> maybe forbid cols<xy at higher level as it would
  972. // introduce a bad runtime penalty here
  973. if buffer.x + chWidth - 1 > right {
  974. // autowrap - DECAWM
  975. // automatically wraps to the beginning of the next line
  976. if wraparound {
  977. buffer.x = marginMode ? buffer.marginLeft : 0
  978. if buffer.y >= buffer.scrollBottom {
  979. scroll (isWrapped: true)
  980. } else {
  981. // The line already exists (eg. the initial viewport), mark it as a
  982. // wrapped line
  983. buffer.y += 1
  984. buffer.lines [buffer.y].isWrapped = true
  985. }
  986. // row changed, get it again
  987. bufferRow = buffer.lines [buffer.y + buffer.yBase]
  988. } else {
  989. if (chWidth == 2) {
  990. // FIXME: check for xterm behavior
  991. // What to do here? We got a wide char that does not fit into last cell
  992. return
  993. }
  994. // FIXME: Do we have to set buffer.x to cols - 1, if not wrapping?
  995. buffer.x = right
  996. }
  997. }
  998. var empty = CharData.Null
  999. empty.attribute = curAttr
  1000. // insert mode: move characters to right
  1001. if insertMode {
  1002. // right shift cells according to the width
  1003. bufferRow.insertCells (pos: buffer.x, n: chWidth, rightMargin: marginMode ? buffer.marginRight : cols-1, fillData: empty)
  1004. // test last cell - since the last cell has only room for
  1005. // a halfwidth char any fullwidth shifted there is lost
  1006. // and will be set to eraseChar
  1007. let lastCell = bufferRow [cols - 1]
  1008. if lastCell.width == 2 {
  1009. bufferRow [cols - 1] = empty
  1010. }
  1011. }
  1012. // write current char to buffer and advance cursor
  1013. lastBufferStorage = (buffer, buffer.y + buffer.yBase, buffer.x, cols, rows)
  1014. if buffer.x >= cols {
  1015. buffer.x = cols-1
  1016. }
  1017. bufferRow [buffer.x] = charData
  1018. buffer.x += 1
  1019. // fullwidth char - also set next cell to placeholder stub and advance cursor
  1020. // for graphemes bigger than fullwidth we can simply loop to zero
  1021. // we already made sure above, that buffer.x + chWidth will not overflow right
  1022. if chWidth > 0 {
  1023. chWidth -= 1
  1024. while chWidth != 0 && buffer.x < buffer.cols {
  1025. bufferRow [buffer.x] = empty
  1026. buffer.x += 1
  1027. chWidth -= 1
  1028. }
  1029. }
  1030. }
  1031. func cmdLineFeed ()
  1032. {
  1033. cmdLineFeedBasic ()
  1034. }
  1035. func cmdLineFeedBasic ()
  1036. {
  1037. let buffer = self.buffer
  1038. let by = buffer.y
  1039. let canScroll = buffer.x >= buffer.marginLeft && buffer.x <= buffer.marginRight
  1040. if by == buffer.scrollBottom {
  1041. if canScroll {
  1042. scroll(isWrapped: false)
  1043. }
  1044. } else if by == rows - 1 {
  1045. } else {
  1046. buffer.y = by + 1
  1047. }
  1048. // If the end of the line is hit, prevent this action from wrapping around to the next line.
  1049. if buffer.x >= cols {
  1050. buffer.x -= 1
  1051. }
  1052. // This event is emitted whenever the terminal outputs a LF or NL.
  1053. emitLineFeed()
  1054. if lineFeedMode {
  1055. buffer.x = usingMargins() ? buffer.marginLeft : 0
  1056. }
  1057. }
  1058. //
  1059. // Backspace handler (Control-h)
  1060. //
  1061. func cmdBackspace ()
  1062. {
  1063. let buffer = self.buffer
  1064. restrictCursor(!reverseWraparound)
  1065. let left = marginMode ? buffer.marginLeft : 0
  1066. let right = marginMode ? buffer.marginRight : buffer.cols-1
  1067. if buffer.x > left {
  1068. buffer.x -= 1
  1069. } else if reverseWraparound {
  1070. if buffer.x <= left {
  1071. if buffer.y > buffer.scrollTop && buffer.y <= buffer.scrollBottom && (buffer.lines [buffer.y + buffer.yBase].isWrapped || marginMode) {
  1072. if !marginMode {
  1073. buffer.lines [buffer.y + buffer.yBase].isWrapped = false
  1074. }
  1075. buffer.y -= 1
  1076. buffer.x = right
  1077. // TODO: find actual last cell based on width used
  1078. } else if buffer.y == buffer.scrollTop {
  1079. buffer.x = right
  1080. buffer.y = buffer.scrollBottom
  1081. } else if buffer.y > 0 {
  1082. buffer.x = right
  1083. buffer.y -= 1
  1084. }
  1085. }
  1086. } else {
  1087. if buffer.x < left && buffer.x > 0 {
  1088. // This compensates for the scenario where backspace is supposed to move one step
  1089. // backwards if the "x" position is behind the left margin.
  1090. // Test BS_MovesLeftWhenLeftOfLeftMargin
  1091. buffer.x -= 1
  1092. } else if buffer.x > left {
  1093. // If we have not reached the limit, we can go back, otherwise stop at the margin
  1094. // Test BS_StopsAtLeftMargin
  1095. buffer.x -= 1
  1096. }
  1097. }
  1098. }
  1099. func cmdCarriageReturn ()
  1100. {
  1101. let buffer = self.buffer
  1102. if marginMode {
  1103. if buffer.x < buffer.marginLeft {
  1104. buffer.x = 0
  1105. } else {
  1106. buffer.x = buffer.marginLeft
  1107. }
  1108. } else {
  1109. buffer.x = 0
  1110. }
  1111. }
  1112. //
  1113. // Horizontal tab (control-i)
  1114. //
  1115. func cmdTab ()
  1116. {
  1117. buffer.x = buffer.nextTabStop ()
  1118. }
  1119. // SO
  1120. // ShiftOut (Control-N) Switch to alternate character set. This invokes the G1 character set
  1121. func cmdShiftOut ()
  1122. {
  1123. setgLevel (1)
  1124. }
  1125. // SI
  1126. // ShiftIn (Control-O) Switch to standard character set. This invokes the G0 character set
  1127. func cmdShiftIn ()
  1128. {
  1129. setgLevel(0)
  1130. }
  1131. // Operating System Commands (OSC)
  1132. func resetAllColors ()
  1133. {
  1134. ansiColors = defaultAnsiColors
  1135. tdel?.colorChanged (source: self, idx: nil)
  1136. }
  1137. func resetColor (_ number: Int)
  1138. {
  1139. if number > 255 {
  1140. return
  1141. }
  1142. ansiColors [number] = defaultAnsiColors [number]
  1143. tdel?.colorChanged(source: self, idx: number)
  1144. }
  1145. func oscResetColor (_ data: ArraySlice<UInt8>)
  1146. {
  1147. if data == [] {
  1148. resetAllColors()
  1149. } else {
  1150. if let param = String (bytes: data, encoding: .ascii) {
  1151. let colors = param.split(separator: ";")
  1152. for color in colors {
  1153. resetColor (Int (color) ?? 0)
  1154. }
  1155. }
  1156. }
  1157. }
  1158. // Implements OSC 7 ; URL which records the current working directory
  1159. func oscSetCurrentDirectory (_ data: ArraySlice<UInt8>)
  1160. {
  1161. if !(tdel?.isProcessTrusted(source: self) ?? false) {
  1162. return
  1163. }
  1164. var s = String (bytes:data, encoding: .utf8)
  1165. if s == nil {
  1166. s = String (bytes:data, encoding: .ascii)
  1167. }
  1168. if let txt = s {
  1169. hostCurrentDirectory = txt
  1170. tdel?.hostCurrentDirectoryUpdated (source: self)
  1171. }
  1172. }
  1173. // Implements OSC 6 ; URL which records the current document
  1174. func oscSetCurrentDocument (_ data: ArraySlice<UInt8>)
  1175. {
  1176. if !(tdel?.isProcessTrusted(source: self) ?? false) {
  1177. return
  1178. }
  1179. var s = String (bytes:data, encoding: .utf8)
  1180. if s == nil {
  1181. s = String (bytes:data, encoding: .ascii)
  1182. }
  1183. if let txt = s {
  1184. hostCurrentDocument = txt
  1185. tdel?.hostCurrentDocumentUpdated (source: self)
  1186. }
  1187. }
  1188. var hyperLinkTracking: (start: Position, payload: String)? = nil
  1189. func oscHyperlink (_ data: ArraySlice<UInt8>)
  1190. {
  1191. let buffer = self.buffer
  1192. if data.count == 1 && data [data.startIndex] == UInt8 (ascii: ";") {
  1193. // We only had the terminator, so we can close ";"
  1194. if let hlt = hyperLinkTracking {
  1195. let str = hlt.payload
  1196. if let urlToken = TinyAtom.lookup (value: str) {
  1197. //print ("Setting the text from \(hlt.start) to \(buffer.x) on line \(buffer.y+buffer.yBase) to \(str)")
  1198. // Between the time the flag was set, and now `y` might have changed negatively,
  1199. // in that case, we do not flag any sequence as a hyperlink
  1200. if hlt.start.row <= buffer.y+buffer.yBase {
  1201. for y in hlt.start.row...(buffer.y+buffer.yBase) {
  1202. let line = buffer.lines [y]
  1203. let startCol = y == hlt.start.row ? min (hlt.start.col, cols-1) : 0
  1204. let endCol = y == buffer.y ? min (buffer.x, cols-1) : (marginMode ? buffer.marginRight : cols-1)
  1205. if endCol > startCol {
  1206. for x in startCol...endCol {
  1207. var cd = line [x]
  1208. cd.setPayload(atom: urlToken)
  1209. line [x] = cd
  1210. }
  1211. }
  1212. }
  1213. }
  1214. }
  1215. }
  1216. hyperLinkTracking = nil
  1217. } else {
  1218. hyperLinkTracking = (start: Position(col: buffer.x, row: buffer.y+buffer.yBase), payload: String (bytes:data, encoding: .ascii) ?? "")
  1219. }
  1220. }
  1221. // Copy to clipboard with sequence on the form:
  1222. // ESC ] 52 ; c ; [base64 data] \a
  1223. // where c is for copy and the only thing supported.
  1224. func oscClipboard (_ data: ArraySlice<UInt8>) {
  1225. // we require data to start with c; followed by base64 content
  1226. guard data.count >= 2,
  1227. data[data.startIndex] == UInt8(ascii: "c"),
  1228. data[data.startIndex+1] == UInt8(ascii: ";") else {
  1229. return
  1230. }
  1231. let base64 = Data(data[(data.startIndex+2)...])
  1232. guard let content = Data(base64Encoded: base64) else {
  1233. return
  1234. }
  1235. tdel?.clipboardCopy(source: self, content: content)
  1236. }
  1237. // Notifications:
  1238. // ESC ] 777 ; notify ; [title] ; [body] \a
  1239. func oscNotification(_ data: ArraySlice<UInt8>) {
  1240. guard let text = String(bytes: data, encoding: .utf8) else {
  1241. return
  1242. }
  1243. let parts = text.components(separatedBy: ";")
  1244. guard parts.count >= 3,
  1245. parts[0] == "notify" else {
  1246. return
  1247. }
  1248. let title = parts[1]
  1249. let body = parts[2...].joined(separator: ";")
  1250. tdel?.notify(source: self, title: title, body: body)
  1251. }
  1252. // OSC 1337 is used by iTerm2 for imgcat and other things:
  1253. // https://iterm2.com/documentation-images.html
  1254. // ESC ] 1337 ; key = value ^G
  1255. //
  1256. // Options
  1257. // ESC ] 1337 ; File = [arguments] : base-64 encoded file contents ^G
  1258. //
  1259. func osciTerm2 (_ data: ArraySlice<UInt8>) {
  1260. // Parses the key-value pairs separated by ";"
  1261. func parseKeyValues (_ data: ArraySlice<UInt8>) -> [String:String] {
  1262. var kv: [String:String] = [:]
  1263. var current = data.startIndex
  1264. repeat {
  1265. let next = data [current..<data.endIndex].firstIndex(where: { b in b == UInt8 (ascii: ";")}) ?? data.endIndex
  1266. guard let equalIdx = data [current..<next].firstIndex(where: { b in b == UInt8 (ascii: "=")}) else {
  1267. break
  1268. }
  1269. guard let key = String (bytes: data[current..<equalIdx], encoding: .utf8) else {
  1270. break
  1271. }
  1272. guard let value = String (bytes: data[equalIdx+1..<next], encoding: .utf8) else {
  1273. break
  1274. }
  1275. kv [key] = value
  1276. current = next == data.endIndex ? next : next+1
  1277. } while current < data.endIndex
  1278. return kv
  1279. }
  1280. /// Parses the dimension specification ("auto", "N%", "Npx" or "N") and returns the enum value for it
  1281. /// puts some artificial limits, to prevent bloat or attacks
  1282. func parseDimension(_ kv: [String:String], key: String) -> ImageSizeRequest {
  1283. let artificialDimensionSizeLimit = 1024*4
  1284. let artificialColumnLimit = 200
  1285. guard let v = kv [key] else {
  1286. return .auto
  1287. }
  1288. if v == "auto" { return .auto }
  1289. if v.hasSuffix ("%") {
  1290. if let n = Int (v.dropLast(1)), n > 0, n <= 100 { return .percent (n) }
  1291. return .auto
  1292. }
  1293. if v.hasSuffix("px") {
  1294. if let n = Int (v.dropLast(2)), n > 0, n < artificialDimensionSizeLimit { return .pixels (n) }
  1295. return .auto
  1296. }
  1297. if let n = Int (v), n > 0, n < artificialColumnLimit { return .cells(n) }
  1298. return .auto
  1299. }
  1300. guard let equalIdx = data.firstIndex (where: { b in b == UInt8(ascii: "=") }) else {
  1301. return
  1302. }
  1303. guard let key = String(bytes: data[data.startIndex..<equalIdx], encoding: .utf8) else {
  1304. return
  1305. }
  1306. switch key {
  1307. case "File":
  1308. guard let colonIdx = data [equalIdx...].firstIndex(where: { b in b == UInt8 (ascii: ":")}) else {
  1309. return
  1310. }
  1311. let kv = parseKeyValues (data [equalIdx+1..<colonIdx])
  1312. // inline == 1 means to display the image inline, the option == 0 downloads the provided file
  1313. // into the file system, and I do not think it is a good idea to download data from untrusted
  1314. // sources like this and potentially override existing files. So let us just not bother
  1315. // supporting that
  1316. if kv["inline"] != "1" {
  1317. return
  1318. }
  1319. guard let imgData = Data(base64Encoded: Data(data [colonIdx+1..<data.endIndex])) else {
  1320. return
  1321. }
  1322. let width = parseDimension (kv, key: "width")
  1323. let height = parseDimension (kv, key: "height")
  1324. tdel?.createImage(source: self, data: imgData, width: width, height: height, preserveAspectRatio: (kv ["preserveAspectRatio"] ?? "1" ) == "1")
  1325. default:
  1326. break
  1327. }
  1328. tdel?.iTermContent(source: self, content: data)
  1329. }
  1330. // OSC 4
  1331. func oscChangeOrQueryColorIndex (_ data: ArraySlice<UInt8>)
  1332. {
  1333. var parsePos = data.startIndex
  1334. while parsePos <= data.endIndex {
  1335. guard let p = data [parsePos...].firstIndex(of: UInt8 (ascii: ";")) else {
  1336. return
  1337. }
  1338. let color = EscapeSequenceParser.parseInt(data [parsePos..<p])
  1339. guard color < 256 else {
  1340. return
  1341. }
  1342. // If the request is a query, reply with the current color definition
  1343. if p+1 < data.endIndex && data [p+1] == UInt8 (ascii: "?") {
  1344. sendResponse (cc.OSC, "4;\(color);\(ansiColors [color].formatAsXcolor())", cc.ST)
  1345. parsePos = p+2
  1346. if parsePos < data.endIndex && data [parsePos] == UInt8(ascii: ";"){
  1347. parsePos += 1
  1348. }
  1349. continue
  1350. }
  1351. //let str = String (bytes:data, encoding: .ascii) ?? ""
  1352. //print ("Parsing color definition \(str)")
  1353. parsePos = p + 1
  1354. let end = data [parsePos...].firstIndex(of: UInt8(ascii: ";")) ?? data.endIndex
  1355. if let newColor = Color.parseColor (data [parsePos..<end]) {
  1356. ansiColors [color] = newColor
  1357. tdel?.colorChanged (source: self, idx: color)
  1358. }
  1359. parsePos = end+1
  1360. }
  1361. //log ("Attempt to set the text Foreground color \(str)")
  1362. }
  1363. func oscSetTextForeground (_ data: ArraySlice<UInt8>)
  1364. {
  1365. if let foreground = Color.parseColor(data) {
  1366. foregroundColor = foreground
  1367. tdel?.setForegroundColor(source: self, color: foreground)
  1368. }
  1369. }
  1370. func oscSetTextBackground (_ data: ArraySlice<UInt8>)
  1371. {
  1372. if let background = Color.parseColor(data) {
  1373. backgroundColor = background
  1374. tdel?.setBackgroundColor(source: self, color: background)
  1375. }
  1376. }
  1377. //
  1378. // ESC E
  1379. // C1.NEL
  1380. // DEC mnemonic: NEL (https://vt100.net/docs/vt510-rm/NEL)
  1381. // Moves cursor to first position on next line.
  1382. //
  1383. func cmdNextLine ()
  1384. {
  1385. buffer.x = usingMargins () ? buffer.marginLeft : 0
  1386. cmdIndex ()
  1387. }
  1388. /**
  1389. * ESC H
  1390. * C1.HTS
  1391. * DEC mnemonic: HTS (https://vt100.net/docs/vt510-rm/HTS.html)
  1392. * Sets a horizontal tab stop at the column position indicated by
  1393. * the value of the active column when the terminal receives an HTS.
  1394. *
  1395. * @vt: #Y C1 HTS "Horizontal Tabulation Set" "\x88" "Places a tab stop at the current cursor position."
  1396. * @vt: #Y ESC HTS "Horizontal Tabulation Set" "ESC H" "Places a tab stop at the current cursor position."
  1397. */
  1398. func cmdTabSet ()
  1399. {
  1400. buffer.tabSet (pos: buffer.x)
  1401. }
  1402. //
  1403. // CSI Ps @
  1404. // Insert Ps (Blank) Character(s) (default = 1) (ICH).
  1405. //
  1406. func cmdInsertChars (_ pars: [Int], _ collect: cstring)
  1407. {
  1408. // Do nothing if we are outside the margin
  1409. if marginMode && (buffer.x < buffer.marginLeft || buffer.x > buffer.marginRight) {
  1410. return
  1411. }
  1412. let cd = CharData (attribute: eraseAttr ())
  1413. let buffer = self.buffer
  1414. buffer.lines [buffer.y + buffer.yBase].insertCells (pos: buffer.x, n: pars.count > 0 ? max (pars [0], 1) : 1, rightMargin: marginMode ? buffer.marginRight : cols-1, fillData: cd)
  1415. updateRange (buffer.y)
  1416. }
  1417. //
  1418. // CSI Ps A
  1419. // Cursor Up Ps Times (default = 1) (CUU).
  1420. //
  1421. func cmdCursorUp (_ pars: [Int], _ collect: cstring)
  1422. {
  1423. let param = max (pars.count > 0 ? pars [0] : 1, 1)
  1424. let buffer = self.buffer
  1425. var top = buffer.scrollTop
  1426. if buffer.y < top {
  1427. top = 0
  1428. }
  1429. if (buffer.y - param < top) {
  1430. buffer.y = top
  1431. } else {
  1432. buffer.y -= param
  1433. }
  1434. }
  1435. //
  1436. // CSI Ps B
  1437. // Cursor Down Ps Times (default = 1) (CUD).
  1438. //
  1439. func cmdCursorDown (_ pars: [Int], _ collect: cstring)
  1440. {
  1441. let buffer = self.buffer
  1442. let param = max (pars.count > 0 ? pars [0] : 1, 1)
  1443. var bottom = buffer.scrollBottom
  1444. // When the cursor starts below the scroll region, CUD moves it down to the
  1445. // bottom of the screen.
  1446. if buffer.y > bottom {
  1447. bottom = buffer.rows-1
  1448. }
  1449. let newY = buffer.y + param
  1450. if newY >= bottom {
  1451. buffer.y = bottom
  1452. } else {
  1453. buffer.y = newY
  1454. }
  1455. // If the end of the line is hit, prevent this action from wrapping around to the next line.
  1456. if buffer.x >= cols {
  1457. buffer.x -= 1
  1458. }
  1459. }
  1460. //
  1461. // CSI Ps B
  1462. // Cursor Forward Ps Times (default = 1) (CUF).
  1463. //
  1464. func cmdCursorForward (_ pars: [Int], _ collect: cstring)
  1465. {
  1466. cursorForward(count: pars.count > 0 ? pars [0] : 1)
  1467. }
  1468. func cursorForward (count: Int)
  1469. {
  1470. var right = marginMode ? buffer.marginRight : cols-1
  1471. // When the cursor starts after the right margin, CUF moves to the full width
  1472. if buffer.x > right {
  1473. right = buffer.cols - 1
  1474. }
  1475. buffer.x += (max (count, 1))
  1476. if buffer.x > right {
  1477. buffer.x = right
  1478. }
  1479. }
  1480. //
  1481. // CSI Ps D
  1482. // Cursor Backward Ps Times (default = 1) (CUB).
  1483. //
  1484. func cmdCursorBackward (_ pars: [Int], _ collect: cstring)
  1485. {
  1486. cursorBackward(count: pars.count > 0 ? pars [0] : 1)
  1487. }
  1488. func cursorBackward (count: Int)
  1489. {
  1490. let buffer = self.buffer
  1491. // What is our left margin - depending on the settings.
  1492. var left = marginMode ? buffer.marginLeft : 0
  1493. // If the cursor is positioned before the margin, we can go backwards to the first column
  1494. if buffer.x < left {
  1495. left = 0
  1496. }
  1497. let newX = buffer.x - max (1, count)
  1498. if newX < left {
  1499. buffer.x = left
  1500. } else {
  1501. buffer.x = newX
  1502. }
  1503. }
  1504. //
  1505. // CSI Ps I
  1506. // Cursor Forward Tabulation Ps tab stops (default = 1) (CHT).
  1507. //
  1508. func cmdCursorForwardTab (_ pars: [Int], _ collect: cstring)
  1509. {
  1510. let param = min (cols-1, max (pars.count > 0 ? pars [0] : 1, 1))
  1511. for _ in 0..<param {
  1512. buffer.x = buffer.nextTabStop ()
  1513. }
  1514. }
  1515. /**
  1516. * Restrict cursor to viewport size / scroll margin (origin mode)
  1517. * - Parameter limitCols: by default it is true, but the reverseWraparound mechanism in Backspace needs `x` to go beyond.
  1518. */
  1519. func restrictCursor(_ limitCols: Bool = true)
  1520. {
  1521. buffer.x = min (cols - (limitCols ? 1 : 0), max (0, buffer.x))
  1522. buffer.y = originMode
  1523. ? min (buffer.scrollBottom, max (buffer.scrollTop, buffer.y))
  1524. : min (rows - 1, max (0, buffer.y))
  1525. updateRange(buffer.y)
  1526. }
  1527. //
  1528. // CSI Ps ; Ps H
  1529. // Cursor Position [row;column] (default = [1,1]) (CUP).
  1530. //
  1531. func cmdCursorPosition (_ pars: [Int], _ collect: cstring)
  1532. {
  1533. setCursor (col: pars.count >= 2 ? (max (1, pars [1])-1) : 0, row: pars.count >= 1 ? (max (1, pars [0]) - 1) : 0)
  1534. }
  1535. func setCursor (col: Int, row: Int)
  1536. {
  1537. updateRange(buffer.y)
  1538. if originMode {
  1539. buffer.x = col + (usingMargins () ? buffer.marginLeft : 0)
  1540. buffer.y = buffer.scrollTop + row
  1541. } else {
  1542. buffer.x = col
  1543. buffer.y = row
  1544. }
  1545. restrictCursor ()
  1546. }
  1547. //
  1548. // CSI Ps E
  1549. // Cursor Next Line Ps Times (default = 1) (CNL).
  1550. // same as CSI Ps B?
  1551. //
  1552. func cmdCursorNextLine (_ pars: [Int], _ collect: cstring)
  1553. {
  1554. cmdCursorDown(pars, collect)
  1555. buffer.x = buffer.marginLeft
  1556. //return
  1557. //let buffer = self.buffer
  1558. //let param = max (pars.count > 0 ? pars [0] : 1, 1)
  1559. //
  1560. //var bottom = buffer.scrollBottom
  1561. //// When the cursor starts below the scroll region, CUD moves it down to the
  1562. //// bottom of the screen.
  1563. //if buffer.y > bottom {
  1564. // bottom = buffer.rows-1
  1565. //}
  1566. //let newY = buffer.y + param
  1567. //
  1568. //if newY >= bottom {
  1569. // buffer.y = bottom
  1570. //} else {
  1571. // buffer.y = newY
  1572. //}
  1573. //// If the end of the line is hit, prevent this action from wrapping around to the next line.
  1574. //if buffer.x >= cols {
  1575. // buffer.x -= 1
  1576. //}
  1577. //buffer.x = buffer.marginLeft
  1578. }
  1579. //
  1580. // CSI Ps F
  1581. // Cursor Preceding Line Ps Times (default = 1) (CPL).
  1582. // reuse CSI Ps A ?
  1583. //
  1584. func cmdCursorPrecedingLine (_ pars: [Int], _ collect: cstring)
  1585. {
  1586. cmdCursorUp(pars, collect)
  1587. buffer.x = buffer.marginLeft
  1588. //let param = max (pars.count > 0 ? pars [0] : 1, 1)
  1589. //let buffer = self.buffer
  1590. //var top = buffer.scrollTop
  1591. //
  1592. //if buffer.y < top {
  1593. // top = 0
  1594. //}
  1595. //if (buffer.y - param < top) {
  1596. // buffer.y = top
  1597. //} else {
  1598. // buffer.y -= param
  1599. //}
  1600. //buffer.x = buffer.marginLeft
  1601. }
  1602. //
  1603. // CSI Ps G
  1604. // Cursor Character Absolute [column] (default = [row,1]) (CHA).
  1605. //
  1606. func cmdCursorCharAbsolute (_ pars: [Int], _ collect: cstring)
  1607. {
  1608. let buffer = self.buffer
  1609. let param = max (pars.count > 0 ? pars [0] : 1, 1)
  1610. buffer.x = (usingMargins() ? buffer.marginLeft : 0) + min (param - 1, cols - 1)
  1611. }
  1612. //
  1613. // CSI Ps K Erase in Line (EL).
  1614. // Ps = 0 -> Erase to Right (default).
  1615. // Ps = 1 -> Erase to Left.
  1616. // Ps = 2 -> Erase All.
  1617. // CSI ? Ps K
  1618. // Erase in Line (DECSEL).
  1619. // Ps = 0 -> Selective Erase to Right (default).
  1620. // Ps = 1 -> Selective Erase to Left.
  1621. // Ps = 2 -> Selective Erase All.
  1622. //
  1623. func cmdEraseInLine (_ pars: [Int], _ collect: cstring)
  1624. {
  1625. let p = pars.count == 0 ? 0 : pars [0]
  1626. switch p {
  1627. case 0:
  1628. eraseInBufferLine (y: buffer.y, start: buffer.x, end: cols)
  1629. case 1:
  1630. eraseInBufferLine (y: buffer.y, start: 0, end: buffer.x + 1)
  1631. case 2:
  1632. eraseInBufferLine (y: buffer.y, start: 0, end: cols)
  1633. default:
  1634. break
  1635. }
  1636. updateRange (buffer.y)
  1637. }
  1638. //
  1639. // CSI Ps J Erase in Display (ED).
  1640. // Ps = 0 -> Erase Below (default).
  1641. // Ps = 1 -> Erase Above.
  1642. // Ps = 2 -> Erase All.
  1643. // Ps = 3 -> Erase Saved Lines (xterm).
  1644. // CSI ? Ps J
  1645. // Erase in Display (DECSED).
  1646. // Ps = 0 -> Selective Erase Below (default).
  1647. // Ps = 1 -> Selective Erase Above.
  1648. // Ps = 2 -> Selective Erase All.
  1649. //
  1650. func cmdEraseInDisplay (_ pars: [Int], _ collect: cstring)
  1651. {
  1652. let p = pars.count == 0 ? 0 : pars [0]
  1653. var j: Int
  1654. switch p {
  1655. case 0:
  1656. j = buffer.y
  1657. updateRange (j)
  1658. eraseInBufferLine (y: j, start: buffer.x, end: cols, clearWrap: buffer.x == 0)
  1659. j += 1
  1660. while j < rows {
  1661. resetBufferLine (y: j)
  1662. j += 1
  1663. }
  1664. updateRange (j - 1)
  1665. case 1:
  1666. j = buffer.y
  1667. updateRange (j)
  1668. // Deleted front part of line and everything before. This line will no longer be wrapped.
  1669. eraseInBufferLine (y: j, start: 0, end: buffer.x + 1, clearWrap: true)
  1670. if buffer.x + 1 >= cols {
  1671. // Deleted entire previous line. This next line can no longer be wrapped.
  1672. buffer.lines [j + 1].isWrapped = false
  1673. }
  1674. while (j != 0) {
  1675. j -= 1
  1676. resetBufferLine (y: j)
  1677. }
  1678. updateRange (0)
  1679. case 2:
  1680. j = rows
  1681. updateRange (j - 1)
  1682. while (j != 0) {
  1683. j -= 1
  1684. resetBufferLine (y: j)
  1685. }
  1686. updateRange (0)
  1687. case 3:
  1688. // Clear scrollback (everything not in viewport)
  1689. let scrollBackSize = buffer.lines.count - rows
  1690. if scrollBackSize > 0 {
  1691. buffer.lines.trimStart (count: scrollBackSize)
  1692. buffer.linesTop = 0
  1693. buffer.yBase = max (buffer.yBase - scrollBackSize, 0)
  1694. buffer.yDisp = max (buffer.yDisp - scrollBackSize, 0)
  1695. }
  1696. break;
  1697. default:
  1698. break
  1699. }
  1700. }
  1701. //
  1702. // Helper method to erase cells in a terminal row.
  1703. // The cell gets replaced with the eraseChar of the terminal.
  1704. // - Parameter y: row index
  1705. // - Parameter start: first cell index to be erased
  1706. // - Parameter end: end - 1 is last erased cell
  1707. //
  1708. func eraseInBufferLine (y: Int, start: Int, end: Int, clearWrap: Bool = false)
  1709. {
  1710. let line = buffer.lines [buffer.yBase + y]
  1711. line.images = nil
  1712. let cd = CharData (attribute: eraseAttr ())
  1713. line.replaceCells (start: start, end: end, fillData: cd)
  1714. if clearWrap {
  1715. line.isWrapped = false
  1716. }
  1717. }
  1718. //
  1719. // CSI Ps L
  1720. // Insert Ps Line(s) (default = 1) (IL).
  1721. //
  1722. func cmdInsertLines (_ pars: [Int], _ collect: cstring)
  1723. {
  1724. let buffer = self.buffer
  1725. if buffer.y < buffer.scrollTop || buffer.y > buffer.scrollBottom {
  1726. return
  1727. }
  1728. // to prevent a Denial of Service
  1729. let maxLines = buffer._lines.maxLength * 2
  1730. var p = min (maxLines, max (pars.count == 0 ? 1 : pars [0], 1))
  1731. let row = buffer.y + buffer.yBase
  1732. let scrollBottomRowsOffset = rows - 1 - buffer.scrollBottom
  1733. let scrollBottomAbsolute = rows - 1 + buffer.yBase - scrollBottomRowsOffset + 1
  1734. let ea = eraseAttr ()
  1735. if marginMode {
  1736. if buffer.x >= buffer.marginLeft && buffer.x <= buffer.marginRight {
  1737. let columnCount = buffer.marginRight-buffer.marginLeft+1
  1738. let rowCount = buffer.scrollBottom-buffer.scrollTop
  1739. for _ in 0..<p {
  1740. for i in (0..<rowCount).reversed() {
  1741. let src = buffer.lines [row+i]
  1742. let dst = buffer.lines [row+i+1]
  1743. dst.copyFrom(src, srcCol: buffer.marginLeft, dstCol: buffer.marginLeft, len: columnCount)
  1744. }
  1745. let last = buffer.lines [row]
  1746. last.fill (with: CharData (attribute: ea), atCol: buffer.marginLeft, len: columnCount)
  1747. }
  1748. }
  1749. } else {
  1750. for _ in 0..<p {
  1751. p -= 1
  1752. // test: echo -e '\e[44m\e[1L\e[0m'
  1753. // blankLine(true) - xterm/linux behavior
  1754. buffer.lines.splice (start: scrollBottomAbsolute - 1, deleteCount: 1, items: [],
  1755. change: { line in updateRange (line) })
  1756. let newLine = buffer.getBlankLine (attribute: ea)
  1757. buffer.lines.splice (start: row, deleteCount: 0, items: [newLine], change: { line in updateRange (line) })
  1758. }
  1759. }
  1760. // this.maxRange();
  1761. updateRange (startLine: buffer.y, endLine: buffer.scrollBottom)
  1762. }
  1763. //
  1764. // ESC ( C
  1765. // Designate G0 Character Set, VT100, ISO 2022.
  1766. // ESC ) C
  1767. // Designate G1 Character Set (ISO 2022, VT100).
  1768. // ESC * C
  1769. // Designate G2 Character Set (ISO 2022, VT220).
  1770. // ESC + C
  1771. // Designate G3 Character Set (ISO 2022, VT220).
  1772. // ESC - C
  1773. // Designate G1 Character Set (VT300).
  1774. // ESC . C
  1775. // Designate G2 Character Set (VT300).
  1776. // ESC / C
  1777. // Designate G3 Character Set (VT300). C = A -> ISO Latin-1 Supplemental. - Supported?
  1778. //
  1779. func selectCharset (_ p: ArraySlice<UInt8>)
  1780. {
  1781. if p.count == 2 {
  1782. // print ("Settin charset to \(p[1])")
  1783. }
  1784. if (p.count != 2) {
  1785. cmdSelectDefaultCharset ()
  1786. return
  1787. }
  1788. var ch: UInt8
  1789. var charset: [UInt8:String]?
  1790. if CharSets.all.keys.contains(p [1]){
  1791. charset = CharSets.all [p [1]]!
  1792. } else {
  1793. charset = nil
  1794. }
  1795. switch p [0] {
  1796. case UInt8 (ascii: "("):
  1797. ch = 0
  1798. case UInt8 (ascii: ")"):
  1799. ch = 1
  1800. case UInt8 (ascii: "-"):
  1801. ch = 1
  1802. case UInt8 (ascii: "*"):
  1803. ch = 2
  1804. case UInt8 (ascii: "."):
  1805. ch = 2
  1806. case UInt8 (ascii: "+"):
  1807. ch = 3
  1808. case UInt8 (ascii: "/"):
  1809. ch = 3
  1810. default:
  1811. return;
  1812. }
  1813. setgCharset (ch, charset: charset)
  1814. }
  1815. //
  1816. // ESC # NUMBER
  1817. //
  1818. func cmdDoubleWidthSingleHeight ()
  1819. {
  1820. abort ()
  1821. }
  1822. //
  1823. // dhtop
  1824. //
  1825. func cmdSetDoubleHeightTop ()
  1826. {
  1827. abort ()
  1828. }
  1829. // dhbot
  1830. func cmdSetDoubleHeightBottom ()
  1831. {
  1832. abort ()
  1833. }
  1834. //
  1835. // swsh
  1836. //
  1837. func cmdSingleWidthSingleHeight ()
  1838. {
  1839. abort ()
  1840. }
  1841. // ESC # 8
  1842. func cmdScreenAlignmentPattern ()
  1843. {
  1844. let cell = CharData(attribute: curAttr.justColor(), char: "E")
  1845. setCursor (col: 0, row: 0)
  1846. for yOffset in 0..<rows {
  1847. let rowN = buffer.y + buffer.yBase + yOffset
  1848. buffer.lines [rowN].fill(with: cell)
  1849. buffer.lines [rowN].isWrapped = false
  1850. }
  1851. updateFullScreen()
  1852. setCursor(col: 0, row: 0)
  1853. }
  1854. func cmdRestoreCursor (_ pars: [Int], _ collect: cstring)
  1855. {
  1856. buffer.x = buffer.savedX
  1857. buffer.y = buffer.savedY
  1858. curAttr = buffer.savedAttr
  1859. charset = buffer.savedCharset
  1860. originMode = buffer.savedOriginMode
  1861. marginMode = buffer.savedMarginMode
  1862. wraparound = buffer.savedWraparound
  1863. reverseWraparound = buffer.savedReverseWraparound
  1864. }
  1865. //
  1866. // Validates optional arguments for top, left, bottom, right sent by various
  1867. // escape sequences and returns validated top, left, bottom, right in our 0-based
  1868. // internal coordinates
  1869. //
  1870. func getRectangleFromRequest (_ pars: ArraySlice<Int>) -> (top: Int, left: Int, bottom: Int, right: Int)?
  1871. {
  1872. let buffer = self.buffer
  1873. let b = pars.startIndex
  1874. var top = max (1, pars.count > 0 ? pars [b] : 1)
  1875. var left = max (pars.count > 1 ? pars [b+1] : 1, 1)
  1876. var bottom = pars.count > 2 ? pars [b+2] : -1
  1877. var right = pars.count > 3 ? pars [b+3] : -1
  1878. if bottom < 0 {
  1879. bottom = rows
  1880. }
  1881. if right < 0 {
  1882. right = cols
  1883. }
  1884. if right > cols {
  1885. right = cols
  1886. }
  1887. if bottom > rows {
  1888. bottom = rows
  1889. }
  1890. if originMode {
  1891. top += buffer.scrollTop
  1892. bottom += buffer.scrollTop
  1893. left += buffer.marginLeft
  1894. right += buffer.marginLeft
  1895. }
  1896. if top > bottom || left > right {
  1897. return nil
  1898. }
  1899. //top = min (top, bottom)
  1900. //left = min (left, right)
  1901. let rowBound = rows-1
  1902. let colBound = cols-1
  1903. return (min (rowBound, top-1), min (colBound, left-1), min (rowBound, bottom-1), min (colBound, right-1))
  1904. }
  1905. //
  1906. // Copy Rectangular Area (DECCRA), VT400 and up.
  1907. // CSI Pts ; Pls ; Pbs ; Prs ; Pps ; Ptd ; Pld ; Ppd $ v
  1908. // Pts ; Pls ; Pbs ; Prs denotes the source rectangle.
  1909. // Pps denotes the source page.
  1910. // Ptd ; Pld denotes the target location.
  1911. // Ppd denotes the target page.
  1912. func csiCopyRectangularArea (_ ipars: [Int], _ collect: cstring)
  1913. {
  1914. if collect == [36] {
  1915. var pars: [Int] = []
  1916. pars.append (ipars.count > 1 && ipars [0] != 0 ? ipars [0] : 1) // Pts default 1
  1917. pars.append (ipars.count > 2 && ipars [1] != 0 ? ipars [1]: 1) // Pls default 1
  1918. pars.append (ipars.count > 3 && ipars [2] != 0 ? ipars [2]: rows-1) // Pbs default to last line of page
  1919. pars.append (ipars.count > 4 && ipars [3] != 0 ? ipars [3]: cols-1) // Prs defaults to last column
  1920. pars.append (ipars.count > 5 && ipars [4] != 0 ? ipars [4]: 1) // Pps page source = 1
  1921. pars.append (ipars.count > 6 && ipars [5] != 0 ? ipars [5]: 1) // Ptd default is 1
  1922. pars.append (ipars.count > 7 && ipars [6] != 0 ? ipars [6]: 1) // Pld default is 1
  1923. pars.append (ipars.count > 8 && ipars [7] != 0 ? ipars [7]: 1) // Ppd default is 1
  1924. // We only support copying on the same page, and the page being 1
  1925. if pars [4] == pars [7] && pars [4] == 1 {
  1926. if let (top, left, bottom, right) = getRectangleFromRequest(pars [0...3]) {
  1927. let rowTarget = min (rows-1, pars [5]-1)
  1928. let colTarget = min (cols-1, pars [6]-1)
  1929. // Block size
  1930. let columns = right-left+1
  1931. let cright = min (cols-1, left + min (columns, cols-colTarget))
  1932. var lines: [[CharData]] = []
  1933. for row in top...bottom {
  1934. let line = buffer.lines [row+buffer.yBase]
  1935. var lineCopy: [CharData] = []
  1936. for col in left...cright {
  1937. lineCopy.append(line [col])
  1938. }
  1939. lines.append(lineCopy)
  1940. }
  1941. for row in 0...(bottom-top) {
  1942. if row+rowTarget >= buffer.rows {
  1943. break
  1944. }
  1945. let line = buffer.lines [row+rowTarget+buffer.yBase]
  1946. let lr = lines [row]
  1947. for col in 0..<(cright-left) {
  1948. if col >= buffer.cols {
  1949. break
  1950. }
  1951. line [colTarget+col] = lr [col]
  1952. }
  1953. }
  1954. }
  1955. }
  1956. }
  1957. }
  1958. // CSI Ps x Request Terminal Parameters (DECREQTPARM).
  1959. // CSI Ps * x Select Attribute Change Extent (DECSACE), VT420 and up.
  1960. // CSI Pc ; Pt ; Pl ; Pb ; Pr $ x Fill Rectangular Area (DECFRA), VT420 and up.
  1961. func csiX (_ pars: [Int], _ collect: cstring)
  1962. {
  1963. if collect == [UInt8 (ascii: "$")] {
  1964. // DECFRA
  1965. if let (top, left, bottom, right) = getRectangleFromRequest(pars [1...]) {
  1966. for row in top...bottom {
  1967. let line = buffer.lines [row+buffer.yBase]
  1968. for col in left...right {
  1969. line [col] = CharData(attribute: curAttr, char: Character (UnicodeScalar (pars [0]) ?? " "))
  1970. }
  1971. }
  1972. }
  1973. } else {
  1974. log ("Not implemented CSI x with collect: collect=\(collect) and pars=\(pars)")
  1975. }
  1976. }
  1977. //
  1978. // CSI # } Pop video attributes from stack (XTPOPSGR), xterm. Popping
  1979. // restores the video-attributes which were saved using XTPUSHSGR
  1980. // to their previous state.
  1981. //
  1982. // CSI Pm ' }
  1983. // Insert Ps Column(s) (default = 1) (DECIC), VT420 and up.
  1984. //
  1985. func csiCloseBrace (_ pars: [Int], _ collect: cstring)
  1986. {
  1987. if collect == [39 /* ' */] {
  1988. // DECIC - Insert Column
  1989. let n = pars.count > 0 ? max (pars [0],1) : 1
  1990. let buffer = self.buffer
  1991. if marginMode && buffer.x < buffer.marginLeft || buffer.x > buffer.marginRight {
  1992. return
  1993. }
  1994. for row in buffer.scrollTop...buffer.scrollBottom {
  1995. let line = buffer.lines [row+buffer.yBase]
  1996. line.insertCells(pos: buffer.x, n: n, rightMargin: marginMode ? buffer.marginRight : cols-1, fillData: buffer.getNullCell())
  1997. line.isWrapped = false
  1998. }
  1999. return
  2000. } else {
  2001. log ("CSI # } not implemented- XTPOPSGR with \(pars)")
  2002. }
  2003. }
  2004. // Required by the test suite
  2005. // CSI Pi ; Pg ; Pt ; Pl ; Pb ; Pr * y
  2006. // Request Checksum of Rectangular Area (DECRQCRA), VT420 and up.
  2007. // Response is
  2008. // DCS Pi ! ~ x x x x ST
  2009. // Pi is the request id.
  2010. // Pg is the page number.
  2011. // Pt ; Pl ; Pb ; Pr denotes the rectangle.
  2012. // The x's are hexadecimal digits 0-9 and A-F.
  2013. func cmdDECRQCRA (_ pars: [Int], _ collect: cstring)
  2014. {
  2015. var checksum: UInt32 = 0
  2016. let rid = pars.count > 0 ? pars [0] : 1
  2017. let _ = pars.count > 1 ? pars [1] : 0
  2018. var result = "0000"
  2019. if (tdel?.isProcessTrusted(source: self) ?? false) && pars.count > 2 {
  2020. if let (top, left, bottom, right) = getRectangleFromRequest(pars [2...]) {
  2021. for row in top...bottom {
  2022. let line = buffer.lines [row+buffer.yBase]
  2023. for col in left...right {
  2024. let cd = line [col]
  2025. let ch = cd.code == 0 ? " " : cd.getCharacter()
  2026. for scalar in ch.unicodeScalars {
  2027. checksum += scalar.value
  2028. }
  2029. }
  2030. }
  2031. }
  2032. result = String(format: "%04x", checksum)
  2033. }
  2034. sendResponse (cc.DCS, "\(rid)!~\(result)", cc.ST)
  2035. }
  2036. // Dispatcher for CSI .* z commands
  2037. func csiZ (_ pars: [Int], _ collect: cstring)
  2038. {
  2039. switch collect {
  2040. case [UInt8 (ascii: "$")]:
  2041. cmdDECERA (pars)
  2042. case [UInt8 (ascii: "'")]:
  2043. // Enable Locator Reporting (DECELR).
  2044. // Valid values for the first parameter:
  2045. // Ps = 0 ⇒ Locator disabled (default).
  2046. // Ps = 1 ⇒ Locator enabled.
  2047. // Ps = 2 ⇒ Locator enabled for one report, then disabled.
  2048. // The second parameter specifies the coordinate unit for locator
  2049. // reports.
  2050. // Valid values for the second parameter:
  2051. // Pu = 0 or omitted ⇒ default to character cells.
  2052. // Pu = 1 ⇐ device physical pixels.
  2053. // Pu = 2 ⇐ character cells.
  2054. print ("TODO: Enable Locator Reporting (DECELR)")
  2055. default:
  2056. break
  2057. }
  2058. }
  2059. // DECERA - Erase Rectangular Area
  2060. // CSI Pt ; Pl ; Pb ; Pr ; $ z
  2061. func cmdDECERA (_ pars: [Int])
  2062. {
  2063. if let (top, left, bottom, right) = getRectangleFromRequest(pars [0...]) {
  2064. for row in top...bottom {
  2065. let line = buffer.lines [row+buffer.yBase]
  2066. for col in left...right {
  2067. line [col] = CharData(attribute: curAttr, char: " ", size: 1)
  2068. }
  2069. }
  2070. }
  2071. }
  2072. // Dispatches to DECSERA or XTPUSHSGR
  2073. func csiOpenBrace (_ pars: [Int], _ collect: cstring)
  2074. {
  2075. if collect == [UInt8 (ascii: "$")] {
  2076. cmdSelectiveEraseRectangularArea (pars)
  2077. } else {
  2078. log ("CSI # { not implemented - XTPUSHSGR with \(pars)")
  2079. }
  2080. }
  2081. // Push video attributes onto stack (XTPUSHSGR), xterm.
  2082. func cmdPushSg (_ pars: [Int])
  2083. {
  2084. }
  2085. // DECSERA - Selective Erase Rectangular Area
  2086. // CSI Pt ; Pl ; Pb ; Pr ; $ {
  2087. func cmdSelectiveEraseRectangularArea (_ pars: [Int])
  2088. {
  2089. if let (top, left, bottom, right) = getRectangleFromRequest(pars [0...]) {
  2090. for row in top...bottom {
  2091. let line = buffer.lines [row+buffer.yBase]
  2092. for col in left...right {
  2093. var cd = line [col]
  2094. cd.setValue(char: " ", size: 1)
  2095. line [col] = cd
  2096. }
  2097. }
  2098. }
  2099. }
  2100. /**
  2101. * Commands send to the `windowCommand` delegate for the front-end to implement capabilities
  2102. * on behalf of the client. The expected return strings in some of these enumeration values is documented
  2103. * below. Returns are only expected for the enum values that start with the prefix `report`
  2104. */
  2105. public enum WindowManipulationCommand {
  2106. /// Raised when the backend should deiconify a window, no return expected
  2107. case deiconifyWindow
  2108. /// Raised when the backend should iconify a window, no return expected
  2109. case iconifyWindow
  2110. /// Raised when the client would like the window to be moved to the x,y position int he screen, not return expected
  2111. case moveWindowTo(x: Int, y: Int)
  2112. /// Raised when the client would like the window to be resized to the specified widht and heigh in pixels, not return expected
  2113. case resizeWindowTo(width: Int, height: Int)
  2114. /// Raised to bring the terminal to the front
  2115. case bringToFront
  2116. /// Send the terminal to the back if possible
  2117. case sendToBack
  2118. /// Trigger a terminal refresh
  2119. case refreshWindow
  2120. /// Request that the size of the terminal be changed to the specified cols and rows
  2121. case resizeTo(cols: Int, rows: Int)
  2122. case restoreMaximizedWindow
  2123. /// Attempt to maximize the window
  2124. case maximizeWindow
  2125. /// Attempt to maximize the window vertically
  2126. case maximizeWindowVertically
  2127. /// Attempt to maximize the window horizontally
  2128. case maximizeWindowHorizontally
  2129. case undoFullScreen
  2130. case switchToFullScreen
  2131. case toggleFullScreen
  2132. case reportTerminalState
  2133. case reportTerminalPosition
  2134. case reportTextAreaPosition
  2135. case reporttextAreaPixelDimension
  2136. case reportSizeOfScreenInPixels
  2137. case reportCellSizeInPixels
  2138. case reportTextAreaCharacters
  2139. case reportScreenSizeCharacters
  2140. case reportIconLabel
  2141. case reportWindowTitle
  2142. case resizeTo (lines: Int)
  2143. }
  2144. // Dispatches to
  2145. func csit (_ pars: [Int], _ collect: cstring)
  2146. {
  2147. switch collect {
  2148. case []:
  2149. cmdWindowOptions(pars)
  2150. case [UInt8 (ascii: ">")]:
  2151. cmdXtermTitleModeSet(pars)
  2152. default:
  2153. log ("Unhandled csiT \(collect)")
  2154. }
  2155. }
  2156. func cmdXtermTitleModeSet (_ pars: [Int])
  2157. {
  2158. // Use the windowTextEncoding type
  2159. for par in pars {
  2160. switch par {
  2161. case 0:
  2162. // Set window/icon labels using hexadecimal.
  2163. xtermTitleSetHex = true
  2164. break
  2165. case 1:
  2166. // Query window/icon labels using hexadecimal.
  2167. xtermTitleQueryHex = true
  2168. break
  2169. case 2:
  2170. // Set window/icon labels using UTF-8.
  2171. xtermTitleSetUtf = true
  2172. break
  2173. case 3:
  2174. // Query window/icon labels using UTF-8.
  2175. xtermTitleQueryUtf = true
  2176. break
  2177. default:
  2178. break
  2179. }
  2180. }
  2181. }
  2182. func cmdXtermTitleModeReset (_ pars: [Int])
  2183. {
  2184. // Use the windowTextEncoding type
  2185. for par in pars {
  2186. switch par {
  2187. case 0:
  2188. // Do not set window/icon labels using hexadecimal.
  2189. xtermTitleSetHex = false
  2190. break
  2191. case 1:
  2192. // Do not query window/icon labels using hexadecimal
  2193. xtermTitleQueryHex = false
  2194. break
  2195. case 2:
  2196. // Do not set window/icon labels using UTF-8.
  2197. xtermTitleSetUtf = false
  2198. break
  2199. case 3:
  2200. // Do not query window/icon labels using UTF-8.
  2201. xtermTitleQueryUtf = false
  2202. break
  2203. default:
  2204. break
  2205. }
  2206. }
  2207. }
  2208. //
  2209. // CSI Ps ; Ps ; Ps t - Various window manipulations and reports (xterm)
  2210. // See https://invisible-island.net/xterm/ctlseqs/ctlseqs.html for a full
  2211. // list of commans for this escape sequence
  2212. func cmdWindowOptions (_ pars: [Int])
  2213. {
  2214. guard let tdel = self.tdel else {
  2215. return
  2216. }
  2217. switch pars {
  2218. case [1]:
  2219. tdel.windowCommand(source: self, command: .deiconifyWindow)
  2220. case [2]:
  2221. tdel.windowCommand(source: self, command: .iconifyWindow)
  2222. case _ where pars.count == 3 && pars.first == 3:
  2223. tdel.windowCommand(source: self, command: .moveWindowTo(x: pars [1], y: pars[2]))
  2224. case _ where pars.count == 3 && pars.first == 4:
  2225. tdel.windowCommand(source: self, command: .moveWindowTo(x: pars [1], y: pars[2]))
  2226. case [5]:
  2227. tdel.windowCommand(source: self, command: .bringToFront)
  2228. case [6]:
  2229. tdel.windowCommand(source: self, command: .sendToBack)
  2230. case [7]:
  2231. tdel.windowCommand(source: self, command: .refreshWindow)
  2232. case _ where pars.count == 3 && pars.first == 8:
  2233. tdel.windowCommand(source: self, command: .resizeTo(cols: pars [1], rows: pars [2]))
  2234. case [9, 0]:
  2235. tdel.windowCommand(source: self, command: .restoreMaximizedWindow)
  2236. case [9, 1]:
  2237. tdel.windowCommand(source: self, command: .maximizeWindow)
  2238. case [9, 2]:
  2239. tdel.windowCommand(source: self, command: .maximizeWindowVertically)
  2240. case [9, 3]:
  2241. tdel.windowCommand(source: self, command: .maximizeWindowHorizontally)
  2242. case [10, 0]:
  2243. tdel.windowCommand(source: self, command: .undoFullScreen)
  2244. case [10, 1]:
  2245. tdel.windowCommand(source: self, command: .switchToFullScreen)
  2246. case [10, 2]:
  2247. tdel.windowCommand(source: self, command: .toggleFullScreen)
  2248. case [15]: // Report size in pixels
  2249. if let r = tdel.windowCommand(source: self, command: .reportSizeOfScreenInPixels) {
  2250. sendResponse(r)
  2251. } else {
  2252. sendResponse (cc.CSI, "5;768;1024t")
  2253. }
  2254. case [16]: // Report cell size in pixels
  2255. // If no value is returned send 16x10
  2256. // TODO: should surface that to the UI, should not do this here
  2257. if let r = tdel.windowCommand(source: self, command: .reportCellSizeInPixels) {
  2258. sendResponse(r)
  2259. } else {
  2260. sendResponse (cc.CSI, "6;16;10t")
  2261. }
  2262. case [18]:
  2263. if let r = tdel.windowCommand(source: self, command: .reportCellSizeInPixels) {
  2264. sendResponse(r)
  2265. } else {
  2266. sendResponse(cc.CSI, "8;\(rows);\(cols)t")
  2267. }
  2268. case [19]:
  2269. if let r = tdel.windowCommand(source: self, command: .reportScreenSizeCharacters) {
  2270. sendResponse(r)
  2271. } else {
  2272. sendResponse(cc.CSI, "9;\(rows);\(cols)t")
  2273. }
  2274. case [20]:
  2275. let it = iconTitle.replacingOccurrences(of: "\\", with: "")
  2276. sendResponse (cc.OSC, "L\(it)", cc.ST)
  2277. case [21]:
  2278. let tt = terminalTitle.replacingOccurrences(of: "\\", with: "")
  2279. sendResponse (cc.OSC, "l\(tt)", cc.ST)
  2280. case [22, 0]:
  2281. terminalTitleStack = terminalTitleStack + [terminalTitle]
  2282. terminalIconStack = terminalIconStack + [iconTitle]
  2283. case [22, 1]:
  2284. terminalIconStack = terminalIconStack + [iconTitle]
  2285. case [22, 2]:
  2286. terminalTitleStack = terminalTitleStack + [terminalTitle]
  2287. case [23, 0]:
  2288. if let nt = terminalTitleStack.last {
  2289. terminalTitleStack = terminalTitleStack.dropLast()
  2290. setTitle(text: nt)
  2291. }
  2292. if let nt = terminalIconStack.last {
  2293. terminalIconStack = terminalIconStack.dropLast()
  2294. setIconTitle(text: nt)
  2295. }
  2296. case [23, 1]:
  2297. if let nt = terminalTitleStack.last {
  2298. terminalTitleStack = terminalTitleStack.dropLast()
  2299. setTitle(text: nt)
  2300. }
  2301. case [23, 2]:
  2302. if let nt = terminalIconStack.last {
  2303. terminalIconStack = terminalIconStack.dropLast()
  2304. setIconTitle(text: nt)
  2305. }
  2306. default:
  2307. log ("Unhandled Window command: \(pars)")
  2308. break
  2309. }
  2310. }
  2311. func cmdSetMargins (_ pars: [Int], _ collect: cstring)
  2312. {
  2313. var left = min (cols-1, max (0, (pars.count > 0 ? pars[0] : 1) - 1))
  2314. let right = min (cols-1, max (0, (pars.count > 1 ? pars [1] : cols) - 1))
  2315. left = min (left, right)
  2316. buffer.marginLeft = left
  2317. buffer.marginRight = right
  2318. }
  2319. //
  2320. // CSI s (sometimes, if the margin mode is false)
  2321. // ESC 7
  2322. // Save cursor (ANSI.SYS).
  2323. //
  2324. func cmdSaveCursor (_ pars: [Int], _ collect: cstring)
  2325. {
  2326. buffer.savedX = buffer.x
  2327. buffer.savedY = buffer.y
  2328. buffer.savedAttr = curAttr
  2329. buffer.savedCharset = charset
  2330. buffer.savedWraparound = wraparound
  2331. buffer.savedOriginMode = originMode
  2332. buffer.savedMarginMode = marginMode
  2333. buffer.savedReverseWraparound = reverseWraparound
  2334. }
  2335. //
  2336. // CSI Ps ; Ps r
  2337. // Set Scrolling Region [top;bottom] (default = full size of window) (DECSTBM).
  2338. // CSI ? Pm r
  2339. //
  2340. func cmdSetScrollRegion (_ pars: [Int], _ collect: cstring)
  2341. {
  2342. if collect != [] {
  2343. return
  2344. }
  2345. let buffer = self.buffer
  2346. let top = pars.count > 0 ? max (pars [0] - 1, 0) : 0
  2347. var bottom = rows
  2348. if pars.count > 1 {
  2349. // bottom = 0 means "bottom of the screen"
  2350. let p = pars [1]
  2351. if p != 0 {
  2352. bottom = min (pars [1], rows)
  2353. }
  2354. }
  2355. // normalize
  2356. bottom -= 1
  2357. // only set the scroll region if top < bottom
  2358. if top < bottom {
  2359. buffer.scrollBottom = bottom
  2360. buffer.scrollTop = top
  2361. }
  2362. setCursor(col: 0, row: 0)
  2363. }
  2364. func setCursorStyle (_ style: CursorStyle)
  2365. {
  2366. if options.cursorStyle != style {
  2367. tdel?.cursorStyleChanged(source: self, newStyle: style)
  2368. options.cursorStyle = style
  2369. }
  2370. }
  2371. //
  2372. // CSI Ps SP q Set cursor style (DECSCUSR, VT520).
  2373. // Ps = 0 -> blinking block.
  2374. // Ps = 1 -> blinking block (default).
  2375. // Ps = 2 -> steady block.
  2376. // Ps = 3 -> blinking underline.
  2377. // Ps = 4 -> steady underline.
  2378. // Ps = 5 -> blinking bar (xterm).
  2379. // Ps = 6 -> steady bar (xterm).
  2380. //
  2381. func cmdSetCursorStyle (_ pars: [Int], _ collect: cstring)
  2382. {
  2383. if (collect != [32]){ /* space */
  2384. return
  2385. }
  2386. let p = max (pars.count == 0 ? 1 : pars [0], 1)
  2387. switch (p) {
  2388. case 1:
  2389. setCursorStyle (.blinkBlock)
  2390. case 2:
  2391. setCursorStyle (.steadyBlock)
  2392. case 3:
  2393. setCursorStyle (.blinkUnderline)
  2394. case 4:
  2395. setCursorStyle (.steadyUnderline)
  2396. case 5:
  2397. setCursorStyle (.blinkingBar)
  2398. case 6:
  2399. setCursorStyle (.steadyBar)
  2400. default:
  2401. break;
  2402. }
  2403. }
  2404. //
  2405. // Proxy for various CSI .* p commands
  2406. func csiPHandler (_ pars: [Int], _ collect: cstring)
  2407. {
  2408. switch collect {
  2409. case [UInt8 (ascii: "!")]:
  2410. cmdSoftReset ()
  2411. case [UInt8 (ascii: "\"")]:
  2412. cmdSetConformanceLevel (pars, collect)
  2413. default:
  2414. log ("Unhandled CSI \(String (cString: collect)) with pars=\(pars)")
  2415. }
  2416. }
  2417. // CSI Pl ; Pc " p
  2418. // Set conformance level (DECSCL), VT220 and up
  2419. func cmdSetConformanceLevel (_ pars: [Int], _ collect: cstring)
  2420. {
  2421. if pars.count > 0 {
  2422. let level = pars [0]
  2423. switch level {
  2424. case 61:
  2425. conformance = .vt100
  2426. cc.send8bit = false
  2427. case 62:
  2428. conformance = .vt200
  2429. case 63:
  2430. conformance = .vt300
  2431. case 64:
  2432. conformance = .vt400
  2433. case 65:
  2434. conformance = .vt500
  2435. default:
  2436. conformance = .vt500
  2437. }
  2438. }
  2439. if pars.count > 1 && conformance != .vt100 {
  2440. switch pars [1] {
  2441. case 0:
  2442. cc.send8bit = true
  2443. case 2:
  2444. cc.send8bit = true
  2445. default:
  2446. cc.send8bit = false
  2447. }
  2448. }
  2449. }
  2450. //
  2451. // http://vt100.net/docs/vt220-rm/table4-10.html
  2452. //
  2453. /* ! - CSI ! p Soft terminal reset (DECSTR). */
  2454. func cmdSoftReset ()
  2455. {
  2456. cursorHidden = false
  2457. insertMode = false
  2458. originMode = false
  2459. reverseWraparound = false
  2460. wraparound = true // defaults: xterm - true, vt100 - false
  2461. applicationKeypad = false
  2462. syncScrollArea ()
  2463. applicationCursor = false
  2464. buffer.scrollTop = 0
  2465. buffer.scrollBottom = rows - 1
  2466. curAttr = CharData.defaultAttr
  2467. buffer.softReset ()
  2468. charset = nil
  2469. setgLevel (0)
  2470. conformance = .vt500
  2471. hyperLinkTracking = nil
  2472. lineFeedMode = options.convertEol
  2473. resetAllColors()
  2474. tdel?.showCursor(source: self)
  2475. // MIGUEL TODO:
  2476. // TODO: audit any new variables, those in setup might be useful
  2477. }
  2478. /// Performs a terminal soft-reset, the equivalent of the DECSTR sequence
  2479. /// For a full reset see `resetToInitialState`
  2480. public func softReset ()
  2481. {
  2482. cmdSoftReset()
  2483. }
  2484. //
  2485. // CSI Ps n Device Status Report (DSR).
  2486. // Ps = 5 -> Status Report. Result (``OK'') is
  2487. // CSI 0 n
  2488. // Ps = 6 -> Report Cursor Position (CPR) [row;column].
  2489. // Result is
  2490. // CSI r ; c R
  2491. // CSI ? Ps n
  2492. // Device Status Report (DSR, DEC-specific).
  2493. // Ps = 6 -> Report Cursor Position (CPR) [row;column] as CSI
  2494. // ? r ; c R (assumes page is zero).
  2495. // Ps = 1 5 -> Report Printer status as CSI ? 1 0 n (ready).
  2496. // or CSI ? 1 1 n (not ready).
  2497. // Ps = 2 5 -> Report UDK status as CSI ? 2 0 n (unlocked)
  2498. // or CSI ? 2 1 n (locked).
  2499. // Ps = 2 6 -> Report Keyboard status as
  2500. // CSI ? 2 7 ; 1 ; 0 ; 0 n (North American).
  2501. // The last two parameters apply to VT400 & up, and denote key-
  2502. // board ready and LK01 respectively.
  2503. // Ps = 5 3 -> Report Locator status as
  2504. // CSI ? 5 3 n Locator available, if compiled-in, or
  2505. // CSI ? 5 0 n No Locator, if not.
  2506. //
  2507. func cmdDeviceStatus (_ pars: [Int], _ collect: cstring)
  2508. {
  2509. let buffer = self.buffer
  2510. if collect.count == 0 {
  2511. switch (pars [0]) {
  2512. case 5:
  2513. // status report
  2514. sendResponse (cc.CSI, "0n")
  2515. case 6:
  2516. // cursor position
  2517. let y = max (1, buffer.y + 1 - (originMode ? buffer.scrollTop : 0))
  2518. // Need the max, because the cursor could be before the leftMargin
  2519. let x = max (1, buffer.x + 1 - (originMode ? buffer.marginLeft : 0))
  2520. sendResponse (cc.CSI, "\(y);\(x)R")
  2521. default:
  2522. break;
  2523. }
  2524. } else if (collect == [UInt8 (ascii: "?")]) {
  2525. // modern xterm doesnt seem to
  2526. // respond to any of these except ?6, 6, and 5
  2527. switch pars [0] {
  2528. case 6:
  2529. // cursor position
  2530. let y = buffer.y + 1 - (originMode ? buffer.scrollTop : 0)
  2531. // Need the max, because the cursor could be before the leftMargin
  2532. let x = max (1, buffer.x + 1 - (usingMargins () ? buffer.marginLeft : 0))
  2533. sendResponse (cc.CSI, "?\(y);\(x);1R")
  2534. case 15:
  2535. // Request printer status report, we respond "We are ready"
  2536. sendResponse(cc.CSI, "?10n")
  2537. break;
  2538. case 25:
  2539. // We respond "User defined keys are locked"
  2540. sendResponse(cc.CSI, "?21n")
  2541. break;
  2542. case 26:
  2543. // Requests keyboard type
  2544. // We respond "American keyboard", TODO: worth plugging something else? Mac perhaps?
  2545. sendResponse(cc.CSI, "?27;1;0;0n")
  2546. break;
  2547. case 53:
  2548. // TODO: no dec locator/mouse
  2549. // this.handler(C0.ESC + '[?50n');
  2550. break;
  2551. case 55:
  2552. // Request locator status
  2553. sendResponse(cc.CSI, "?53n")
  2554. case 56:
  2555. // What kind of locator we have, we reply mouse, but perhaps on iOS we should respond something else
  2556. sendResponse(cc.CSI, "?57;1n")
  2557. case 62:
  2558. // Macro space report
  2559. sendResponse(cc.CSI, "0*{")
  2560. case 63:
  2561. // Requests checksum of macros, we return 0
  2562. let id = pars.count > 1 ? pars [1] : 0
  2563. sendResponse(cc.DCS, "\(id)!~0000", cc.ST)
  2564. case 75:
  2565. // Data integrity report, no issues:
  2566. sendResponse (cc.CSI, "?70n")
  2567. case 85:
  2568. // Multiple session status, we reply single session
  2569. sendResponse (cc.CSI, "?83n")
  2570. default:
  2571. break
  2572. }
  2573. }
  2574. }
  2575. //
  2576. // CSI Pm m Character Attributes (SGR).
  2577. // Ps = 0 -> Normal (default).
  2578. // Ps = 1 -> Bold.
  2579. // Ps = 2 -> Faint, decreased intensity (ISO 6429).
  2580. // Ps = 4 -> Underlined.
  2581. // Ps = 5 -> Blink (appears as Bold).
  2582. // Ps = 7 -> Inverse.
  2583. // Ps = 8 -> Invisible, i.e., hidden (VT300).
  2584. // Ps = 9 -> Crossed out character
  2585. // Ps = 2 2 -> Normal (neither bold nor faint).
  2586. // Ps = 2 4 -> Not underlined.
  2587. // Ps = 2 5 -> Steady (not blinking).
  2588. // Ps = 2 7 -> Positive (not inverse).
  2589. // Ps = 2 8 -> Visible, i.e., not hidden (VT300).
  2590. // Ps = 2 9 -> Not crossed out
  2591. // Ps = 3 0 -> Set foreground color to Black.
  2592. // Ps = 3 1 -> Set foreground color to Red.
  2593. // Ps = 3 2 -> Set foreground color to Green.
  2594. // Ps = 3 3 -> Set foreground color to Yellow.
  2595. // Ps = 3 4 -> Set foreground color to Blue.
  2596. // Ps = 3 5 -> Set foreground color to Magenta.
  2597. // Ps = 3 6 -> Set foreground color to Cyan.
  2598. // Ps = 3 7 -> Set foreground color to White.
  2599. // Ps = 3 9 -> Set foreground color to default (original).
  2600. // Ps = 4 0 -> Set background color to Black.
  2601. // Ps = 4 1 -> Set background color to Red.
  2602. // Ps = 4 2 -> Set background color to Green.
  2603. // Ps = 4 3 -> Set background color to Yellow.
  2604. // Ps = 4 4 -> Set background color to Blue.
  2605. // Ps = 4 5 -> Set background color to Magenta.
  2606. // Ps = 4 6 -> Set background color to Cyan.
  2607. // Ps = 4 7 -> Set background color to White.
  2608. // Ps = 4 9 -> Set background color to default (original).
  2609. //
  2610. // If 16-color support is compiled, the following apply. Assume
  2611. // that xterm's resources are set so that the ISO color codes are
  2612. // the first 8 of a set of 16. Then the aixterm colors are the
  2613. // bright versions of the ISO colors:
  2614. // Ps = 9 0 -> Set foreground color to Black.
  2615. // Ps = 9 1 -> Set foreground color to Red.
  2616. // Ps = 9 2 -> Set foreground color to Green.
  2617. // Ps = 9 3 -> Set foreground color to Yellow.
  2618. // Ps = 9 4 -> Set foreground color to Blue.
  2619. // Ps = 9 5 -> Set foreground color to Magenta.
  2620. // Ps = 9 6 -> Set foreground color to Cyan.
  2621. // Ps = 9 7 -> Set foreground color to White.
  2622. // Ps = 1 0 0 -> Set background color to Black.
  2623. // Ps = 1 0 1 -> Set background color to Red.
  2624. // Ps = 1 0 2 -> Set background color to Green.
  2625. // Ps = 1 0 3 -> Set background color to Yellow.
  2626. // Ps = 1 0 4 -> Set background color to Blue.
  2627. // Ps = 1 0 5 -> Set background color to Magenta.
  2628. // Ps = 1 0 6 -> Set background color to Cyan.
  2629. // Ps = 1 0 7 -> Set background color to White.
  2630. //
  2631. // If xterm is compiled with the 16-color support disabled, it
  2632. // supports the following, from rxvt:
  2633. // Ps = 1 0 0 -> Set foreground and background color to
  2634. // default.
  2635. //
  2636. // If 88- or 256-color support is compiled, the following apply.
  2637. // Ps = 3 8 ; 5 ; Ps -> Set foreground color to the second
  2638. // Ps.
  2639. // Ps = 4 8 ; 5 ; Ps -> Set background color to the second
  2640. // Ps.
  2641. //
  2642. func cmdCharAttributes (_ pars: [Int], _ collect: cstring)
  2643. {
  2644. // Optimize a single SGR0.
  2645. if pars.count == 1 && pars [0] == 0 {
  2646. curAttr = CharData.defaultAttr
  2647. return;
  2648. }
  2649. let parCount = pars.count
  2650. //let empty = CharacterStyle (attribute: 0)
  2651. var style = curAttr.style
  2652. var fg = curAttr.fg
  2653. var bg = curAttr.bg
  2654. let def = CharData.defaultAttr
  2655. var i = 0
  2656. // Extended Colors
  2657. //
  2658. // There is an ambiguity here that is troublesome, to support extended
  2659. // colors and colorspaces, two competing systems exists, one uses for example:
  2660. // 38;2;R;G;B;NEXT - foreground true color
  2661. // 38:2:ColorSpace:R:G:B:REST;NEXT - second style for the same
  2662. //
  2663. // The former apparently was a mistake, but we need to disambiguate the meaning
  2664. // of pars, based on whether the above uses ":" or ";" we need that, because
  2665. // the SGR is a collection of attributes, so after our parameter values, we
  2666. // need to continue processing
  2667. //
  2668. //
  2669. func parseExtendedColor () -> Attribute.Color? {
  2670. var color: Attribute.Color? = nil
  2671. let v = parser._parsTxt
  2672. // If this is the new style
  2673. if v.count > 2 && v [2] == UInt8(ascii: ":") {
  2674. switch pars [i] {
  2675. case 2: // RGB color
  2676. i += 1
  2677. // Color style, we ignore "ColorSpace"
  2678. if i+3 < parCount {
  2679. color = Attribute.Color.trueColor(
  2680. red: UInt8(min (pars [i+1], 255)),
  2681. green: UInt8(min (pars [i+2], 255)),
  2682. blue: UInt8(min (pars [i+3], 255)))
  2683. i += 4
  2684. }
  2685. default:
  2686. break
  2687. }
  2688. } else {
  2689. switch pars [i] {
  2690. case 2: // RGB color
  2691. i += 1
  2692. if i+2 < parCount {
  2693. color = Attribute.Color.trueColor(
  2694. red: UInt8(min (pars [i], 255)),
  2695. green: UInt8(min (pars [i+1], 255)),
  2696. blue: UInt8(min (pars [i+2], 255)))
  2697. i += 3
  2698. }
  2699. case 3: // CMY color - not supported
  2700. break
  2701. case 4: // CMYK color - not supported
  2702. break
  2703. case 5: // indexed color
  2704. if i+1 < parCount {
  2705. fg = Attribute.Color.ansi256(code: UInt8 (min (255, pars [i+1])))
  2706. i += 1
  2707. }
  2708. i += 1
  2709. default:
  2710. break
  2711. }
  2712. }
  2713. return color
  2714. }
  2715. while i < parCount {
  2716. var p = pars [i]
  2717. switch p {
  2718. case 0:
  2719. // default
  2720. style = def.style
  2721. fg = def.fg
  2722. bg = def.bg
  2723. case 1:
  2724. // bold text
  2725. style = [style, .bold]
  2726. case 2:
  2727. // dimmed text
  2728. style = [style, .dim]
  2729. case 3:
  2730. // italic text
  2731. style = [style, .italic]
  2732. case 4:
  2733. // underlined text
  2734. style = [style, .underline]
  2735. case 5:
  2736. // blink
  2737. style = [style, .blink]
  2738. case 7:
  2739. // inverse and positive
  2740. // test with: echo -e '\e[31m\e[42mhello\e[7mworld\e[27mhi\e[m'
  2741. style = [style, .inverse]
  2742. case 8:
  2743. // invisible
  2744. style = [style, .invisible]
  2745. case 9:
  2746. style = [style, .crossedOut]
  2747. case 21:
  2748. // double underline
  2749. break
  2750. case 22:
  2751. // not bold nor faint
  2752. style.remove (.bold)
  2753. style.remove (.dim)
  2754. case 23:
  2755. // not italic
  2756. style.remove (.italic)
  2757. case 24:
  2758. // not underlined
  2759. style.remove (.underline)
  2760. case 25:
  2761. // not blink
  2762. style.remove (.blink)
  2763. case 27:
  2764. // not inverse
  2765. style.remove (.inverse)
  2766. case 28:
  2767. // not invisible
  2768. style.remove (.invisible)
  2769. case 29:
  2770. // not crossed out
  2771. style.remove (.crossedOut)
  2772. case 30...37:
  2773. // fg color 8
  2774. fg = Attribute.Color.ansi256(code: UInt8(p - 30))
  2775. case 38:
  2776. i += 1
  2777. if let parsed = parseExtendedColor () {
  2778. fg = parsed
  2779. }
  2780. continue
  2781. case 39:
  2782. // reset fg
  2783. fg = CharData.defaultAttr.fg
  2784. case 40...47:
  2785. // bg color 8
  2786. bg = Attribute.Color.ansi256(code: UInt8(p - 40))
  2787. case 48:
  2788. i += 1
  2789. if let parsed = parseExtendedColor() {
  2790. bg = parsed
  2791. }
  2792. continue
  2793. case 49:
  2794. // reset bg
  2795. bg = CharData.defaultAttr.bg
  2796. case 90...97:
  2797. // fg color 16
  2798. p += 8
  2799. fg = Attribute.Color.ansi256(code: UInt8(p - 90))
  2800. case 100...107:
  2801. // bg color 16
  2802. p += 8;
  2803. bg = Attribute.Color.ansi256(code: UInt8(p - 100))
  2804. default:
  2805. log ("Unknown SGR attribute: \(p) \(pars)")
  2806. }
  2807. i += 1
  2808. }
  2809. curAttr = Attribute(fg: fg, bg: bg, style: style)
  2810. }
  2811. //
  2812. //CSI Pm l Reset Mode (RM).
  2813. // Ps = 2 -> Keyboard Action Mode (AM).
  2814. // Ps = 4 -> Replace Mode (IRM).
  2815. // Ps = 1 2 -> Send/receive (SRM).
  2816. // Ps = 2 0 -> Normal Linefeed (LNM).
  2817. //CSI ? Pm l
  2818. // DEC Private Mode Reset (DECRST).
  2819. // Ps = 1 -> Normal Cursor Keys (DECCKM).
  2820. // Ps = 2 -> Designate VT52 mode (DECANM).
  2821. // Ps = 3 -> 80 Column Mode (DECCOLM).
  2822. // Ps = 4 -> Jump (Fast) Scroll (DECSCLM).
  2823. // Ps = 5 -> Normal Video (DECSCNM).
  2824. // Ps = 6 -> Normal Cursor Mode (DECOM).
  2825. // Ps = 7 -> No Wraparound Mode (DECAWM).
  2826. // Ps = 8 -> No Auto-repeat Keys (DECARM).
  2827. // Ps = 9 -> Don't send Mouse X & Y on button press.
  2828. // Ps = 1 0 -> Hide toolbar (rxvt).
  2829. // Ps = 1 2 -> Stop Blinking Cursor (att610).
  2830. // Ps = 1 8 -> Don't print form feed (DECPFF).
  2831. // Ps = 1 9 -> Limit print to scrolling region (DECPEX).
  2832. // Ps = 2 5 -> Hide Cursor (DECTCEM).
  2833. // Ps = 3 0 -> Don't show scrollbar (rxvt).
  2834. // Ps = 3 5 -> Disable font-shifting functions (rxvt).
  2835. // Ps = 4 0 -> Disallow 80 -> 132 Mode.
  2836. // Ps = 4 1 -> No more(1) fix (see curses resource).
  2837. // Ps = 4 2 -> Disable Nation Replacement Character sets (DEC-
  2838. // NRCM).
  2839. // Ps = 4 4 -> Turn Off Margin Bell.
  2840. // Ps = 4 5 -> No Reverse-wraparound Mode.
  2841. // Ps = 4 6 -> Stop Logging. (This is normally disabled by a
  2842. // compile-time option).
  2843. // Ps = 4 7 -> Use Normal Screen Buffer.
  2844. // Ps = 6 6 -> Numeric keypad (DECNKM).
  2845. // Ps = 6 7 -> Backarrow key sends delete (DECBKM).
  2846. // Ps = 1 0 0 0 -> Don't send Mouse X & Y on button press and
  2847. // release. See the section Mouse Tracking.
  2848. // Ps = 1 0 0 1 -> Don't use Hilite Mouse Tracking.
  2849. // Ps = 1 0 0 2 -> Don't use Cell Motion Mouse Tracking.
  2850. // Ps = 1 0 0 3 -> Don't use All Motion Mouse Tracking.
  2851. // Ps = 1 0 0 4 -> Don't send FocusIn/FocusOut events.
  2852. // Ps = 1 0 0 5 -> Disable Extended Mouse Mode.
  2853. // Ps = 1 0 1 0 -> Don't scroll to bottom on tty output
  2854. // (rxvt).
  2855. // Ps = 1 0 1 1 -> Don't scroll to bottom on key press (rxvt).
  2856. // Ps = 1 0 3 4 -> Don't interpret "meta" key. (This disables
  2857. // the eightBitInput resource).
  2858. // Ps = 1 0 3 5 -> Disable special modifiers for Alt and Num-
  2859. // Lock keys. (This disables the numLock resource).
  2860. // Ps = 1 0 3 6 -> Don't send ESC when Meta modifies a key.
  2861. // (This disables the metaSendsEscape resource).
  2862. // Ps = 1 0 3 7 -> Send VT220 Remove from the editing-keypad
  2863. // Delete key.
  2864. // Ps = 1 0 3 9 -> Don't send ESC when Alt modifies a key.
  2865. // (This disables the altSendsEscape resource).
  2866. // Ps = 1 0 4 0 -> Do not keep selection when not highlighted.
  2867. // (This disables the keepSelection resource).
  2868. // Ps = 1 0 4 1 -> Use the PRIMARY selection. (This disables
  2869. // the selectToClipboard resource).
  2870. // Ps = 1 0 4 2 -> Disable Urgency window manager hint when
  2871. // Control-G is received. (This disables the bellIsUrgent
  2872. // resource).
  2873. // Ps = 1 0 4 3 -> Disable raising of the window when Control-
  2874. // G is received. (This disables the popOnBell resource).
  2875. // Ps = 1 0 4 7 -> Use Normal Screen Buffer, clearing screen
  2876. // first if in the Alternate Screen. (This may be disabled by
  2877. // the titeInhibit resource).
  2878. // Ps = 1 0 4 8 -> Restore cursor as in DECRC. (This may be
  2879. // disabled by the titeInhibit resource).
  2880. // Ps = 1 0 4 9 -> Use Normal Screen Buffer and restore cursor
  2881. // as in DECRC. (This may be disabled by the titeInhibit
  2882. // resource). This combines the effects of the 1 0 4 7 and 1 0
  2883. // 4 8 modes. Use this with terminfo-based applications rather
  2884. // than the 4 7 mode.
  2885. // Ps = 1 0 5 0 -> Reset terminfo/termcap function-key mode.
  2886. // Ps = 1 0 5 1 -> Reset Sun function-key mode.
  2887. // Ps = 1 0 5 2 -> Reset HP function-key mode.
  2888. // Ps = 1 0 5 3 -> Reset SCO function-key mode.
  2889. // Ps = 1 0 6 0 -> Reset legacy keyboard emulation (X11R6).
  2890. // Ps = 1 0 6 1 -> Reset keyboard emulation to Sun/PC style.
  2891. // Ps = 2 0 0 4 -> Reset bracketed paste mode.
  2892. //
  2893. func cmdResetMode (_ pars: [Int], _ collect: cstring)
  2894. {
  2895. if pars.count == 0 {
  2896. return
  2897. }
  2898. if pars.count > 1 {
  2899. for i in 0..<pars.count {
  2900. resetMode (pars [i], collect)
  2901. }
  2902. return
  2903. }
  2904. resetMode (pars [0], collect)
  2905. }
  2906. func resetMode (_ par: Int, _ collect: cstring)
  2907. {
  2908. if collect == [] {
  2909. switch (par) {
  2910. case 2:
  2911. // KAM mode - unlocks the keyboard, not supported
  2912. break
  2913. case 4:
  2914. // IRM Insert/Replace Mode
  2915. insertMode = false
  2916. case 20:
  2917. // LNM—Line Feed/New Line Mode
  2918. lineFeedMode = false
  2919. break
  2920. default:
  2921. break
  2922. }
  2923. } else if collect == [UInt8 (ascii: "?")] {
  2924. switch (par) {
  2925. case 1:
  2926. applicationCursor = false
  2927. case 3:
  2928. if allow80To132 {
  2929. // DECCOLM
  2930. resize (cols: 80, rows: rows)
  2931. tdel?.sizeChanged(source: self)
  2932. resetToInitialState()
  2933. }
  2934. case 4: // DECSCLM - Jump scroll mode
  2935. // Ignore, unimplemented
  2936. break
  2937. case 5:
  2938. // Reset default color
  2939. curAttr = CharData.defaultAttr
  2940. case 6:
  2941. // DECOM Reset
  2942. originMode = false
  2943. case 7:
  2944. wraparound = false
  2945. case 12:
  2946. cursorBlink = false
  2947. case 40:
  2948. allow80To132 = false
  2949. case 41:
  2950. // Workaround not implemented
  2951. break
  2952. case 45:
  2953. reverseWraparound = false
  2954. case 66:
  2955. log ("Switching back to normal keypad.");
  2956. applicationKeypad = false
  2957. syncScrollArea ()
  2958. case 69:
  2959. // DECSLRM
  2960. marginMode = false
  2961. case 9: // X10 Mouse
  2962. mouseMode = .off
  2963. case 1000: // vt200 mouse
  2964. mouseMode = .off
  2965. case 95: // DECNCSM - clear on DECCOLM changes
  2966. // unsupported
  2967. break
  2968. case 1002: // button event mouse
  2969. mouseMode = .off
  2970. case 1003: // any event mouse
  2971. mouseMode = .off
  2972. case 1004: // send focusin/focusout events
  2973. sendFocus = false
  2974. case 1005: // utf8 ext mode mouse
  2975. mouseProtocol = .x10
  2976. case 1006: // sgr ext mode mouse
  2977. mouseProtocol = .x10
  2978. case 1015: // urxvt ext mode mouse
  2979. mouseProtocol = .x10
  2980. case 25: // hide cursor
  2981. hideCursor ()
  2982. case 1048: // alt screen cursor
  2983. cmdRestoreCursor ([], [])
  2984. case 1034:
  2985. // Terminal.app ignores this request, and keeps sending ESC+letter
  2986. break
  2987. case 1049: // alt screen buffer cursor
  2988. fallthrough
  2989. case 47: // normal screen buffer
  2990. fallthrough
  2991. case 1047: // normal screen buffer - clearing it first
  2992. // Ensure the selection manager has the correct buffer
  2993. buffers!.activateNormalBuffer (clearAlt: par == 1047 || par == 1049)
  2994. if (par == 1049){
  2995. cmdRestoreCursor ([], [])
  2996. }
  2997. refresh (startRow: 0, endRow: rows - 1)
  2998. syncScrollArea ()
  2999. showCursor ()
  3000. tdel?.bufferActivated(source: self)
  3001. case 2004: // bracketed paste mode (https://cirw.in/blog/bracketed-paste)
  3002. bracketedPasteMode = false
  3003. break
  3004. default:
  3005. log ("Unhandled DEC Private Mode Reset (DECRST) with \(par)")
  3006. break
  3007. }
  3008. }
  3009. }
  3010. //
  3011. // CSI Pm h Set Mode (SM).
  3012. // Ps = 2 -> Keyboard Action Mode (AM).
  3013. // Ps = 4 -> Insert Mode (IRM).
  3014. // Ps = 1 2 -> Send/receive (SRM).
  3015. // Ps = 2 0 -> Automatic Newline (LNM).
  3016. // CSI ? Pm h
  3017. // DEC Private Mode Set (DECSET).
  3018. // Ps = 1 -> Application Cursor Keys (DECCKM).
  3019. // Ps = 2 -> Designate USASCII for character sets G0-G3
  3020. // (DECANM), and set VT100 mode.
  3021. // Ps = 3 -> 132 Column Mode (DECCOLM).
  3022. // Ps = 4 -> Smooth (Slow) Scroll (DECSCLM).
  3023. // Ps = 5 -> Reverse Video (DECSCNM).
  3024. // Ps = 6 -> Origin Mode (DECOM).
  3025. // Ps = 7 -> Wraparound Mode (DECAWM).
  3026. // Ps = 8 -> Auto-repeat Keys (DECARM).
  3027. // Ps = 9 -> Send Mouse X & Y on button press. See the sec-
  3028. // tion Mouse Tracking.
  3029. // Ps = 1 0 -> Show toolbar (rxvt).
  3030. // Ps = 1 2 -> Start Blinking Cursor (att610).
  3031. // Ps = 1 8 -> Print form feed (DECPFF).
  3032. // Ps = 1 9 -> Set print extent to full screen (DECPEX).
  3033. // Ps = 2 5 -> Show Cursor (DECTCEM).
  3034. // Ps = 3 0 -> Show scrollbar (rxvt).
  3035. // Ps = 3 5 -> Enable font-shifting functions (rxvt).
  3036. // Ps = 3 8 -> Enter Tektronix Mode (DECTEK).
  3037. // Ps = 4 0 -> Allow 80 -> 132 Mode.
  3038. // Ps = 4 1 -> more(1) fix (see curses resource).
  3039. // Ps = 4 2 -> Enable Nation Replacement Character sets (DECN-
  3040. // RCM).
  3041. // Ps = 4 4 -> Turn On Margin Bell.
  3042. // Ps = 4 5 -> Reverse-wraparound Mode.
  3043. // Ps = 4 6 -> Start Logging. This is normally disabled by a
  3044. // compile-time option.
  3045. // Ps = 4 7 -> Use Alternate Screen Buffer. (This may be dis-
  3046. // abled by the titeInhibit resource).
  3047. // Ps = 6 6 -> Application keypad (DECNKM).
  3048. // Ps = 6 7 -> Backarrow key sends backspace (DECBKM).
  3049. // Ps = 1 0 0 0 -> Send Mouse X & Y on button press and
  3050. // release. See the section Mouse Tracking.
  3051. // Ps = 1 0 0 1 -> Use Hilite Mouse Tracking.
  3052. // Ps = 1 0 0 2 -> Use Cell Motion Mouse Tracking.
  3053. // Ps = 1 0 0 3 -> Use All Motion Mouse Tracking.
  3054. // Ps = 1 0 0 4 -> Send FocusIn/FocusOut events.
  3055. // Ps = 1 0 0 5 -> Enable Extended Mouse Mode.
  3056. // Ps = 1 0 1 0 -> Scroll to bottom on tty output (rxvt).
  3057. // Ps = 1 0 1 1 -> Scroll to bottom on key press (rxvt).
  3058. // Ps = 1 0 3 4 -> Interpret "meta" key, sets eighth bit.
  3059. // (enables the eightBitInput resource).
  3060. // Ps = 1 0 3 5 -> Enable special modifiers for Alt and Num-
  3061. // Lock keys. (This enables the numLock resource).
  3062. // Ps = 1 0 3 6 -> Send ESC when Meta modifies a key. (This
  3063. // enables the metaSendsEscape resource).
  3064. // Ps = 1 0 3 7 -> Send DEL from the editing-keypad Delete
  3065. // key.
  3066. // Ps = 1 0 3 9 -> Send ESC when Alt modifies a key. (This
  3067. // enables the altSendsEscape resource).
  3068. // Ps = 1 0 4 0 -> Keep selection even if not highlighted.
  3069. // (This enables the keepSelection resource).
  3070. // Ps = 1 0 4 1 -> Use the CLIPBOARD selection. (This enables
  3071. // the selectToClipboard resource).
  3072. // Ps = 1 0 4 2 -> Enable Urgency window manager hint when
  3073. // Control-G is received. (This enables the bellIsUrgent
  3074. // resource).
  3075. // Ps = 1 0 4 3 -> Enable raising of the window when Control-G
  3076. // is received. (enables the popOnBell resource).
  3077. // Ps = 1 0 4 7 -> Use Alternate Screen Buffer. (This may be
  3078. // disabled by the titeInhibit resource).
  3079. // Ps = 1 0 4 8 -> Save cursor as in DECSC. (This may be dis-
  3080. // abled by the titeInhibit resource).
  3081. // Ps = 1 0 4 9 -> Save cursor as in DECSC and use Alternate
  3082. // Screen Buffer, clearing it first. (This may be disabled by
  3083. // the titeInhibit resource). This combines the effects of the 1
  3084. // 0 4 7 and 1 0 4 8 modes. Use this with terminfo-based
  3085. // applications rather than the 4 7 mode.
  3086. // Ps = 1 0 5 0 -> Set terminfo/termcap function-key mode.
  3087. // Ps = 1 0 5 1 -> Set Sun function-key mode.
  3088. // Ps = 1 0 5 2 -> Set HP function-key mode.
  3089. // Ps = 1 0 5 3 -> Set SCO function-key mode.
  3090. // Ps = 1 0 6 0 -> Set legacy keyboard emulation (X11R6).
  3091. // Ps = 1 0 6 1 -> Set VT220 keyboard emulation.
  3092. // Ps = 2 0 0 4 -> Set bracketed paste mode.
  3093. // Modes:
  3094. // http: *vt100.net/docs/vt220-rm/chapter4.html
  3095. //
  3096. func cmdSetMode (_ pars: [Int], _ collect: cstring)
  3097. {
  3098. if pars.count == 0 {
  3099. return
  3100. }
  3101. if pars.count > 1 {
  3102. for i in 0..<pars.count {
  3103. setMode (pars [i], collect)
  3104. }
  3105. return
  3106. }
  3107. setMode (pars [0], collect)
  3108. }
  3109. func setMode (_ par: Int, _ collect: cstring)
  3110. {
  3111. if (collect == []) {
  3112. switch par {
  3113. case 2:
  3114. // KAM mode - unlocks the keyboard, I do not want to support it
  3115. break
  3116. case 4:
  3117. // IRM Insert/Replace Mode
  3118. // https://vt100.net/docs/vt510-rm/IRM.html
  3119. insertMode = true
  3120. // case 12:
  3121. // SRM—Local Echo: Send/Receive Mode
  3122. // break
  3123. case 20:
  3124. // Automatic New Line (LNM)
  3125. lineFeedMode = true
  3126. break;
  3127. default:
  3128. log ("Unhandled verbatim setMode with \(par) and \(collect)")
  3129. break
  3130. }
  3131. } else if collect == [UInt8 (ascii: "?")] {
  3132. switch par {
  3133. case 1:
  3134. applicationCursor = true
  3135. case 2:
  3136. setgCharset (0, charset: CharSets.defaultCharset)
  3137. setgCharset (1, charset: CharSets.defaultCharset)
  3138. setgCharset (2, charset: CharSets.defaultCharset)
  3139. setgCharset (3, charset: CharSets.defaultCharset)
  3140. // set VT100 mode here
  3141. case 3: // DECCOLM - go to 132 col mode
  3142. if allow80To132 {
  3143. resize (cols: 132, rows: rows)
  3144. resetToInitialState()
  3145. tdel?.sizeChanged(source: self)
  3146. }
  3147. case 4: // Smooth scroll mode
  3148. // DECSCLM, unsupported
  3149. break
  3150. case 5:
  3151. // Inverted colors
  3152. curAttr = CharData.invertedAttr
  3153. case 6:
  3154. // DECOM Set
  3155. originMode = true
  3156. case 7:
  3157. wraparound = true
  3158. case 12:
  3159. cursorBlink = true
  3160. break;
  3161. case 40:
  3162. allow80To132 = true
  3163. case 66:
  3164. log ("Serial port requested application keypad.")
  3165. applicationKeypad = true
  3166. syncScrollArea ()
  3167. case 9:
  3168. // X10 Mouse
  3169. mouseMode = .x10
  3170. case 45: // Xterm Reverse Wrap-around
  3171. // reverse wraparound can only be enabled if Auto-wrap is enabled (DECAWM)
  3172. if wraparound {
  3173. reverseWraparound = true
  3174. }
  3175. case 69:
  3176. // Enable left and right margin mode (DECLRMM),
  3177. marginMode = true
  3178. case 95: // DECNCSM - clear on DECCOLM changes
  3179. // unsupported
  3180. break
  3181. case 1000:
  3182. // SET_VT200_HIGHLIGHT_MOUSE
  3183. mouseMode = .vt200
  3184. case 1002:
  3185. // SET_BTN_EVENT_MOUSE
  3186. mouseMode = .buttonEventTracking
  3187. case 1003:
  3188. // SET_ANY_EVENT_MOUSE
  3189. mouseMode = .anyEvent
  3190. case 1004: // send focusin/focusout events
  3191. // focusin: ^[[I
  3192. // focusout: ^[[O
  3193. sendFocus = true
  3194. case 1005:
  3195. // utf8 ext mode mouse
  3196. mouseProtocol = .utf8
  3197. break;
  3198. case 1006: // sgr ext mode mouse
  3199. mouseProtocol = .sgr
  3200. case 1015: // urxvt ext mode mouse
  3201. mouseProtocol = .urxvt
  3202. case 25: // show cursor
  3203. showCursor()
  3204. case 63:
  3205. // DECRLM - Cursor Right to Left Mode, not supported
  3206. break
  3207. case 1034:
  3208. // Terminal.app ignores this request, and keeps sending ESC+letter
  3209. // Given our UTF8 world, I do not think this is a worth encoding
  3210. break
  3211. case 1048: // alt screen cursor
  3212. cmdSaveCursor ([], [])
  3213. case 1049: // alt screen buffer cursor
  3214. cmdSaveCursor ([], [])
  3215. // FALL-THROUGH
  3216. fallthrough
  3217. case 47: // alt screen buffer
  3218. fallthrough
  3219. case 1047: // alt screen buffer
  3220. buffers!.activateAltBuffer (fillAttr: nil)
  3221. refresh (startRow: 0, endRow: rows - 1)
  3222. syncScrollArea ()
  3223. showCursor ()
  3224. tdel?.bufferActivated(source: self)
  3225. case 2004: // bracketed paste mode (https://cirw.in/blog/bracketed-paste)
  3226. // TODO: must implement bracketed paste mode
  3227. bracketedPasteMode = true
  3228. default:
  3229. log ("Unhandled DEC Private Mode Set (DECSET) with \(par)")
  3230. break;
  3231. }
  3232. } else {
  3233. log ("Unhandled setMode (SM) with \(par) and \(collect)")
  3234. }
  3235. }
  3236. //
  3237. // CSI Ps g Tab Clear (TBC).
  3238. // Ps = 0 -> Clear Current Column (default).
  3239. // Ps = 3 -> Clear All.
  3240. // Potentially:
  3241. // Ps = 2 -> Clear Stops on Line.
  3242. // http://vt100.net/annarbor/aaa-ug/section6.html
  3243. //
  3244. func cmdTabClear (_ pars: [Int], _ collect: cstring)
  3245. {
  3246. let p = pars.count == 0 ? 0 : pars [0]
  3247. if p == 0 {
  3248. buffer.tabClear(pos: buffer.x)
  3249. } else if (p == 3) {
  3250. buffer.clearTabStops ()
  3251. }
  3252. }
  3253. //
  3254. // CSI Ps ; Ps f
  3255. // Horizontal and Vertical Position [row;column] (default =
  3256. // [1,1]) (HVP).
  3257. //
  3258. func cmdHVPosition (_ pars: [Int], _ collect: cstring)
  3259. {
  3260. var p = 1
  3261. var q = 1
  3262. if pars.count > 0 {
  3263. p = max (pars [0], 1)
  3264. if (pars.count > 1){
  3265. q = max (pars [1], 1)
  3266. }
  3267. }
  3268. buffer.y = p - 1 + (originMode ? buffer.scrollTop : 0)
  3269. if buffer.y >= rows {
  3270. buffer.y = rows - 1
  3271. }
  3272. buffer.x = q - 1 + (originMode && marginMode ? buffer.marginLeft : 0)
  3273. if buffer.x >= cols {
  3274. buffer.x = cols - 1
  3275. }
  3276. }
  3277. //
  3278. // CSI Pm e Vertical Position Relative (VPR)
  3279. // [rows] (default = [row+1,column])
  3280. // reuse CSI Ps B ?
  3281. //
  3282. func cmdVPositionRelative (_ pars: [Int], _ collect: cstring)
  3283. {
  3284. let p = max (pars.count == 0 ? 1 : pars [0], 1)
  3285. let newY = buffer.y + p
  3286. if newY >= rows {
  3287. buffer.y = rows - 1
  3288. } else {
  3289. buffer.y = newY
  3290. }
  3291. // If the end of the line is hit, prevent this action from wrapping around to the next line.
  3292. if buffer.x >= cols {
  3293. buffer.x -= 1
  3294. }
  3295. }
  3296. //
  3297. // CSI Pm d Vertical Position Absolute (VPA)
  3298. // [row] (default = [1,column])
  3299. //
  3300. func cmdLinePosAbsolute (_ pars: [Int], collect: cstring)
  3301. {
  3302. let p = max (pars.count == 0 ? 1 : pars [0], 1)
  3303. if (p - 1 >= rows) {
  3304. buffer.y = rows - 1
  3305. } else {
  3306. buffer.y = p - 1
  3307. }
  3308. }
  3309. //
  3310. // CSI Ps c Send Device Attributes (Primary DA).
  3311. // Ps = 0 or omitted -> request attributes from terminal. The
  3312. // response depends on the decTerminalID resource setting.
  3313. // -> CSI ? 1 ; 2 c (``VT100 with Advanced Video Option'')
  3314. // -> CSI ? 1 ; 0 c (``VT101 with No Options'')
  3315. // -> CSI ? 6 c (``VT102'')
  3316. // -> CSI ? 6 0 ; 1 ; 2 ; 6 ; 8 ; 9 ; 1 5 ; c (``VT220'')
  3317. // The VT100-style response parameters do not mean anything by
  3318. // themselves. VT220 parameters do, telling the host what fea-
  3319. // tures the terminal supports:
  3320. // Ps = 1 -> 132-columns.
  3321. // Ps = 2 -> Printer.
  3322. // Ps = 4 -> Sixel graphics
  3323. // Ps = 6 -> Selective erase.
  3324. // Ps = 8 -> User-defined keys.
  3325. // Ps = 9 -> National replacement character sets.
  3326. // Ps = 1 5 -> Technical characters.
  3327. // Ps = 2 2 -> ANSI color, e.g., VT525.
  3328. // Ps = 2 9 -> ANSI text locator (i.e., DEC Locator mode).
  3329. // CSI > Ps c
  3330. // Send Device Attributes (Secondary DA).
  3331. // Ps = 0 or omitted -> request the terminal's identification
  3332. // code. The response depends on the decTerminalID resource set-
  3333. // ting. It should apply only to VT220 and up, but xterm extends
  3334. // this to VT100.
  3335. // -> CSI > Pp ; Pv ; Pc c
  3336. // where Pp denotes the terminal type
  3337. // Pp = 0 -> ``VT100''.
  3338. // Pp = 1 -> ``VT220''.
  3339. // and Pv is the firmware version (for xterm, this was originally
  3340. // the XFree86 patch number, starting with 95). In a DEC termi-
  3341. // nal, Pc indicates the ROM cartridge registration number and is
  3342. // always zero.
  3343. // More information:
  3344. // xterm/charproc.c - line 2012, for more information.
  3345. // vim responds with ^[[?0c or ^[[?1c after the terminal's response (?)
  3346. //
  3347. func cmdSendDeviceAttributes (_ pars: [Int], collect: cstring)
  3348. {
  3349. if pars.count > 0 && pars [0] > 0 {
  3350. log ("SendDeviceAttributes got \(pars) and \(String(cString: collect))")
  3351. return
  3352. }
  3353. if collect == [UInt8 (ascii: ">")] || collect == [UInt8 (ascii: ">"), UInt8 (ascii: "0")] {
  3354. // DA2 Secondary Device Attributes
  3355. if pars.count == 0 || pars [0] == 0 {
  3356. let vt525 = 65 // we identified as a vt525
  3357. let kbd = 1 // PC-style keyboard
  3358. sendResponse(cc.CSI, ">\(vt525);20;\(kbd)c")
  3359. return
  3360. }
  3361. log ("Got a CSI > c with an unknown set of argument")
  3362. return
  3363. }
  3364. // We should use a terminal emulation level, and not rely on the TERM name
  3365. // for now, "xterm" as a part of the name surfaces all the capabilities.
  3366. let name = options.termName
  3367. if collect == [] {
  3368. let termVt525 = 65
  3369. let sixel = options.enableSixelReported ? ";6" : ""
  3370. let cols132 = 1
  3371. let printer = 2
  3372. let decsera = 6
  3373. let horizontalScrolling = 21
  3374. let ansiColor = 22
  3375. // Send Device Attributes (Primary DA).1
  3376. if name.hasPrefix("xterm") {
  3377. sendResponse (cc.CSI, "?\(termVt525)\(sixel);\(cols132);\(printer);\(decsera);\(horizontalScrolling);\(ansiColor)c")
  3378. } else if name.hasPrefix("screen") || name.hasPrefix ("rxvt-unicode") {
  3379. sendResponse (cc.CSI, "?\(cols132);\(printer)c")
  3380. } else if name.hasPrefix ("linux") {
  3381. sendResponse (cc.CSI, "?\(decsera)c")
  3382. }
  3383. } else if collect.count == 1 && collect [0] == UInt8 (ascii: ">") {
  3384. // xterm and urxvt
  3385. // seem to spit this
  3386. // out around ~370 times (?).
  3387. if name.hasPrefix ("xterm") {
  3388. sendResponse (cc.CSI, ">0;276;0c")
  3389. } else if name.hasPrefix ("rxvt-unicode") {
  3390. sendResponse (cc.CSI, ">85;95;0c")
  3391. } else if name.hasPrefix ("linux") {
  3392. // not supported by linux console.
  3393. // linux console echoes parameters.
  3394. sendResponse ("\(pars[0])c")
  3395. } else if name.hasPrefix ("screen") {
  3396. sendResponse (cc.CSI, ">83;40003;0c")
  3397. }
  3398. }
  3399. }
  3400. //
  3401. // CSI Ps b Repeat the preceding graphic character Ps times (REP).
  3402. //
  3403. func cmdRepeatPrecedingCharacter (_ pars: [Int], collect: cstring)
  3404. {
  3405. // Maximum repeat, to avoid a denial of service
  3406. let maxRepeat = cols*rows*2
  3407. let p = min (maxRepeat, max (pars.count == 0 ? 1 : pars [0], 1))
  3408. let line = buffer.lines [buffer.yBase + buffer.y]
  3409. let chData = buffer.x - 1 < 0 ? CharData (attribute: CharData.defaultAttr) : line [buffer.x - 1]
  3410. for _ in 0..<p {
  3411. insertCharacter(chData)
  3412. }
  3413. }
  3414. //
  3415. //CSI Pm a Character Position Relative
  3416. // [columns] (default = [row,col+1]) (HPR)
  3417. //reuse CSI Ps C ?
  3418. //
  3419. func cmdHPositionRelative (_ pars: [Int], collect: cstring)
  3420. {
  3421. let p = max (pars.count == 0 ? 1 : pars [0], 1)
  3422. buffer.x += p
  3423. if buffer.x >= cols {
  3424. buffer.x = cols - 1
  3425. }
  3426. }
  3427. //
  3428. // CSI Pm ` Character Position Absolute
  3429. // [column] (default = [row,1]) (HPA).
  3430. //
  3431. func cmdCharPosAbsolute (_ pars: [Int], collect: cstring)
  3432. {
  3433. let p = max (pars.count == 0 ? 1 : pars [0], 1)
  3434. buffer.x = p - 1
  3435. if buffer.x >= cols {
  3436. buffer.x = cols - 1
  3437. }
  3438. }
  3439. //
  3440. //CSI Ps Z Cursor Backward Tabulation Ps tab stops (default = 1) (CBT).
  3441. //
  3442. func cmdCursorBackwardTab (_ pars: [Int], collect: cstring)
  3443. {
  3444. if buffer.x > cols {
  3445. return
  3446. }
  3447. let p = max (pars.count == 0 ? 1 : pars [0], 1)
  3448. for _ in 0..<p {
  3449. buffer.x = buffer.previousTabStop ()
  3450. }
  3451. }
  3452. //
  3453. // CSI Ps X
  3454. // Erase Ps Character(s) (default = 1) (ECH).
  3455. //
  3456. func cmdEraseChars (_ pars: [Int], collect: cstring)
  3457. {
  3458. let p = max (pars.count == 0 ? 1 : pars [0], 1)
  3459. buffer.lines [buffer.y + buffer.yBase].replaceCells (
  3460. start: buffer.x,
  3461. end: buffer.x + p,
  3462. fillData: CharData (attribute: eraseAttr ()))
  3463. }
  3464. func csiT (_ pars: [Int], collect: cstring)
  3465. {
  3466. if collect.count == 0 {
  3467. cmdScrollDown(pars)
  3468. } else if collect == [UInt8 (ascii: ">")] {
  3469. cmdXtermTitleModeReset(pars)
  3470. }
  3471. }
  3472. //
  3473. // CSI Ps T Scroll down Ps lines (default = 1) (SD).
  3474. //
  3475. func cmdScrollDown (_ pars: [Int])
  3476. {
  3477. let p = min (max (pars.count == 0 ? 1 : pars [0], 1), rows)
  3478. let da = CharData.defaultAttr
  3479. let row = buffer.scrollTop + buffer.yBase
  3480. let columnCount = buffer.marginRight-buffer.marginLeft+1
  3481. let rowCount = buffer.scrollBottom-buffer.scrollTop
  3482. for _ in 0..<p {
  3483. for i in (0..<rowCount).reversed() {
  3484. let src = buffer.lines [row+i]
  3485. let dst = buffer.lines [row+i+1]
  3486. dst.copyFrom(src, srcCol: buffer.marginLeft, dstCol: buffer.marginLeft, len: columnCount)
  3487. }
  3488. let last = buffer.lines [row]
  3489. last.fill (with: CharData (attribute: da), atCol: buffer.marginLeft, len: columnCount)
  3490. }
  3491. // this.maxRange();
  3492. updateRange (startLine: buffer.scrollTop, endLine: buffer.scrollBottom)
  3493. }
  3494. //
  3495. // CSI Ps S Scroll up Ps lines (default = 1) (SU).
  3496. //
  3497. func cmdScrollUp (_ pars: [Int], collect: cstring)
  3498. {
  3499. let p = max (pars.count == 0 ? 1 : pars [0], 1)
  3500. let da = CharData.defaultAttr
  3501. if marginMode {
  3502. let row = buffer.scrollTop + buffer.yBase
  3503. let columnCount = buffer.marginRight-buffer.marginLeft+1
  3504. let rowCount = buffer.scrollBottom-buffer.scrollTop
  3505. for _ in 0..<p {
  3506. for i in 0..<(rowCount) {
  3507. let src = buffer.lines [row+i+1]
  3508. let dst = buffer.lines [row+i]
  3509. dst.copyFrom(src, srcCol: buffer.marginLeft, dstCol: buffer.marginLeft, len: columnCount)
  3510. }
  3511. let last = buffer.lines [row+rowCount]
  3512. last.fill (with: CharData (attribute: da), atCol: buffer.marginLeft, len: columnCount)
  3513. }
  3514. } else {
  3515. for _ in 0..<p {
  3516. buffer.lines.splice (start: buffer.yBase + buffer.scrollTop, deleteCount: 1,
  3517. items: [], change: { line in updateRange (line)})
  3518. buffer.lines.splice (start: buffer.yBase + buffer.scrollBottom, deleteCount: 0,
  3519. items: [buffer.getBlankLine (attribute: da)],
  3520. change: { line in updateRange (line) })
  3521. }
  3522. }
  3523. // this.maxRange();
  3524. updateRange (startLine: buffer.scrollTop, endLine: buffer.scrollBottom)
  3525. }
  3526. //
  3527. // CSI Ps P
  3528. // Delete Ps Character(s) (default = 1) (DCH).
  3529. //
  3530. func cmdDeleteChars (pars: [Int], _ collect: cstring)
  3531. {
  3532. let buffer = self.buffer
  3533. var p = max (pars.count == 0 ? 1 : pars [0], 1)
  3534. if marginMode {
  3535. if buffer.x < buffer.marginLeft || buffer.x > buffer.marginRight {
  3536. return
  3537. }
  3538. if buffer.x + p > buffer.marginRight {
  3539. p = buffer.marginRight - buffer.x + 1
  3540. }
  3541. }
  3542. // buffer.x = buffer.cols is a special case on the edge, we do not delete columns in that boundary
  3543. if buffer.x == buffer.cols {
  3544. return
  3545. }
  3546. buffer.lines [buffer.y + buffer.yBase].deleteCells (
  3547. pos: buffer.x, n: p, rightMargin: marginMode ? buffer.marginRight : cols-1, fillData: CharData (attribute: eraseAttr ()))
  3548. updateRange (buffer.y)
  3549. }
  3550. //
  3551. // CSI Ps M
  3552. // Delete Ps Line(s) (default = 1) (DL).
  3553. //
  3554. func cmdDeleteLines (_ pars: [Int], _ collect: cstring)
  3555. {
  3556. restrictCursor()
  3557. let buffer = self.buffer
  3558. // No point deleting more lines than the available rows, prevents
  3559. // a denial of service caused by very large numbers passed here
  3560. let p = min (buffer.rows+1, max (pars.count == 0 ? 1 : pars [0], 1))
  3561. let row = buffer.y + buffer.yBase
  3562. var j = rows - 1 - buffer.scrollBottom
  3563. j = rows - 1 + buffer.yBase - j
  3564. let ea = eraseAttr ()
  3565. if marginMode {
  3566. if buffer.x >= buffer.marginLeft && buffer.x <= buffer.marginRight {
  3567. let columnCount = buffer.marginRight-buffer.marginLeft+1
  3568. let rowCount = buffer.scrollBottom-buffer.scrollTop
  3569. for _ in 0..<p {
  3570. for i in 0..<(rowCount) {
  3571. let src = buffer.lines [row+i+1]
  3572. let dst = buffer.lines [row+i]
  3573. dst.copyFrom(src, srcCol: buffer.marginLeft, dstCol: buffer.marginLeft, len: columnCount)
  3574. }
  3575. let last = buffer.lines [row+rowCount]
  3576. last.fill (with: CharData (attribute: ea), atCol: buffer.marginLeft, len: columnCount)
  3577. }
  3578. }
  3579. } else {
  3580. if buffer.y >= buffer.scrollTop && buffer.y <= buffer.scrollBottom {
  3581. for _ in 0..<p {
  3582. // test: echo -e '\e[44m\e[1M\e[0m'
  3583. // blankLine(true) - xterm/linux behavior
  3584. buffer.lines.splice (start: row, deleteCount: 1, items: [], change: { line in updateRange (line)})
  3585. buffer.lines.splice (start: j, deleteCount: 0,
  3586. items: [buffer.getBlankLine (attribute: ea)],
  3587. change: { line in updateRange (line)})
  3588. }
  3589. }
  3590. }
  3591. // this.maxRange();
  3592. updateRange (startLine: buffer.y, endLine: buffer.scrollBottom)
  3593. }
  3594. //
  3595. // CSI Ps ' ~
  3596. // Delete Ps Column(s) (default = 1) (DECDC), VT420 and up.
  3597. //
  3598. // @vt: #Y CSI DECDC "Delete Columns" "CSI Ps ' ~" "Delete `Ps` columns at cursor position."
  3599. // DECDC deletes `Ps` times columns at the cursor position for all lines with the scroll margins,
  3600. // moving content to the left. Blank columns are added at the right margin.
  3601. // DECDC has no effect outside the scrolling margins.
  3602. func cmdDeleteColumns (_ pars: [Int], _ collect: cstring)
  3603. {
  3604. let buffer = self.buffer
  3605. if buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop {
  3606. return
  3607. }
  3608. // buffer.x = buffer.cols is a special case on the edge, we do not delete columns in that boundary
  3609. if buffer.x == buffer.cols {
  3610. return
  3611. }
  3612. if marginMode {
  3613. if buffer.x < buffer.marginLeft || buffer.x > buffer.marginRight {
  3614. return
  3615. }
  3616. }
  3617. let p = max (pars.count == 0 ? 1 : pars [0], 1)
  3618. for y in buffer.scrollTop...buffer.scrollBottom {
  3619. let line = buffer.lines [buffer.yBase + y]
  3620. line.deleteCells(pos: buffer.x, n: p, rightMargin: marginMode ? buffer.marginRight : cols-1, fillData: buffer.getNullCell(attribute: eraseAttr()))
  3621. line.isWrapped = false
  3622. }
  3623. updateRange (startLine: buffer.scrollTop, endLine: buffer.scrollBottom)
  3624. }
  3625. //
  3626. // Helper method to reset cells in a terminal row.
  3627. // The cell gets replaced with the eraseChar of the terminal and the isWrapped property is set to false.
  3628. // @param y row index
  3629. //
  3630. func resetBufferLine (y: Int)
  3631. {
  3632. eraseInBufferLine (y: y, start: 0, end: cols, clearWrap: true)
  3633. updateRange(y)
  3634. }
  3635. /**
  3636. * Sends the provided text to the connected backend
  3637. */
  3638. public func sendResponse (text: String)
  3639. {
  3640. tdel?.send (source: self, data: ([UInt8] (text.utf8))[...])
  3641. }
  3642. /**
  3643. * Sends the provided text to the connected backend, takes a variable list of arguments
  3644. * that could be either [UInt8], Strings, or a single UInt8 value.
  3645. */
  3646. public func sendResponse (_ items: Any ...)
  3647. {
  3648. var buffer: [UInt8] = []
  3649. for item in items {
  3650. if let arr = item as? [UInt8] {
  3651. buffer.append(contentsOf: arr)
  3652. } else if let str = item as? String {
  3653. buffer.append (contentsOf: [UInt8] (str.utf8))
  3654. } else if let c = item as? UInt8 {
  3655. buffer.append (c)
  3656. } else {
  3657. log ("Do not know how to handle type \(item)")
  3658. }
  3659. }
  3660. tdel?.send (source: self, data: buffer[...])
  3661. }
  3662. #if DEBUG
  3663. public var silentLog = false
  3664. #else
  3665. public var silentLog = true
  3666. #endif
  3667. func error (_ text: String)
  3668. {
  3669. if !silentLog {
  3670. print("Error: \(text)")
  3671. }
  3672. }
  3673. func log (_ text: String)
  3674. {
  3675. if !silentLog {
  3676. print("Info: \(text)")
  3677. }
  3678. }
  3679. /**
  3680. * Processes the provided byte-array coming from the backend
  3681. */
  3682. public func feed (byteArray: [UInt8])
  3683. {
  3684. parse (buffer: byteArray[...])
  3685. }
  3686. public func feed (text: String)
  3687. {
  3688. parse (buffer: ([UInt8] (text.utf8))[...])
  3689. }
  3690. public func feed (buffer: ArraySlice<UInt8>)
  3691. {
  3692. parse (buffer: buffer)
  3693. }
  3694. public func parse (buffer: ArraySlice<UInt8>)
  3695. {
  3696. parser.parse(data: buffer)
  3697. }
  3698. var dirtyLines: Set<Int> = Set<Int>()
  3699. /**
  3700. * Registers the given line as requiring to be updated by the front-end engine
  3701. *
  3702. * The front-end engine should call `getUpdateRange` to
  3703. * determine which region in the screen needs to be redrawn. This method adds the specified
  3704. * line to the range of modified lines
  3705. *
  3706. * Scrolling tells if this was just issued as part of scrolling which we don't register for the
  3707. * scroll-invariant update ranges.
  3708. */
  3709. func updateRange (_ y: Int, scrolling: Bool = false, updateDirtySet: Bool = true)
  3710. {
  3711. if !scrolling {
  3712. let effectiveY = buffer.yDisp + y
  3713. if effectiveY >= 0 {
  3714. if effectiveY < scrollInvariantRefreshStart {
  3715. scrollInvariantRefreshStart = effectiveY
  3716. }
  3717. if effectiveY > scrollInvariantRefreshEnd {
  3718. scrollInvariantRefreshEnd = effectiveY
  3719. }
  3720. }
  3721. }
  3722. if y >= 0 {
  3723. if y < refreshStart {
  3724. refreshStart = y
  3725. }
  3726. if y > refreshEnd {
  3727. refreshEnd = y
  3728. }
  3729. }
  3730. if updateDirtySet {
  3731. dirtyLines.insert (y)
  3732. }
  3733. }
  3734. func updateRange (startLine: Int, endLine: Int, scrolling: Bool = false)
  3735. {
  3736. updateRange (startLine, scrolling: scrolling, updateDirtySet: false)
  3737. updateRange (endLine, scrolling: scrolling, updateDirtySet: false)
  3738. for line in min(startLine,endLine)...max(startLine,endLine) {
  3739. dirtyLines.insert (line)
  3740. }
  3741. }
  3742. public func updateFullScreen ()
  3743. {
  3744. refreshStart = 0
  3745. refreshEnd = rows
  3746. scrollInvariantRefreshStart = buffer.yDisp
  3747. scrollInvariantRefreshEnd = buffer.yDisp + rows
  3748. for line in 0...rows {
  3749. dirtyLines.insert (line)
  3750. }
  3751. }
  3752. /**
  3753. * Returns the starting and ending lines that need to be redrawn, or nil
  3754. * if no part of the screen needs to be updated. Alternatively, you can
  3755. * get a Set<Int> with the changed lines by calling `changedLines()`.
  3756. *
  3757. * UI toolkits should call `clearUpdateRange` to reset these changes
  3758. * after they have used this information, so that new changes only reflect
  3759. * the actual changes.
  3760. */
  3761. public func getUpdateRange () -> (startY: Int, endY: Int)?
  3762. {
  3763. if refreshEnd == -1 && refreshStart == Int.max {
  3764. //print ("Emtpy update range")
  3765. return nil
  3766. }
  3767. //print ("Update: \(refreshStart) \(refreshEnd)")
  3768. return (refreshStart, refreshEnd)
  3769. }
  3770. /**
  3771. * Returns a set containing the lines that have been modified, the
  3772. * returned set is not sorted.
  3773. *
  3774. * UI toolkits should call `clearUpdateRange` to reset these changes
  3775. * after they have used this information, so that new changes only reflect
  3776. * the actual changes.
  3777. */
  3778. public func changedLines () -> Set<Int>
  3779. {
  3780. return dirtyLines
  3781. }
  3782. /**
  3783. * Check for payload identifiers that are not in use and stop retaining their payload,
  3784. * to avoid accumulting memory for images and URLs that are no longer visible or
  3785. * available by scrolling.
  3786. */
  3787. public func garbageCollectPayload() {
  3788. // stop right away if there is nothing to collect
  3789. if TinyAtom.lastCollected == TinyAtom.lastUsed {
  3790. return
  3791. }
  3792. // check all atoms used in both buffers
  3793. var used = Set<UInt16>()
  3794. for buffer in [buffers.normal, buffers.alt] {
  3795. for line in buffer._lines.array {
  3796. if let array = line?.data {
  3797. for data in array {
  3798. let code = data.payload.code
  3799. if code > 0 {
  3800. used.insert(code)
  3801. }
  3802. }
  3803. }
  3804. }
  3805. }
  3806. // since we create atoms in order we expect them to run out of use
  3807. // in order as well and stop with first atom that is still in use
  3808. for code in UInt16(TinyAtom.lastCollected + 1)...UInt16(TinyAtom.lastUsed) {
  3809. if used.contains(code) {
  3810. // code still in use
  3811. break
  3812. }
  3813. TinyAtom.lastCollected = Int(code)
  3814. TinyAtom.release(code: code)
  3815. }
  3816. }
  3817. /**
  3818. * Returns the starting and ending lines that need to be redrawn, or nil
  3819. * if no part of the screen needs to be updated.
  3820. *
  3821. * This is different from getUpdateRange() in that lines are from start of scroll back,
  3822. * not what the terminal has visible right now.
  3823. */
  3824. public func getScrollInvariantUpdateRange () -> (startY: Int, endY: Int)?
  3825. {
  3826. if scrollInvariantRefreshEnd == -1 && scrollInvariantRefreshStart == Int.max {
  3827. //print ("Emtpy update range")
  3828. return nil
  3829. }
  3830. //print ("Update: \(scrollInvariantRefreshStart) \(scrollInvariantRefreshEnd)")
  3831. return (scrollInvariantRefreshStart, scrollInvariantRefreshEnd)
  3832. }
  3833. /**
  3834. * Clears the state of the pending display redraw region as well as the dirtyLines set.
  3835. */
  3836. public func clearUpdateRange ()
  3837. {
  3838. refreshStart = Int.max
  3839. refreshEnd = -1
  3840. scrollInvariantRefreshStart = Int.max
  3841. scrollInvariantRefreshEnd = -1
  3842. dirtyLines.removeAll()
  3843. }
  3844. /**
  3845. * Zero-based (row, column) of cursor location relative to visible part of display.
  3846. * Returns: a tuple, where the first element contains the column (x) and the second the row (y) where the cursor is.
  3847. */
  3848. public func getCursorLocation() -> (x: Int, y: Int) {
  3849. return (buffer.x, buffer.y)
  3850. }
  3851. /**
  3852. * Returns the uppermost visible row on the terminal buffer
  3853. */
  3854. public func getTopVisibleRow() -> Int {
  3855. return buffer.yDisp
  3856. }
  3857. // ESC c Full Reset (RIS)
  3858. /// This performs a full reset of the terminal, like a soft reset, but additionally resets the buffer conents and scroll area.
  3859. /// for a soft reset see `softReset`
  3860. public func resetToInitialState ()
  3861. {
  3862. options.rows = rows
  3863. options.cols = cols
  3864. let savedCursorHidden = cursorHidden
  3865. setup (isReset: true)
  3866. cursorHidden = savedCursorHidden
  3867. refresh (startRow: 0, endRow: rows-1)
  3868. syncScrollArea ()
  3869. }
  3870. // Support for:
  3871. // ESC 6 Back Index (DECBI) and
  3872. // ESC 9 Forward Index (DECFI)
  3873. func columnIndex (back: Bool)
  3874. {
  3875. let buffer = self.buffer
  3876. let x = buffer.x
  3877. let leftMargin = buffer.marginLeft
  3878. if back {
  3879. if x == leftMargin {
  3880. columnScroll (back: back, at: x)
  3881. } else {
  3882. cursorBackward(count: 1)
  3883. }
  3884. } else {
  3885. let rightMargin = buffer.marginRight
  3886. if x == rightMargin {
  3887. columnScroll (back: back, at: leftMargin)
  3888. } else if x == buffer.cols {
  3889. // on the boundaries, we ignore, test_DECFI_WholeScreenScrolls
  3890. } else {
  3891. cursorForward(count: 1)
  3892. }
  3893. }
  3894. }
  3895. func columnScroll (back: Bool, at: Int)
  3896. {
  3897. if buffer.y < buffer.scrollTop || buffer.y > buffer.scrollBottom || buffer.x < buffer.marginLeft || buffer.x > buffer.marginRight {
  3898. return
  3899. }
  3900. for y in buffer.scrollTop...buffer.scrollBottom {
  3901. let line = buffer.lines [buffer.yBase + y]
  3902. if back {
  3903. line.insertCells(pos: at, n: 1, rightMargin: marginMode ? buffer.marginRight : cols-1, fillData: buffer.getNullCell())
  3904. } else {
  3905. line.deleteCells(pos: at, n: 1, rightMargin: marginMode ? buffer.marginRight : cols-1, fillData: buffer.getNullCell(attribute: eraseAttr()))
  3906. }
  3907. //line.isWrapped = false
  3908. }
  3909. updateRange (buffer.scrollTop)
  3910. updateRange (buffer.scrollBottom)
  3911. }
  3912. // ESC D Index (Index is 0x84) - IND
  3913. func cmdIndex ()
  3914. {
  3915. restrictCursor()
  3916. let buffer = self.buffer
  3917. let newY = buffer.y + 1
  3918. if newY > buffer.scrollBottom {
  3919. scroll ()
  3920. } else {
  3921. buffer.y = newY
  3922. }
  3923. // If the end of the line is hit, prevent this action from wrapping around to the next line
  3924. if buffer.x > cols {
  3925. buffer.x -= 1
  3926. }
  3927. }
  3928. var blankLine: BufferLine = BufferLine(cols: 0)
  3929. public func scroll (isWrapped: Bool = false)
  3930. {
  3931. let buffer = self.buffer
  3932. var newLine = blankLine
  3933. if newLine.count != cols || newLine [0].attribute != eraseAttr () {
  3934. newLine = buffer.getBlankLine (attribute: eraseAttr (), isWrapped: isWrapped)
  3935. blankLine = newLine
  3936. }
  3937. newLine.isWrapped = isWrapped
  3938. let topRow = buffer.yBase + buffer.scrollTop
  3939. let bottomRow = buffer.yBase + buffer.scrollBottom
  3940. if buffer.scrollTop == 0 {
  3941. // Determine whether the buffer is going to be trimmed after insertion.
  3942. let willBufferBeTrimmed = buffer.lines.isFull
  3943. // Insert the line using the fastest method
  3944. if bottomRow == buffer.lines.count - 1 {
  3945. if willBufferBeTrimmed {
  3946. buffer.lines.recycle ()
  3947. } else {
  3948. buffer.lines.push (BufferLine (from: newLine))
  3949. }
  3950. } else {
  3951. buffer.lines.splice (start: bottomRow + 1, deleteCount: 0,
  3952. items: [BufferLine (from: newLine)],
  3953. change: { line in updateRange (line)})
  3954. }
  3955. // Only adjust ybase and ydisp when the buffer is not trimmed
  3956. if !willBufferBeTrimmed {
  3957. buffer.yBase += 1
  3958. // Only scroll the ydisp with ybase if the user has not scrolled up
  3959. if !userScrolling {
  3960. buffer.yDisp += 1
  3961. }
  3962. } else {
  3963. if buffer.hasScrollback {
  3964. buffer.linesTop += 1
  3965. }
  3966. // When the buffer is full and the user has scrolled up, keep the text
  3967. // stable unless ydisp is right at the top
  3968. if userScrolling {
  3969. buffer.yDisp = max (buffer.yDisp - 1, 0)
  3970. }
  3971. }
  3972. } else {
  3973. // scrollTop is non-zero which means no line will be going to the
  3974. // scrollback, instead we can just shift them in-place.
  3975. let scrollRegionHeight = bottomRow - topRow + 1 /*as it's zero-based*/
  3976. if scrollRegionHeight > 1 {
  3977. buffer.lines.shiftElements (start: topRow + 1, count: scrollRegionHeight - 1, offset: -1)
  3978. }
  3979. buffer.lines [bottomRow] = BufferLine (from: newLine)
  3980. }
  3981. // Move the viewport to the bottom of the buffer unless the user is
  3982. // scrolling.
  3983. if !userScrolling {
  3984. buffer.yDisp = buffer.yBase
  3985. }
  3986. //buffer.dump ()
  3987. // Flag rows that need updating
  3988. updateRange (buffer.scrollTop, scrolling: true)
  3989. updateRange (buffer.scrollBottom, scrolling: true)
  3990. if !buffer.hasScrollback {
  3991. updateRange(startLine: buffer.scrollTop, endLine: buffer.scrollBottom)
  3992. }
  3993. /**
  3994. * This event is emitted whenever the terminal is scrolled.
  3995. * The one parameter passed is the new y display position.
  3996. *
  3997. * @event scroll
  3998. */
  3999. tdel?.scrolled(source: self, yDisp: buffer.yDisp)
  4000. }
  4001. public func emitLineFeed ()
  4002. {
  4003. tdel?.linefeed(source: self)
  4004. }
  4005. //
  4006. // ESC n
  4007. // ESC o
  4008. // ESC |
  4009. // ESC }
  4010. // ESC ~
  4011. // DEC mnemonic: LS (https://vt100.net/docs/vt510-rm/LS.html)
  4012. // When you use a locking shift, the character set remains in GL or GR until
  4013. // you use another locking shift. (partly supported)
  4014. //
  4015. func setgLevel (_ v: UInt8)
  4016. {
  4017. gLevel = v
  4018. if let cs = CharSets.all [v] {
  4019. charset = cs
  4020. } else {
  4021. charset = nil
  4022. }
  4023. }
  4024. //
  4025. // ESC % @
  4026. // ESC % G
  4027. // Select default character set. UTF-8 is not supported (string are unicode anyways)
  4028. // therefore ESC % G does the same.
  4029. //
  4030. func cmdSelectDefaultCharset ()
  4031. {
  4032. setgLevel (0)
  4033. setgCharset (0, charset: CharSets.defaultCharset)
  4034. }
  4035. //
  4036. // ESC c
  4037. // DEC mnemonic: RIS (https://vt100.net/docs/vt510-rm/RIS.html)
  4038. // Reset to initial state.
  4039. //
  4040. func cmdReset ()
  4041. {
  4042. parser.reset ()
  4043. resetToInitialState ()
  4044. }
  4045. //
  4046. // ESC >
  4047. // DEC mnemonic: DECKPNM (https://vt100.net/docs/vt510-rm/DECKPNM.html)
  4048. // Enables the keypad to send numeric characters to the host.
  4049. //
  4050. func cmdKeypadNumericMode ()
  4051. {
  4052. applicationKeypad = false
  4053. syncScrollArea ()
  4054. }
  4055. //
  4056. // ESC =
  4057. // DEC mnemonic: DECKPAM (https://vt100.net/docs/vt510-rm/DECKPAM.html)
  4058. // Enables the numeric keypad to send application sequences to the host.
  4059. //
  4060. func cmdKeypadApplicationMode ()
  4061. {
  4062. applicationKeypad = true
  4063. syncScrollArea ()
  4064. }
  4065. func eraseAttr () -> Attribute
  4066. {
  4067. Attribute (fg: CharData.defaultAttr.fg, bg: curAttr.bg, style: CharData.defaultAttr.style)
  4068. }
  4069. func setgCharset (_ v: UInt8, charset: [UInt8: String]?)
  4070. {
  4071. CharSets.all [v] = charset
  4072. if gLevel == v {
  4073. self.charset = charset
  4074. }
  4075. }
  4076. public func resize (cols: Int, rows: Int)
  4077. {
  4078. let newCols = max (cols, MINIMUM_COLS)
  4079. let newRows = max (rows, MINIMUM_ROWS)
  4080. if newCols == self.cols && newRows == self.rows {
  4081. return
  4082. }
  4083. let oldCols = self.cols
  4084. buffers.resize(newColumns: newCols, newRows: newRows)
  4085. self.cols = newCols
  4086. self.rows = newRows
  4087. options.cols = newCols
  4088. options.rows = newRows
  4089. buffers.normal.setupTabStops (index: oldCols)
  4090. buffers.alt.setupTabStops (index: oldCols)
  4091. refresh (startRow: 0, endRow: self.rows - 1)
  4092. }
  4093. func syncScrollArea ()
  4094. {
  4095. // This should call the viewport sync-scroll-area
  4096. }
  4097. /**
  4098. * Registers that the region between startRow and endRow was modified and needs to be updated by the
  4099. */
  4100. public func refresh (startRow: Int, endRow: Int)
  4101. {
  4102. // TO BE HONEST - This probably should not be called directly,
  4103. // instead the view shoudl after feeding data, determine if there is a need
  4104. // to refresh based on the parameters provided for refresh ranges, and then
  4105. // update, to avoid the backend rtiggering this multiple times.
  4106. updateRange (startLine: startRow, endLine: endRow)
  4107. }
  4108. public func showCursor ()
  4109. {
  4110. if cursorHidden == false {
  4111. return
  4112. }
  4113. cursorHidden = false
  4114. //refresh (startRow: buffer.y, endRow: buffer.y)
  4115. tdel?.showCursor (source: self)
  4116. }
  4117. public func hideCursor ()
  4118. {
  4119. if cursorHidden {
  4120. return
  4121. }
  4122. cursorHidden = true
  4123. tdel?.hideCursor(source: self)
  4124. }
  4125. // Encode button and position to characters
  4126. func encodeMouseUtf (data: inout [UInt8], ch: Int)
  4127. {
  4128. if ch == 2047 {
  4129. data.append(0)
  4130. return
  4131. }
  4132. if ch < 127 {
  4133. data.append (UInt8(ch))
  4134. } else {
  4135. let rc = ch > 2047 ? 2047 : ch
  4136. data.append (0xc0 | (UInt8 (rc >> 6)))
  4137. data.append (0x80 | (UInt8 (rc & 0x3f)))
  4138. }
  4139. }
  4140. /**
  4141. * Encodes the button action in the format expected by the client
  4142. * - Parameter button: The button to encode
  4143. * - Parameter release: `true` if this is a mouse release event
  4144. * - Parameter shift: `true` if the shift key is pressed
  4145. * - Parameter meta: `true` if the meta/alt key is pressed
  4146. * - Parameter control: `true` if the control key is pressed
  4147. * - Returns: the encoded value
  4148. */
  4149. public func encodeButton (button: Int, release: Bool, shift: Bool, meta: Bool, control: Bool) -> Int
  4150. {
  4151. var value: Int
  4152. if release {
  4153. value = 3
  4154. } else {
  4155. switch (button) {
  4156. case 0:
  4157. value = 0
  4158. case 1:
  4159. value = 1
  4160. case 2:
  4161. value = 2
  4162. case 4:
  4163. value = 64
  4164. case 5:
  4165. value = 65
  4166. default:
  4167. value = 0
  4168. }
  4169. }
  4170. if mouseMode.sendsModifiers() {
  4171. if shift {
  4172. value |= 4
  4173. }
  4174. if meta {
  4175. value |= 8
  4176. }
  4177. if control {
  4178. value |= 16
  4179. }
  4180. }
  4181. return value
  4182. }
  4183. /**
  4184. * Sends a mouse event for a specific button at the specific location
  4185. * - Parameter buttonFlags: Button flags encoded in Cb mode.
  4186. * - Parameter x: X coordinate for the event
  4187. * - Parameter y: Y coordinate for the event
  4188. */
  4189. public func sendEvent (buttonFlags: Int, x: Int, y: Int)
  4190. {
  4191. //print ("got \(mouseProtocol)")
  4192. switch mouseProtocol {
  4193. case .x10:
  4194. sendResponse(cc.CSI, "M", [UInt8(buttonFlags+32), min (UInt8(255), UInt8(32 + x+1)), min (UInt8(255), UInt8(32+y+1))])
  4195. case .sgr:
  4196. let bflags : Int = ((buttonFlags & 3) == 3) ? (buttonFlags & ~3) : buttonFlags
  4197. let m = ((buttonFlags & 3) == 3) ? "m" : "M"
  4198. sendResponse(cc.CSI, "<\(bflags);\(x+1);\(y+1)\(m)")
  4199. case .urxvt:
  4200. sendResponse(cc.CSI, "\(buttonFlags+32);\(x+1);\(y+1)M");
  4201. case .utf8:
  4202. var buffer: [UInt8] = [UInt8 (ascii: "M")]
  4203. encodeMouseUtf(data: &buffer, ch: buttonFlags+32)
  4204. encodeMouseUtf (data: &buffer, ch: x+33)
  4205. encodeMouseUtf (data: &buffer, ch: y+33)
  4206. sendResponse(cc.CSI, buffer)
  4207. }
  4208. }
  4209. /**
  4210. * Sends a mouse motion event for a specific button at the specific location
  4211. * - Parameter buttonFlags: Button flags encoded in Cb mode.
  4212. * - Parameter x: X coordinate for the event
  4213. * - Parameter y: Y coordinate for the event
  4214. */
  4215. public func sendMotion (buttonFlags: Int, x: Int, y: Int)
  4216. {
  4217. sendEvent(buttonFlags: buttonFlags+32, x: x, y: y)
  4218. }
  4219. static var matchColorCache : [Int:Int] = [:]
  4220. func matchColor (_ r1: Int, _ g1: Int, _ b1: Int) -> Int32
  4221. {
  4222. // TODO
  4223. abort ()
  4224. }
  4225. var terminalTitle: String = "" // The Xterm terminal title
  4226. var iconTitle: String = "" // The Xterm minimized window title
  4227. var terminalTitleStack: [String] = []
  4228. var terminalIconStack: [String] = []
  4229. public func setTitle (text: String)
  4230. {
  4231. terminalTitle = text
  4232. tdel?.setTerminalTitle(source: self, title: text)
  4233. }
  4234. public func setIconTitle (text: String)
  4235. {
  4236. iconTitle = text
  4237. tdel?.setTerminalIconTitle(source: self, title: text)
  4238. }
  4239. func reverseIndex ()
  4240. {
  4241. restrictCursor()
  4242. if buffer.y == buffer.scrollTop {
  4243. // possibly move the code below to term.reverseScroll()
  4244. // test: echo -ne '\e[1;1H\e[44m\eM\e[0m'
  4245. // blankLine(true) is xterm/linux behavior
  4246. let scrollRegionHeight = buffer.scrollBottom - buffer.scrollTop
  4247. buffer.lines.shiftElements (start: buffer.y + buffer.yBase, count: scrollRegionHeight, offset: 1)
  4248. buffer.lines [buffer.y + buffer.yBase] = buffer.getBlankLine (attribute: eraseAttr ())
  4249. updateRange (startLine: buffer.scrollTop, endLine: buffer.scrollBottom)
  4250. } else if buffer.y > 0 {
  4251. buffer.y -= 1
  4252. }
  4253. }
  4254. /**
  4255. * Provides a baseline set of environment variables that would be useful to run the terminal,
  4256. * you can customzie these accordingly.
  4257. * - Parameters:
  4258. * - termName: desired name for the terminal, if set to nil (the default), it sets it to xterm-256color
  4259. * - trueColor: if set to true, sets the COLORTERM variable to truecolor,
  4260. * - Returns: an array of default environment variables that include TERM set to the specified value, or xterm-256color,
  4261. * and if trueColor is true, COLORTERM=truecolor, the LANG=en_US.UTF-8 and it mirrors the currently set values
  4262. * for LOGNAME, USER, DISPLAY, LC_TYPE, USER and HOME.
  4263. */
  4264. public static func getEnvironmentVariables (termName: String? = nil, trueColor: Bool = true) -> [String]
  4265. {
  4266. var l : [String] = []
  4267. let t = termName == nil ? "xterm-256color" : termName!
  4268. l.append ("TERM=\(t)")
  4269. if trueColor {
  4270. l.append ("COLORTERM=truecolor")
  4271. }
  4272. // Without this, tools like "vi" produce sequences that are not UTF-8 friendly
  4273. l.append ("LANG=en_US.UTF-8")
  4274. let env = ProcessInfo.processInfo.environment
  4275. for x in ["LOGNAME", "USER", "DISPLAY", "LC_TYPE", "USER", "HOME" /* "PATH" */ ] {
  4276. if env.keys.contains(x) {
  4277. l.append ("\(x)=\(env[x]!)")
  4278. }
  4279. }
  4280. return l
  4281. }
  4282. /// Specified the kind of buffer is being requested from the terminal
  4283. public enum BufferKind {
  4284. /// The currently active buffer (can be either normal or alt)
  4285. case active
  4286. /// The normal buffer, regardless of which buffer is active
  4287. case normal
  4288. /// The alternate buffer, regardless of which buffer is active
  4289. case alt
  4290. }
  4291. func bufferFromKind (kind: BufferKind) -> Buffer
  4292. {
  4293. switch kind {
  4294. case .active:
  4295. return buffers.active
  4296. case .normal:
  4297. return buffers.normal
  4298. case .alt:
  4299. return buffers.alt
  4300. }
  4301. }
  4302. /// Returns the contents of the specified terminal buffer encoded as UTF8 in the provided Data buffer
  4303. /// - Parameter kind: which buffer to retrive the data for
  4304. /// - Parameter encoding: which encoding to use for the returned value, defaults to utf8
  4305. public func getBufferAsData (kind: BufferKind = .active, encoding: String.Encoding = .utf8) -> Data
  4306. {
  4307. var result = Data()
  4308. let b = bufferFromKind(kind: kind)
  4309. let newLine = Data([10])
  4310. for row in 0..<b.lines.count {
  4311. let bufferLine = b.lines [row]
  4312. let str = bufferLine.translateToString(trimRight: true)
  4313. if let encoded = str.data(using: encoding) {
  4314. result.append (encoded)
  4315. result.append (newLine)
  4316. }
  4317. }
  4318. return result
  4319. }
  4320. }
  4321. // Default implementations
  4322. public extension TerminalDelegate {
  4323. func cursorStyleChanged (source: Terminal, newStyle: CursorStyle)
  4324. {
  4325. // Do nothing
  4326. }
  4327. func setTerminalTitle (source: Terminal, title: String) {
  4328. // Do nothing
  4329. }
  4330. func setTerminalIconTitle (source: Terminal, title: String) {
  4331. // nothing
  4332. }
  4333. func scrolled(source: Terminal, yDisp: Int) {
  4334. // nothing
  4335. }
  4336. func linefeed(source: Terminal) {
  4337. // nothing
  4338. }
  4339. func bufferActivated(source: Terminal) {
  4340. // nothing
  4341. }
  4342. func windowCommand(source: Terminal, command: Terminal.WindowManipulationCommand) -> [UInt8]? {
  4343. // no special handling
  4344. return nil
  4345. }
  4346. func sizeChanged(source: Terminal) {
  4347. // nothing
  4348. }
  4349. func bell (source: Terminal){
  4350. // nothing
  4351. }
  4352. func isProcessTrusted (source: Terminal) -> Bool {
  4353. return true
  4354. }
  4355. func selectionChanged (source: Terminal){
  4356. // nothing
  4357. }
  4358. func showCursor(source: Terminal) {
  4359. // nothing
  4360. }
  4361. func hideCursor(source: Terminal) {
  4362. // nothing
  4363. }
  4364. func mouseModeChanged(source: Terminal) {
  4365. }
  4366. func hostCurrentDirectoryUpdated (source: Terminal) {
  4367. }
  4368. func hostCurrentDocumentUpdated (source: Terminal) {
  4369. }
  4370. func colorChanged (source: Terminal, idx: Int?) {
  4371. }
  4372. func getColors (source: Terminal) -> (foreground: Color, background: Color)
  4373. {
  4374. return (source.foregroundColor, source.backgroundColor)
  4375. }
  4376. func setForegroundColor (source: Terminal, color: Color)
  4377. {
  4378. source.foregroundColor = color
  4379. }
  4380. func setBackgroundColor (source: Terminal, color: Color)
  4381. {
  4382. source.backgroundColor = color
  4383. }
  4384. func iTermContent (source: Terminal, content: ArraySlice<UInt8>) {
  4385. }
  4386. func clipboardCopy(source: Terminal, content: Data) {
  4387. }
  4388. func notify(source: Terminal, title: String, body: String) {
  4389. }
  4390. func createImageFromBitmap (source: Terminal, bytes: inout [UInt8], width: Int, height: Int) {
  4391. }
  4392. func createImage (source: Terminal, data: Data, width: ImageSizeRequest, height: ImageSizeRequest, preserveAspectRatio: Bool) {
  4393. }
  4394. }