terminal.c 293 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123
  1. /*
  2. * Terminal emulator.
  3. */
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <ctype.h>
  7. #include <limits.h>
  8. #include <wchar.h>
  9. #include <time.h>
  10. #include <assert.h>
  11. #include "putty.h"
  12. #include "terminal.h"
  13. #define VT52_PLUS
  14. #define CL_ANSIMIN 0x0001 /* Codes in all ANSI like terminals. */
  15. #define CL_VT100 0x0002 /* VT100 */
  16. #define CL_VT100AVO 0x0004 /* VT100 +AVO; 132x24 (not 132x14) & attrs */
  17. #define CL_VT102 0x0008 /* VT102 */
  18. #define CL_VT220 0x0010 /* VT220 */
  19. #define CL_VT320 0x0020 /* VT320 */
  20. #define CL_VT420 0x0040 /* VT420 */
  21. #define CL_VT510 0x0080 /* VT510, NB VT510 includes ANSI */
  22. #define CL_VT340TEXT 0x0100 /* VT340 extensions that appear in the VT420 */
  23. #define CL_SCOANSI 0x1000 /* SCOANSI not in ANSIMIN. */
  24. #define CL_ANSI 0x2000 /* ANSI ECMA-48 not in the VT100..VT420 */
  25. #define CL_OTHER 0x4000 /* Others, Xterm, linux, putty, dunno, etc */
  26. #define TM_VT100 (CL_ANSIMIN|CL_VT100)
  27. #define TM_VT100AVO (TM_VT100|CL_VT100AVO)
  28. #define TM_VT102 (TM_VT100AVO|CL_VT102)
  29. #define TM_VT220 (TM_VT102|CL_VT220)
  30. #define TM_VTXXX (TM_VT220|CL_VT340TEXT|CL_VT510|CL_VT420|CL_VT320)
  31. #define TM_SCOANSI (CL_ANSIMIN|CL_SCOANSI)
  32. #define TM_PUTTY (0xFFFF)
  33. #define UPDATE_DELAY ((TICKSPERSEC+49)/50)/* ticks to defer window update */
  34. #define TBLINK_DELAY ((TICKSPERSEC*9+19)/20)/* ticks between text blinks*/
  35. #define CBLINK_DELAY (CURSORBLINK) /* ticks between cursor blinks */
  36. #define VBELL_DELAY (VBELL_TIMEOUT) /* visual bell timeout in ticks */
  37. #define compatibility(x) \
  38. if ( ((CL_##x)&term->compatibility_level) == 0 ) { \
  39. term->termstate=TOPLEVEL; \
  40. break; \
  41. }
  42. #define compatibility2(x,y) \
  43. if ( ((CL_##x|CL_##y)&term->compatibility_level) == 0 ) { \
  44. term->termstate=TOPLEVEL; \
  45. break; \
  46. }
  47. #define has_compat(x) ( ((CL_##x)&term->compatibility_level) != 0 )
  48. static const char *const EMPTY_WINDOW_TITLE = "";
  49. static const char sco2ansicolour[] = { 0, 4, 2, 6, 1, 5, 3, 7 };
  50. #define sel_nl_sz (sizeof(sel_nl)/sizeof(wchar_t))
  51. static const wchar_t sel_nl[] = SEL_NL;
  52. /* forward declaration */
  53. static void term_userpass_state_free(struct term_userpass_state *s);
  54. /*
  55. * Fetch the character at a particular position in a line array,
  56. * for purposes of `wordtype'. The reason this isn't just a simple
  57. * array reference is that if the character we find is UCSWIDE,
  58. * then we must look one space further to the left.
  59. */
  60. #define UCSGET(a, x) \
  61. ( (x)>0 && (a)[(x)].chr == UCSWIDE ? (a)[(x)-1].chr : (a)[(x)].chr )
  62. /*
  63. * Detect the various aliases of U+0020 SPACE.
  64. */
  65. #define IS_SPACE_CHR(chr) \
  66. ((chr) == 0x20 || (DIRECT_CHAR(chr) && ((chr) & 0xFF) == 0x20))
  67. /*
  68. * Spot magic CSETs.
  69. */
  70. #define CSET_OF(chr) (DIRECT_CHAR(chr)||DIRECT_FONT(chr) ? (chr)&CSET_MASK : 0)
  71. /*
  72. * Internal prototypes.
  73. */
  74. static void resizeline(Terminal *, termline *, int);
  75. static termline *lineptr(Terminal *, int, int);
  76. static void check_line_size(Terminal *, termline *);
  77. static void do_paint(Terminal *);
  78. static void erase_lots(Terminal *, bool, bool, bool);
  79. static int find_last_nonempty_line(Terminal *, tree234 *);
  80. static void swap_screen(Terminal *, int, bool, bool);
  81. static void update_sbar(Terminal *);
  82. static void deselect(Terminal *);
  83. static void term_print_finish(Terminal *);
  84. static void scroll(Terminal *, int, int, int, bool);
  85. static void parse_optionalrgb(optionalrgb *out, unsigned *values);
  86. static void term_added_data(Terminal *term, bool);
  87. static void term_update_raw_mouse_mode(Terminal *term);
  88. static void term_out_cb(void *);
  89. static termline *newtermline(Terminal *term, int cols, bool bce)
  90. {
  91. termline *line;
  92. int j;
  93. line = snew(termline);
  94. line->chars = snewn(cols, termchar);
  95. for (j = 0; j < cols; j++)
  96. line->chars[j] = (bce ? term->erase_char : term->basic_erase_char);
  97. line->cols = line->size = cols;
  98. line->lattr = LATTR_NORM;
  99. line->trusted = false;
  100. line->temporary = false;
  101. line->cc_free = 0;
  102. return line;
  103. }
  104. static void freetermline(termline *line)
  105. {
  106. if (line) {
  107. sfree(line->chars);
  108. sfree(line);
  109. }
  110. }
  111. void term_release_line(termline *line)
  112. {
  113. if (line->temporary)
  114. freetermline(line);
  115. }
  116. const int colour_indices_conf_to_oscp[CONF_NCOLOURS] = {
  117. #define COLOUR_ENTRY(id,name) OSCP_COLOUR_##id,
  118. CONF_COLOUR_LIST(COLOUR_ENTRY)
  119. #undef COLOUR_ENTRY
  120. };
  121. const int colour_indices_conf_to_osc4[CONF_NCOLOURS] = {
  122. #define COLOUR_ENTRY(id,name) OSC4_COLOUR_##id,
  123. CONF_COLOUR_LIST(COLOUR_ENTRY)
  124. #undef COLOUR_ENTRY
  125. };
  126. const int colour_indices_oscp_to_osc4[OSCP_NCOLOURS] = {
  127. #define COLOUR_ENTRY(id) OSC4_COLOUR_##id,
  128. OSCP_COLOUR_LIST(COLOUR_ENTRY)
  129. #undef COLOUR_ENTRY
  130. };
  131. #ifdef TERM_CC_DIAGS
  132. /*
  133. * Diagnostic function: verify that a termline has a correct
  134. * combining character structure.
  135. *
  136. * This is a performance-intensive check, so it's no longer enabled
  137. * by default.
  138. */
  139. static void cc_check(termline *line)
  140. {
  141. unsigned char *flags;
  142. int i, j;
  143. assert(line->size >= line->cols);
  144. flags = snewn(line->size, unsigned char);
  145. for (i = 0; i < line->size; i++)
  146. flags[i] = (i < line->cols);
  147. for (i = 0; i < line->cols; i++) {
  148. j = i;
  149. while (line->chars[j].cc_next) {
  150. j += line->chars[j].cc_next;
  151. assert(j >= line->cols && j < line->size);
  152. assert(!flags[j]);
  153. flags[j] = true;
  154. }
  155. }
  156. j = line->cc_free;
  157. if (j) {
  158. while (1) {
  159. assert(j >= line->cols && j < line->size);
  160. assert(!flags[j]);
  161. flags[j] = true;
  162. if (line->chars[j].cc_next)
  163. j += line->chars[j].cc_next;
  164. else
  165. break;
  166. }
  167. }
  168. j = 0;
  169. for (i = 0; i < line->size; i++)
  170. j += (flags[i] != 0);
  171. assert(j == line->size);
  172. sfree(flags);
  173. }
  174. #endif
  175. static void clear_cc(termline *line, int col);
  176. /*
  177. * Add a combining character to a character cell.
  178. */
  179. static void add_cc(termline *line, int col, unsigned long chr)
  180. {
  181. int newcc;
  182. assert(col >= 0 && col < line->cols);
  183. /*
  184. * Don't add combining characters at all to U+FFFD REPLACEMENT
  185. * CHARACTER. (Partly it's a slightly incoherent idea in the first
  186. * place; mostly, U+FFFD is what we generate if a cell already has
  187. * too many ccs, in which case we want it to be a fixed point when
  188. * further ccs are added.)
  189. */
  190. if (line->chars[col].chr == 0xFFFD)
  191. return;
  192. /*
  193. * Walk the cc list of the cell in question to find its current
  194. * end point.
  195. */
  196. size_t ncc = 0;
  197. int origcol = col;
  198. while (line->chars[col].cc_next) {
  199. col += line->chars[col].cc_next;
  200. if (++ncc >= CC_LIMIT) {
  201. /*
  202. * There are already too many combining characters in this
  203. * character cell. Change strategy: throw out the entire
  204. * chain and replace the main character with U+FFFD.
  205. *
  206. * (Rationale: extrapolating from UTR #36 section 3.6.2
  207. * suggests the principle that it's better to substitute
  208. * U+FFFD than to _ignore_ input completely. Also, if the
  209. * user copies and pastes an overcombined character cell,
  210. * this way it will clearly indicate that we haven't
  211. * reproduced the writer's original intentions, instead of
  212. * looking as if it was the _writer's_ fault that the 33rd
  213. * cc is missing.)
  214. *
  215. * Per the code above, this will also prevent any further
  216. * ccs from being added to this cell.
  217. */
  218. clear_cc(line, origcol);
  219. line->chars[origcol].chr = 0xFFFD;
  220. return;
  221. }
  222. }
  223. /*
  224. * Extend the cols array if the free list is empty.
  225. */
  226. if (!line->cc_free) {
  227. int n = line->size;
  228. size_t tmpsize = line->size;
  229. sgrowarray(line->chars, tmpsize, tmpsize);
  230. assert(tmpsize <= INT_MAX);
  231. line->size = tmpsize;
  232. line->cc_free = n;
  233. while (n < line->size) {
  234. if (n+1 < line->size)
  235. line->chars[n].cc_next = 1;
  236. else
  237. line->chars[n].cc_next = 0;
  238. n++;
  239. }
  240. }
  241. /*
  242. * `col' now points at the last cc currently in this cell; so
  243. * we simply add another one.
  244. */
  245. newcc = line->cc_free;
  246. if (line->chars[newcc].cc_next)
  247. line->cc_free = newcc + line->chars[newcc].cc_next;
  248. else
  249. line->cc_free = 0;
  250. line->chars[newcc].cc_next = 0;
  251. line->chars[newcc].chr = chr;
  252. line->chars[col].cc_next = newcc - col;
  253. #ifdef TERM_CC_DIAGS
  254. cc_check(line);
  255. #endif
  256. }
  257. /*
  258. * Clear the combining character list in a character cell.
  259. */
  260. static void clear_cc(termline *line, int col)
  261. {
  262. int oldfree, origcol = col;
  263. assert(col >= 0 && col < line->cols);
  264. if (!line->chars[col].cc_next)
  265. return; /* nothing needs doing */
  266. oldfree = line->cc_free;
  267. line->cc_free = col + line->chars[col].cc_next;
  268. while (line->chars[col].cc_next)
  269. col += line->chars[col].cc_next;
  270. if (oldfree)
  271. line->chars[col].cc_next = oldfree - col;
  272. else
  273. line->chars[col].cc_next = 0;
  274. line->chars[origcol].cc_next = 0;
  275. #ifdef TERM_CC_DIAGS
  276. cc_check(line);
  277. #endif
  278. }
  279. /*
  280. * Compare two character cells for equality. Special case required
  281. * in do_paint() where we override what we expect the chr and attr
  282. * fields to be.
  283. */
  284. static bool termchars_equal_override(termchar *a, termchar *b,
  285. unsigned long bchr, unsigned long battr)
  286. {
  287. /* FULL-TERMCHAR */
  288. if (!truecolour_equal(a->truecolour, b->truecolour))
  289. return false;
  290. if (a->chr != bchr)
  291. return false;
  292. if ((a->attr &~ DATTR_MASK) != (battr &~ DATTR_MASK))
  293. return false;
  294. while (a->cc_next || b->cc_next) {
  295. if (!a->cc_next || !b->cc_next)
  296. return false; /* one cc-list ends, other does not */
  297. a += a->cc_next;
  298. b += b->cc_next;
  299. if (a->chr != b->chr)
  300. return false;
  301. }
  302. return true;
  303. }
  304. static bool termchars_equal(termchar *a, termchar *b)
  305. {
  306. return termchars_equal_override(a, b, b->chr, b->attr);
  307. }
  308. /*
  309. * Copy a character cell. (Requires a pointer to the destination
  310. * termline, so as to access its free list.)
  311. */
  312. static void copy_termchar(termline *destline, int x, termchar *src)
  313. {
  314. clear_cc(destline, x);
  315. destline->chars[x] = *src; /* copy everything except cc-list */
  316. destline->chars[x].cc_next = 0; /* and make sure this is zero */
  317. while (src->cc_next) {
  318. src += src->cc_next;
  319. add_cc(destline, x, src->chr);
  320. }
  321. #ifdef TERM_CC_DIAGS
  322. cc_check(destline);
  323. #endif
  324. }
  325. /*
  326. * Move a character cell within its termline.
  327. */
  328. static void move_termchar(termline *line, termchar *dest, termchar *src)
  329. {
  330. /* First clear the cc list from the original char, just in case. */
  331. clear_cc(line, dest - line->chars);
  332. /* Move the character cell and adjust its cc_next. */
  333. *dest = *src; /* copy everything except cc-list */
  334. if (src->cc_next)
  335. dest->cc_next = src->cc_next - (dest-src);
  336. /* Ensure the original cell doesn't have a cc list. */
  337. src->cc_next = 0;
  338. #ifdef TERM_CC_DIAGS
  339. cc_check(line);
  340. #endif
  341. }
  342. #ifndef NO_SCROLLBACK_COMPRESSION
  343. /*
  344. * Compress and decompress a termline into an RLE-based format for
  345. * storing in scrollback. (Since scrollback almost never needs to
  346. * be modified and exists in huge quantities, this is a sensible
  347. * tradeoff, particularly since it allows us to continue adding
  348. * features to the main termchar structure without proportionally
  349. * bloating the terminal emulator's memory footprint unless those
  350. * features are in constant use.)
  351. */
  352. static void makerle(strbuf *b, termline *ldata,
  353. void (*makeliteral)(strbuf *b, termchar *c,
  354. unsigned long *state))
  355. {
  356. int hdrpos, hdrsize, n, prevlen, prevpos, thislen, thispos;
  357. bool prev2;
  358. termchar *c = ldata->chars;
  359. unsigned long state = 0, oldstate;
  360. n = ldata->cols;
  361. hdrpos = b->len;
  362. hdrsize = 0;
  363. put_byte(b, 0);
  364. prevlen = prevpos = 0;
  365. prev2 = false;
  366. while (n-- > 0) {
  367. thispos = b->len;
  368. makeliteral(b, c++, &state);
  369. thislen = b->len - thispos;
  370. if (thislen == prevlen &&
  371. !memcmp(b->u + prevpos, b->u + thispos, thislen)) {
  372. /*
  373. * This literal precisely matches the previous one.
  374. * Turn it into a run if it's worthwhile.
  375. *
  376. * With one-byte literals, it costs us two bytes to
  377. * encode a run, plus another byte to write the header
  378. * to resume normal output; so a three-element run is
  379. * neutral, and anything beyond that is unconditionally
  380. * worthwhile. With two-byte literals or more, even a
  381. * 2-run is a win.
  382. */
  383. if (thislen > 1 || prev2) {
  384. int runpos, runlen;
  385. /*
  386. * It's worth encoding a run. Start at prevpos,
  387. * unless hdrsize==0 in which case we can back up
  388. * another one and start by overwriting hdrpos.
  389. */
  390. hdrsize--; /* remove the literal at prevpos */
  391. if (prev2) {
  392. assert(hdrsize > 0);
  393. hdrsize--;
  394. prevpos -= prevlen;/* and possibly another one */
  395. }
  396. if (hdrsize == 0) {
  397. assert(prevpos == hdrpos + 1);
  398. runpos = hdrpos;
  399. strbuf_shrink_to(b, prevpos+prevlen);
  400. } else {
  401. memmove(b->u + prevpos+1, b->u + prevpos, prevlen);
  402. runpos = prevpos;
  403. strbuf_shrink_to(b, prevpos+prevlen+1);
  404. /*
  405. * Terminate the previous run of ordinary
  406. * literals.
  407. */
  408. assert(hdrsize >= 1 && hdrsize <= 128);
  409. b->u[hdrpos] = hdrsize - 1;
  410. }
  411. runlen = prev2 ? 3 : 2;
  412. while (n > 0 && runlen < 129) {
  413. int tmppos, tmplen;
  414. tmppos = b->len;
  415. oldstate = state;
  416. makeliteral(b, c, &state);
  417. tmplen = b->len - tmppos;
  418. bool match = tmplen == thislen &&
  419. !memcmp(b->u + runpos+1, b->u + tmppos, tmplen);
  420. strbuf_shrink_to(b, tmppos);
  421. if (!match) {
  422. state = oldstate;
  423. break; /* run over */
  424. }
  425. n--, c++, runlen++;
  426. }
  427. assert(runlen >= 2 && runlen <= 129);
  428. b->u[runpos] = runlen + 0x80 - 2;
  429. hdrpos = b->len;
  430. hdrsize = 0;
  431. put_byte(b, 0);
  432. /* And ensure this run doesn't interfere with the next. */
  433. prevlen = prevpos = 0;
  434. prev2 = false;
  435. continue;
  436. } else {
  437. /*
  438. * Just flag that the previous two literals were
  439. * identical, in case we find a third identical one
  440. * we want to turn into a run.
  441. */
  442. prev2 = true;
  443. prevlen = thislen;
  444. prevpos = thispos;
  445. }
  446. } else {
  447. prev2 = false;
  448. prevlen = thislen;
  449. prevpos = thispos;
  450. }
  451. /*
  452. * This character isn't (yet) part of a run. Add it to
  453. * hdrsize.
  454. */
  455. hdrsize++;
  456. if (hdrsize == 128) {
  457. b->u[hdrpos] = hdrsize - 1;
  458. hdrpos = b->len;
  459. hdrsize = 0;
  460. put_byte(b, 0);
  461. prevlen = prevpos = 0;
  462. prev2 = false;
  463. }
  464. }
  465. /*
  466. * Clean up.
  467. */
  468. if (hdrsize > 0) {
  469. assert(hdrsize <= 128);
  470. b->u[hdrpos] = hdrsize - 1;
  471. } else {
  472. strbuf_shrink_to(b, hdrpos);
  473. }
  474. }
  475. static void makeliteral_chr(strbuf *b, termchar *c, unsigned long *state)
  476. {
  477. /*
  478. * My encoding for characters is UTF-8-like, in that it stores
  479. * 7-bit ASCII in one byte and uses high-bit-set bytes as
  480. * introducers to indicate a longer sequence. However, it's
  481. * unlike UTF-8 in that it doesn't need to be able to
  482. * resynchronise, and therefore I don't want to waste two bits
  483. * per byte on having recognisable continuation characters.
  484. * Also I don't want to rule out the possibility that I may one
  485. * day use values 0x80000000-0xFFFFFFFF for interesting
  486. * purposes, so unlike UTF-8 I need a full 32-bit range.
  487. * Accordingly, here is my encoding:
  488. *
  489. * 00000000-0000007F: 0xxxxxxx (but see below)
  490. * 00000080-00003FFF: 10xxxxxx xxxxxxxx
  491. * 00004000-001FFFFF: 110xxxxx xxxxxxxx xxxxxxxx
  492. * 00200000-0FFFFFFF: 1110xxxx xxxxxxxx xxxxxxxx xxxxxxxx
  493. * 10000000-FFFFFFFF: 11110ZZZ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
  494. *
  495. * (`Z' is like `x' but is always going to be zero since the
  496. * values I'm encoding don't go above 2^32. In principle the
  497. * five-byte form of the encoding could extend to 2^35, and
  498. * there could be six-, seven-, eight- and nine-byte forms as
  499. * well to allow up to 64-bit values to be encoded. But that's
  500. * completely unnecessary for these purposes!)
  501. *
  502. * The encoding as written above would be very simple, except
  503. * that 7-bit ASCII can occur in several different ways in the
  504. * terminal data; sometimes it crops up in the D800 page
  505. * (CSET_ASCII) but at other times it's in the 0000 page (real
  506. * Unicode). Therefore, this encoding is actually _stateful_:
  507. * the one-byte encoding of 00-7F actually indicates `reuse the
  508. * upper three bytes of the last character', and to encode an
  509. * absolute value of 00-7F you need to use the two-byte form
  510. * instead.
  511. */
  512. if ((c->chr & ~0x7F) == *state) {
  513. put_byte(b, (unsigned char)(c->chr & 0x7F));
  514. } else if (c->chr < 0x4000) {
  515. put_byte(b, (unsigned char)(((c->chr >> 8) & 0x3F) | 0x80));
  516. put_byte(b, (unsigned char)(c->chr & 0xFF));
  517. } else if (c->chr < 0x200000) {
  518. put_byte(b, (unsigned char)(((c->chr >> 16) & 0x1F) | 0xC0));
  519. put_uint16(b, c->chr & 0xFFFF);
  520. } else if (c->chr < 0x10000000) {
  521. put_byte(b, (unsigned char)(((c->chr >> 24) & 0x0F) | 0xE0));
  522. put_byte(b, (unsigned char)((c->chr >> 16) & 0xFF));
  523. put_uint16(b, c->chr & 0xFFFF);
  524. } else {
  525. put_byte(b, 0xF0);
  526. put_uint32(b, c->chr);
  527. }
  528. *state = c->chr & ~0xFF;
  529. }
  530. static void makeliteral_attr(strbuf *b, termchar *c, unsigned long *state)
  531. {
  532. /*
  533. * My encoding for attributes is 16-bit-granular and assumes
  534. * that the top bit of the word is never required. I either
  535. * store a two-byte value with the top bit clear (indicating
  536. * just that value), or a four-byte value with the top bit set
  537. * (indicating the same value with its top bit clear).
  538. *
  539. * However, first I permute the bits of the attribute value, so
  540. * that the eight bits of colour (four in each of fg and bg)
  541. * which are never non-zero unless xterm 256-colour mode is in
  542. * use are placed higher up the word than everything else. This
  543. * ensures that attribute values remain 16-bit _unless_ the
  544. * user uses extended colour.
  545. */
  546. unsigned attr, colourbits;
  547. attr = c->attr;
  548. assert(ATTR_BGSHIFT > ATTR_FGSHIFT);
  549. colourbits = (attr >> (ATTR_BGSHIFT + 4)) & 0xF;
  550. colourbits <<= 4;
  551. colourbits |= (attr >> (ATTR_FGSHIFT + 4)) & 0xF;
  552. attr = (((attr >> (ATTR_BGSHIFT + 8)) << (ATTR_BGSHIFT + 4)) |
  553. (attr & ((1 << (ATTR_BGSHIFT + 4))-1)));
  554. attr = (((attr >> (ATTR_FGSHIFT + 8)) << (ATTR_FGSHIFT + 4)) |
  555. (attr & ((1 << (ATTR_FGSHIFT + 4))-1)));
  556. attr |= (colourbits << (32-9));
  557. if (attr < 0x8000) {
  558. put_byte(b, (unsigned char)((attr >> 8) & 0xFF));
  559. put_byte(b, (unsigned char)(attr & 0xFF));
  560. } else {
  561. put_byte(b, (unsigned char)(((attr >> 24) & 0x7F) | 0x80));
  562. put_byte(b, (unsigned char)((attr >> 16) & 0xFF));
  563. put_byte(b, (unsigned char)((attr >> 8) & 0xFF));
  564. put_byte(b, (unsigned char)(attr & 0xFF));
  565. }
  566. }
  567. static void makeliteral_truecolour(strbuf *b, termchar *c, unsigned long *state)
  568. {
  569. /*
  570. * Put the used parts of the colour info into the buffer.
  571. */
  572. put_byte(b, ((c->truecolour.fg.enabled ? 1 : 0) |
  573. (c->truecolour.bg.enabled ? 2 : 0)));
  574. if (c->truecolour.fg.enabled) {
  575. put_byte(b, c->truecolour.fg.r);
  576. put_byte(b, c->truecolour.fg.g);
  577. put_byte(b, c->truecolour.fg.b);
  578. }
  579. if (c->truecolour.bg.enabled) {
  580. put_byte(b, c->truecolour.bg.r);
  581. put_byte(b, c->truecolour.bg.g);
  582. put_byte(b, c->truecolour.bg.b);
  583. }
  584. }
  585. static void makeliteral_cc(strbuf *b, termchar *c, unsigned long *state)
  586. {
  587. /*
  588. * For combining characters, I just encode a bunch of ordinary
  589. * chars using makeliteral_chr, and terminate with a \0
  590. * character (which I know won't come up as a combining char
  591. * itself).
  592. *
  593. * I don't use the stateful encoding in makeliteral_chr.
  594. */
  595. unsigned long zstate;
  596. termchar z;
  597. while (c->cc_next) {
  598. c += c->cc_next;
  599. assert(c->chr != 0);
  600. zstate = 0;
  601. makeliteral_chr(b, c, &zstate);
  602. }
  603. z.chr = 0;
  604. zstate = 0;
  605. makeliteral_chr(b, &z, &zstate);
  606. }
  607. typedef struct compressed_scrollback_line {
  608. size_t len;
  609. /* compressed data follows after this */
  610. } compressed_scrollback_line;
  611. static termline *decompressline_no_free(compressed_scrollback_line *line);
  612. static compressed_scrollback_line *compressline_no_free(termline *ldata)
  613. {
  614. strbuf *b = strbuf_new();
  615. /* Leave space for the header structure */
  616. strbuf_append(b, sizeof(compressed_scrollback_line));
  617. /*
  618. * First, store the column count, 7 bits at a time, least
  619. * significant `digit' first, with the high bit set on all but
  620. * the last.
  621. */
  622. {
  623. int n = ldata->cols;
  624. while (n >= 128) {
  625. put_byte(b, (unsigned char)((n & 0x7F) | 0x80));
  626. n >>= 7;
  627. }
  628. put_byte(b, (unsigned char)(n));
  629. }
  630. /*
  631. * Next store the lattrs; same principle. We add one extra bit to
  632. * this to indicate the trust state of the line.
  633. */
  634. {
  635. int n = ldata->lattr | (ldata->trusted ? 0x10000 : 0);
  636. while (n >= 128) {
  637. put_byte(b, (unsigned char)((n & 0x7F) | 0x80));
  638. n >>= 7;
  639. }
  640. put_byte(b, (unsigned char)(n));
  641. }
  642. /*
  643. * Now we store a sequence of separate run-length encoded
  644. * fragments, each containing exactly as many symbols as there
  645. * are columns in the ldata.
  646. *
  647. * All of these have a common basic format:
  648. *
  649. * - a byte 00-7F indicates that X+1 literals follow it
  650. * - a byte 80-FF indicates that a single literal follows it
  651. * and expects to be repeated (X-0x80)+2 times.
  652. *
  653. * The format of the `literals' varies between the fragments.
  654. */
  655. makerle(b, ldata, makeliteral_chr);
  656. makerle(b, ldata, makeliteral_attr);
  657. makerle(b, ldata, makeliteral_truecolour);
  658. makerle(b, ldata, makeliteral_cc);
  659. size_t linelen = b->len - sizeof(compressed_scrollback_line);
  660. compressed_scrollback_line *line =
  661. (compressed_scrollback_line *)strbuf_to_str(b);
  662. line->len = linelen;
  663. /*
  664. * Diagnostics: ensure that the compressed data really does
  665. * decompress to the right thing.
  666. *
  667. * This is a bit performance-heavy for production code.
  668. */
  669. #ifdef TERM_CC_DIAGS
  670. #ifndef CHECK_SB_COMPRESSION
  671. {
  672. termline *dcl;
  673. int i;
  674. #ifdef DIAGNOSTIC_SB_COMPRESSION
  675. for (i = 0; i < b->len; i++) {
  676. printf(" %02x ", b->data[i]);
  677. }
  678. printf("\n");
  679. #endif
  680. dcl = decompressline_no_free(line);
  681. assert(ldata->cols == dcl->cols);
  682. assert(ldata->lattr == dcl->lattr);
  683. for (i = 0; i < ldata->cols; i++)
  684. assert(termchars_equal(&ldata->chars[i], &dcl->chars[i]));
  685. #ifdef DIAGNOSTIC_SB_COMPRESSION
  686. printf("%d cols (%d bytes) -> %d bytes (factor of %g)\n",
  687. ldata->cols, 4 * ldata->cols, dused,
  688. (double)dused / (4 * ldata->cols));
  689. #endif
  690. freetermline(dcl);
  691. }
  692. #endif
  693. #endif /* TERM_CC_DIAGS */
  694. return line;
  695. }
  696. static compressed_scrollback_line *compressline_and_free(termline *ldata)
  697. {
  698. compressed_scrollback_line *cline = compressline_no_free(ldata);
  699. freetermline(ldata);
  700. return cline;
  701. }
  702. static void readrle(BinarySource *bs, termline *ldata,
  703. void (*readliteral)(BinarySource *bs, termchar *c,
  704. termline *ldata, unsigned long *state))
  705. {
  706. int n = 0;
  707. unsigned long state = 0;
  708. while (n < ldata->cols) {
  709. int hdr = get_byte(bs);
  710. if (hdr >= 0x80) {
  711. /* A run. */
  712. size_t pos = bs->pos, count = hdr + 2 - 0x80;
  713. while (count--) {
  714. assert(n < ldata->cols);
  715. bs->pos = pos;
  716. readliteral(bs, ldata->chars + n, ldata, &state);
  717. n++;
  718. }
  719. } else {
  720. /* Just a sequence of consecutive literals. */
  721. int count = hdr + 1;
  722. while (count--) {
  723. assert(n < ldata->cols);
  724. readliteral(bs, ldata->chars + n, ldata, &state);
  725. n++;
  726. }
  727. }
  728. }
  729. assert(n == ldata->cols);
  730. }
  731. static void readliteral_chr(BinarySource *bs, termchar *c, termline *ldata,
  732. unsigned long *state)
  733. {
  734. int byte;
  735. /*
  736. * 00000000-0000007F: 0xxxxxxx
  737. * 00000080-00003FFF: 10xxxxxx xxxxxxxx
  738. * 00004000-001FFFFF: 110xxxxx xxxxxxxx xxxxxxxx
  739. * 00200000-0FFFFFFF: 1110xxxx xxxxxxxx xxxxxxxx xxxxxxxx
  740. * 10000000-FFFFFFFF: 11110ZZZ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
  741. */
  742. byte = get_byte(bs);
  743. if (byte < 0x80) {
  744. c->chr = byte | *state;
  745. } else if (byte < 0xC0) {
  746. c->chr = (byte &~ 0xC0) << 8;
  747. c->chr |= get_byte(bs);
  748. } else if (byte < 0xE0) {
  749. c->chr = (byte &~ 0xE0) << 16;
  750. c->chr |= get_uint16(bs);
  751. } else if (byte < 0xF0) {
  752. c->chr = (byte &~ 0xF0) << 24;
  753. c->chr |= get_byte(bs) << 16;
  754. c->chr |= get_uint16(bs);
  755. } else {
  756. assert(byte == 0xF0);
  757. c->chr = get_uint32(bs);
  758. }
  759. *state = c->chr & ~0xFF;
  760. }
  761. static void readliteral_attr(BinarySource *bs, termchar *c, termline *ldata,
  762. unsigned long *state)
  763. {
  764. unsigned val, attr, colourbits;
  765. val = get_uint16(bs);
  766. if (val >= 0x8000) {
  767. val &= ~0x8000;
  768. val <<= 16;
  769. val |= get_uint16(bs);
  770. }
  771. colourbits = (val >> (32-9)) & 0xFF;
  772. attr = (val & ((1<<(32-9))-1));
  773. attr = (((attr >> (ATTR_FGSHIFT + 4)) << (ATTR_FGSHIFT + 8)) |
  774. (attr & ((1 << (ATTR_FGSHIFT + 4))-1)));
  775. attr = (((attr >> (ATTR_BGSHIFT + 4)) << (ATTR_BGSHIFT + 8)) |
  776. (attr & ((1 << (ATTR_BGSHIFT + 4))-1)));
  777. attr |= (colourbits >> 4) << (ATTR_BGSHIFT + 4);
  778. attr |= (colourbits & 0xF) << (ATTR_FGSHIFT + 4);
  779. c->attr = attr;
  780. }
  781. static void readliteral_truecolour(
  782. BinarySource *bs, termchar *c, termline *ldata, unsigned long *state)
  783. {
  784. int flags = get_byte(bs);
  785. if (flags & 1) {
  786. c->truecolour.fg.enabled = true;
  787. c->truecolour.fg.r = get_byte(bs);
  788. c->truecolour.fg.g = get_byte(bs);
  789. c->truecolour.fg.b = get_byte(bs);
  790. } else {
  791. c->truecolour.fg = optionalrgb_none;
  792. }
  793. if (flags & 2) {
  794. c->truecolour.bg.enabled = true;
  795. c->truecolour.bg.r = get_byte(bs);
  796. c->truecolour.bg.g = get_byte(bs);
  797. c->truecolour.bg.b = get_byte(bs);
  798. } else {
  799. c->truecolour.bg = optionalrgb_none;
  800. }
  801. }
  802. static void readliteral_cc(BinarySource *bs, termchar *c, termline *ldata,
  803. unsigned long *state)
  804. {
  805. termchar n;
  806. unsigned long zstate;
  807. int x = c - ldata->chars;
  808. c->cc_next = 0;
  809. while (1) {
  810. zstate = 0;
  811. readliteral_chr(bs, &n, ldata, &zstate);
  812. if (!n.chr)
  813. break;
  814. add_cc(ldata, x, n.chr);
  815. }
  816. }
  817. static termline *decompressline_no_free(compressed_scrollback_line *line)
  818. {
  819. int ncols, byte, shift;
  820. BinarySource bs[1];
  821. termline *ldata;
  822. BinarySource_BARE_INIT(bs, line+1, line->len);
  823. /*
  824. * First read in the column count.
  825. */
  826. ncols = shift = 0;
  827. do {
  828. byte = get_byte(bs);
  829. ncols |= (byte & 0x7F) << shift;
  830. shift += 7;
  831. } while (byte & 0x80);
  832. /*
  833. * Now create the output termline.
  834. */
  835. ldata = snew(termline);
  836. ldata->chars = snewn(ncols, termchar);
  837. ldata->cols = ldata->size = ncols;
  838. ldata->temporary = true;
  839. ldata->cc_free = 0;
  840. /*
  841. * We must set all the cc pointers in ldata->chars to 0 right
  842. * now, so that cc diagnostics that verify the integrity of the
  843. * whole line will make sense while we're in the middle of
  844. * building it up.
  845. */
  846. {
  847. int i;
  848. for (i = 0; i < ldata->cols; i++)
  849. ldata->chars[i].cc_next = 0;
  850. }
  851. /*
  852. * Now read in the lattr.
  853. */
  854. int lattr = shift = 0;
  855. do {
  856. byte = get_byte(bs);
  857. lattr |= (byte & 0x7F) << shift;
  858. shift += 7;
  859. } while (byte & 0x80);
  860. ldata->lattr = lattr & 0xFFFF;
  861. ldata->trusted = (lattr & 0x10000) != 0;
  862. /*
  863. * Now we read in each of the RLE streams in turn.
  864. */
  865. readrle(bs, ldata, readliteral_chr);
  866. readrle(bs, ldata, readliteral_attr);
  867. readrle(bs, ldata, readliteral_truecolour);
  868. readrle(bs, ldata, readliteral_cc);
  869. /* And we always expect that we ended up exactly at the end of the
  870. * compressed data. */
  871. assert(!get_err(bs));
  872. assert(get_avail(bs) == 0);
  873. return ldata;
  874. }
  875. static inline void free_compressed_line(compressed_scrollback_line *cline)
  876. {
  877. sfree(cline);
  878. }
  879. static termline *decompressline_and_free(compressed_scrollback_line *cline)
  880. {
  881. termline *ldata = decompressline_no_free(cline);
  882. free_compressed_line(cline);
  883. return ldata;
  884. }
  885. #else /* NO_SCROLLBACK_COMPRESSION */
  886. static termline *duptermline(termline *oldline)
  887. {
  888. termline *newline = snew(termline);
  889. *newline = *oldline; /* copy the POD structure fields */
  890. newline->chars = snewn(newline->size, termchar);
  891. for (int j = 0; j < newline->size; j++)
  892. newline->chars[j] = oldline->chars[j];
  893. return newline;
  894. }
  895. typedef termline compressed_scrollback_line;
  896. static inline compressed_scrollback_line *compressline_and_free(
  897. termline *ldata)
  898. {
  899. return ldata;
  900. }
  901. static inline compressed_scrollback_line *compressline_no_free(termline *ldata)
  902. {
  903. return duptermline(ldata);
  904. }
  905. static inline termline *decompressline_no_free(
  906. compressed_scrollback_line *line)
  907. {
  908. /* This will return a line without the 'temporary' flag, which
  909. * means that unlineptr() is already set up to avoid freeing it */
  910. return line;
  911. }
  912. static inline termline *decompressline_and_free(
  913. compressed_scrollback_line *line)
  914. {
  915. /* Same as decompressline_no_free, because the caller will free
  916. * our returned termline, and that does all the freeing necessary */
  917. return line;
  918. }
  919. static inline void free_compressed_line(compressed_scrollback_line *line)
  920. {
  921. freetermline(line);
  922. }
  923. #endif /* NO_SCROLLBACK_COMPRESSION */
  924. /*
  925. * Resize a line to make it `cols' columns wide.
  926. */
  927. static void resizeline(Terminal *term, termline *line, int cols)
  928. {
  929. int i, oldcols;
  930. if (line->cols != cols) {
  931. oldcols = line->cols;
  932. /*
  933. * This line is the wrong length, which probably means it
  934. * hasn't been accessed since a resize. Resize it now.
  935. *
  936. * First, go through all the characters that will be thrown
  937. * out in the resize (if we're shrinking the line) and
  938. * return their cc lists to the cc free list.
  939. */
  940. for (i = cols; i < oldcols; i++)
  941. clear_cc(line, i);
  942. /*
  943. * If we're shrinking the line, we now bodily move the
  944. * entire cc section from where it started to where it now
  945. * needs to be. (We have to do this before the resize, so
  946. * that the data we're copying is still there. However, if
  947. * we're expanding, we have to wait until _after_ the
  948. * resize so that the space we're copying into is there.)
  949. */
  950. if (cols < oldcols)
  951. memmove(line->chars + cols, line->chars + oldcols,
  952. (line->size - line->cols) * TSIZE);
  953. /*
  954. * Now do the actual resize, leaving the _same_ amount of
  955. * cc space as there was to begin with.
  956. */
  957. line->size += cols - oldcols;
  958. line->chars = sresize(line->chars, line->size, TTYPE);
  959. line->cols = cols;
  960. /*
  961. * If we're expanding the line, _now_ we move the cc
  962. * section.
  963. */
  964. if (cols > oldcols)
  965. memmove(line->chars + cols, line->chars + oldcols,
  966. (line->size - line->cols) * TSIZE);
  967. /*
  968. * Go through what's left of the original line, and adjust
  969. * the first cc_next pointer in each list. (All the
  970. * subsequent ones are still valid because they are
  971. * relative offsets within the cc block.) Also do the same
  972. * to the head of the cc_free list.
  973. */
  974. for (i = 0; i < oldcols && i < cols; i++)
  975. if (line->chars[i].cc_next)
  976. line->chars[i].cc_next += cols - oldcols;
  977. if (line->cc_free)
  978. line->cc_free += cols - oldcols;
  979. /*
  980. * And finally fill in the new space with erase chars. (We
  981. * don't have to worry about cc lists here, because we
  982. * _know_ the erase char doesn't have one.)
  983. */
  984. for (i = oldcols; i < cols; i++)
  985. line->chars[i] = term->basic_erase_char;
  986. #ifdef TERM_CC_DIAGS
  987. cc_check(line);
  988. #endif
  989. }
  990. }
  991. /*
  992. * Get the number of lines in the scrollback.
  993. */
  994. static int sblines(Terminal *term)
  995. {
  996. int sblines = count234(term->scrollback);
  997. if (term->erase_to_scrollback &&
  998. term->alt_which && term->alt_screen) {
  999. sblines += term->alt_sblines;
  1000. }
  1001. return sblines;
  1002. }
  1003. static void null_line_error(Terminal *term, int y, int lineno,
  1004. tree234 *whichtree, int treeindex,
  1005. const char *varname)
  1006. {
  1007. modalfatalbox("%s==NULL in terminal.c\n"
  1008. "lineno=%d y=%d w=%d h=%d\n"
  1009. "count(scrollback=%p)=%d\n"
  1010. "count(screen=%p)=%d\n"
  1011. "count(alt=%p)=%d alt_sblines=%d\n"
  1012. "whichtree=%p treeindex=%d\n"
  1013. "commitid=%s\n\n"
  1014. "Please contact <putty@projects.tartarus.org> "
  1015. "and pass on the above information.",
  1016. varname, lineno, y, term->cols, term->rows,
  1017. term->scrollback, count234(term->scrollback),
  1018. term->screen, count234(term->screen),
  1019. term->alt_screen, count234(term->alt_screen),
  1020. term->alt_sblines, whichtree, treeindex, commitid);
  1021. }
  1022. static inline int checkscr(int y, int lineno)
  1023. {
  1024. if (y < 0)
  1025. modalfatalbox("screen line %d < 0 in terminal.c:%d", y, lineno);
  1026. return y;
  1027. }
  1028. /*
  1029. * Retrieve a line of the screen or of the scrollback, according to
  1030. * whether the y coordinate is non-negative or negative
  1031. * (respectively).
  1032. */
  1033. static termline *lineptr(Terminal *term, int y, int lineno)
  1034. {
  1035. termline *line;
  1036. tree234 *whichtree;
  1037. int treeindex;
  1038. if (y >= 0) {
  1039. whichtree = term->screen;
  1040. treeindex = y;
  1041. } else {
  1042. int altlines = 0;
  1043. if (term->erase_to_scrollback &&
  1044. term->alt_which && term->alt_screen) {
  1045. altlines = term->alt_sblines;
  1046. }
  1047. if (y < -altlines) {
  1048. whichtree = term->scrollback;
  1049. treeindex = y + altlines + count234(term->scrollback);
  1050. } else {
  1051. whichtree = term->alt_screen;
  1052. treeindex = y + term->alt_sblines;
  1053. /* treeindex = y + count234(term->alt_screen); */
  1054. }
  1055. }
  1056. if (whichtree == term->scrollback) {
  1057. compressed_scrollback_line *cline = index234(whichtree, treeindex);
  1058. if (!cline)
  1059. null_line_error(term, y, lineno, whichtree, treeindex, "cline");
  1060. line = decompressline_no_free(cline);
  1061. } else {
  1062. line = index234(whichtree, treeindex);
  1063. }
  1064. /* We assume that we don't screw up and retrieve something out of range. */
  1065. if (line == NULL)
  1066. null_line_error(term, y, lineno, whichtree, treeindex, "line");
  1067. assert(line != NULL);
  1068. /*
  1069. * Here we resize lines to _at least_ the right length, but we
  1070. * don't truncate them. Truncation is done as a side effect of
  1071. * modifying the line.
  1072. *
  1073. * The point of this policy is to try to arrange that resizing the
  1074. * terminal window repeatedly - e.g. successive steps in an X11
  1075. * opaque window-resize drag, or resizing as a side effect of
  1076. * retiling by tiling WMs such as xmonad - does not throw away
  1077. * data gratuitously. Specifically, we want a sequence of resize
  1078. * operations with no terminal output between them to have the
  1079. * same effect as a single resize to the ultimate terminal size,
  1080. * and also (for the case in which xmonad narrows a window that's
  1081. * scrolling things) we want scrolling up new text at the bottom
  1082. * of a narrowed window to avoid truncating lines further up when
  1083. * the window is re-widened.
  1084. */
  1085. if (term->cols > line->cols)
  1086. resizeline(term, line, term->cols);
  1087. return line;
  1088. }
  1089. /*
  1090. * Macro wrappers for lineptr. The distinction between lineptr and
  1091. * scrlineptr is that lineptr can retrieve any line, from the screen
  1092. * _or_ from scrollback, and in return, you have to call unlineptr
  1093. * when you're done with it, in case it was a dynamically allocated
  1094. * line decompressed from scrollback that needs freeing. But
  1095. * scrlineptr will only retrieve lines from the active screen (and
  1096. * enforces this by an assertion), which means it's always just
  1097. * returning a pointer to an existing unpacked termline, and you don't
  1098. * have to call unlineptr afterwards. So drawing code (which might
  1099. * need the scrollback) will have to call lineptr/unlineptr, but
  1100. * update code during term_out can call scrlineptr.
  1101. *
  1102. * The 'assertion' in scrlineptr is done using a helper function that
  1103. * returns the input column number, which allows this macro to avoid
  1104. * double-evaluating its argument.
  1105. */
  1106. #define lineptr(x) (lineptr)(term,x,__LINE__)
  1107. #define scrlineptr(x) (lineptr)(term,checkscr(x,__LINE__),__LINE__)
  1108. #define unlineptr(line) term_release_line(line)
  1109. /* Wrapper for external use (e.g. tests), without the __LINE__ parameter */
  1110. termline *term_get_line(Terminal *term, int y) { return lineptr(y); }
  1111. /*
  1112. * Coerce a termline to the terminal's current width. Unlike the
  1113. * optional resize in lineptr() above, this is potentially destructive
  1114. * of text, since it can shrink as well as grow the line.
  1115. *
  1116. * We call this whenever a termline is actually going to be modified.
  1117. * Helpfully, putting a single call to this function in check_boundary
  1118. * deals with _nearly_ all such cases, leaving only a few things like
  1119. * bulk erase and ESC#8 to handle separately.
  1120. */
  1121. static void check_line_size(Terminal *term, termline *line)
  1122. {
  1123. if (term->cols != line->cols) /* trivial optimisation */
  1124. resizeline(term, line, term->cols);
  1125. }
  1126. static void term_schedule_tblink(Terminal *term);
  1127. static void term_schedule_cblink(Terminal *term);
  1128. static void term_update_callback(void *ctx);
  1129. static void term_timer(void *ctx, unsigned long now)
  1130. {
  1131. Terminal *term = (Terminal *)ctx;
  1132. if (term->tblink_pending && now == term->next_tblink) {
  1133. term->tblinker = !term->tblinker;
  1134. term->tblink_pending = false;
  1135. term_schedule_tblink(term);
  1136. term->window_update_pending = true;
  1137. }
  1138. if (term->cblink_pending && now == term->next_cblink) {
  1139. term->cblinker = !term->cblinker;
  1140. term->cblink_pending = false;
  1141. term_schedule_cblink(term);
  1142. term->window_update_pending = true;
  1143. }
  1144. if (term->in_vbell && now == term->vbell_end) {
  1145. term->in_vbell = false;
  1146. term->window_update_pending = true;
  1147. }
  1148. if (term->window_update_cooldown &&
  1149. now == term->window_update_cooldown_end) {
  1150. term->window_update_cooldown = false;
  1151. }
  1152. if (term->window_update_pending)
  1153. term_update_callback(term);
  1154. }
  1155. static void term_update_callback(void *ctx)
  1156. {
  1157. Terminal *term = (Terminal *)ctx;
  1158. if (!term->window_update_pending)
  1159. return;
  1160. if (!term->window_update_cooldown) {
  1161. term_update(term);
  1162. term->window_update_cooldown = true;
  1163. term->window_update_cooldown_end = schedule_timer(
  1164. UPDATE_DELAY, term_timer, term);
  1165. }
  1166. }
  1167. static void term_schedule_update(Terminal *term)
  1168. {
  1169. if (!term->window_update_pending) {
  1170. term->window_update_pending = true;
  1171. queue_toplevel_callback(term_update_callback, term);
  1172. }
  1173. }
  1174. /*
  1175. * Call this whenever the terminal window state changes, to queue an
  1176. * update. This also resets the phase of cursor blinking, so that the
  1177. * cursor remains visible as it moves with the output, and sets a flag
  1178. * to indicate that if we have the 'reset scrollback on display
  1179. * activity' setting enabled, then we should activate it.
  1180. */
  1181. static void seen_disp_event(Terminal *term)
  1182. {
  1183. if (term->scroll_on_disp) {
  1184. term->disptop = 0;
  1185. term->win_scrollbar_update_pending = true;
  1186. }
  1187. term->cblinker = true;
  1188. term->cblink_pending = false;
  1189. term_schedule_cblink(term);
  1190. term_schedule_update(term);
  1191. }
  1192. /*
  1193. * Call when the terminal's blinking-text settings change, or when
  1194. * a text blink has just occurred.
  1195. */
  1196. static void term_schedule_tblink(Terminal *term)
  1197. {
  1198. if (term->blink_is_real) {
  1199. if (!term->tblink_pending)
  1200. term->next_tblink = schedule_timer(TBLINK_DELAY, term_timer, term);
  1201. term->tblink_pending = true;
  1202. } else {
  1203. term->tblinker = true; /* reset when not in use */
  1204. term->tblink_pending = false;
  1205. }
  1206. }
  1207. /*
  1208. * Likewise with cursor blinks.
  1209. */
  1210. static void term_schedule_cblink(Terminal *term)
  1211. {
  1212. if (term->blink_cur && term->has_focus) {
  1213. if (!term->cblink_pending)
  1214. term->next_cblink = schedule_timer(CBLINK_DELAY, term_timer, term);
  1215. term->cblink_pending = true;
  1216. } else {
  1217. term->cblinker = true; /* reset when not in use */
  1218. term->cblink_pending = false;
  1219. }
  1220. }
  1221. /*
  1222. * Call to begin a visual bell.
  1223. */
  1224. static void term_schedule_vbell(Terminal *term, bool already_started,
  1225. long startpoint)
  1226. {
  1227. long ticks_already_gone;
  1228. if (already_started)
  1229. ticks_already_gone = GETTICKCOUNT() - startpoint;
  1230. else
  1231. ticks_already_gone = 0;
  1232. if (ticks_already_gone < VBELL_DELAY) {
  1233. term->in_vbell = true;
  1234. term->vbell_end = schedule_timer(VBELL_DELAY - ticks_already_gone,
  1235. term_timer, term);
  1236. } else {
  1237. term->in_vbell = false;
  1238. }
  1239. }
  1240. /*
  1241. * Set up power-on settings for the terminal.
  1242. * If 'clear' is false, don't actually clear the primary screen, and
  1243. * position the cursor below the last non-blank line (scrolling if
  1244. * necessary).
  1245. */
  1246. static void power_on(Terminal *term, bool clear)
  1247. {
  1248. term->alt_x = term->alt_y = 0;
  1249. term->savecurs.x = term->savecurs.y = 0;
  1250. term->alt_savecurs.x = term->alt_savecurs.y = 0;
  1251. term->alt_t = term->marg_t = 0;
  1252. if (term->rows != -1)
  1253. term->alt_b = term->marg_b = term->rows - 1;
  1254. else
  1255. term->alt_b = term->marg_b = 0;
  1256. if (term->cols != -1) {
  1257. int i;
  1258. for (i = 0; i < term->cols; i++)
  1259. term->tabs[i] = (i % 8 == 0 ? true : false);
  1260. }
  1261. term->alt_om = term->dec_om = conf_get_bool(term->conf, CONF_dec_om);
  1262. term->alt_ins = false;
  1263. term->insert = false;
  1264. term->alt_wnext = false;
  1265. term->wrapnext = false;
  1266. term->save_wnext = false;
  1267. term->alt_save_wnext = false;
  1268. term->alt_wrap = term->wrap = conf_get_bool(term->conf, CONF_wrap_mode);
  1269. term->alt_cset = term->cset = term->save_cset = term->alt_save_cset = 0;
  1270. term->alt_utf = false;
  1271. term->utf = false;
  1272. term->save_utf = false;
  1273. term->alt_save_utf = false;
  1274. term->utf8.state = 0;
  1275. term->alt_sco_acs = term->sco_acs =
  1276. term->save_sco_acs = term->alt_save_sco_acs = 0;
  1277. term->cset_attr[0] = term->cset_attr[1] =
  1278. term->save_csattr = term->alt_save_csattr = CSET_ASCII;
  1279. term->rvideo = false;
  1280. term->in_vbell = false;
  1281. term->cursor_on = true;
  1282. term->big_cursor = false;
  1283. term->default_attr = term->save_attr =
  1284. term->alt_save_attr = term->curr_attr = ATTR_DEFAULT;
  1285. term->curr_truecolour.fg = term->curr_truecolour.bg = optionalrgb_none;
  1286. term->save_truecolour = term->alt_save_truecolour = term->curr_truecolour;
  1287. term->app_cursor_keys = conf_get_bool(term->conf, CONF_app_cursor);
  1288. term->app_keypad_keys = conf_get_bool(term->conf, CONF_app_keypad);
  1289. term->use_bce = conf_get_bool(term->conf, CONF_bce);
  1290. term->blink_is_real = conf_get_bool(term->conf, CONF_blinktext);
  1291. term->erase_char = term->basic_erase_char;
  1292. term->alt_which = 0;
  1293. term_print_finish(term);
  1294. term->xterm_mouse = 0;
  1295. term->xterm_extended_mouse = false;
  1296. term->urxvt_extended_mouse = false;
  1297. term->raw_mouse_reported_x = 0;
  1298. term->raw_mouse_reported_y = 0;
  1299. win_set_raw_mouse_mode(term->win, false);
  1300. term->win_pointer_shape_pending = true;
  1301. term->win_pointer_shape_raw = false;
  1302. term->bracketed_paste = false;
  1303. term->srm_echo = false;
  1304. {
  1305. int i;
  1306. for (i = 0; i < 256; i++)
  1307. term->wordness[i] = conf_get_int_int(term->conf, CONF_wordness, i);
  1308. }
  1309. if (term->screen) {
  1310. swap_screen(term, 1, false, false);
  1311. erase_lots(term, false, true, true);
  1312. swap_screen(term, 0, false, false);
  1313. if (clear)
  1314. erase_lots(term, false, true, true);
  1315. term->curs.y = find_last_nonempty_line(term, term->screen) + 1;
  1316. if (term->curs.y == term->rows) {
  1317. term->curs.y--;
  1318. scroll(term, 0, term->rows - 1, 1, true);
  1319. }
  1320. } else {
  1321. term->curs.y = 0;
  1322. }
  1323. term->curs.x = 0;
  1324. term_schedule_tblink(term);
  1325. term_schedule_cblink(term);
  1326. term_schedule_update(term);
  1327. }
  1328. /*
  1329. * Force a screen update.
  1330. */
  1331. void term_update(Terminal *term)
  1332. {
  1333. term->window_update_pending = false;
  1334. if (term->win_move_pending) {
  1335. win_move(term->win, term->win_move_pending_x,
  1336. term->win_move_pending_y);
  1337. term->win_move_pending = false;
  1338. }
  1339. if (term->win_resize_pending == WIN_RESIZE_NEED_SEND) {
  1340. term->win_resize_pending = WIN_RESIZE_AWAIT_REPLY;
  1341. win_request_resize(term->win, term->win_resize_pending_w,
  1342. term->win_resize_pending_h);
  1343. }
  1344. if (term->win_zorder_pending) {
  1345. win_set_zorder(term->win, term->win_zorder_top);
  1346. term->win_zorder_pending = false;
  1347. }
  1348. if (term->win_minimise_pending) {
  1349. win_set_minimised(term->win, term->win_minimise_enable);
  1350. term->win_minimise_pending = false;
  1351. }
  1352. if (term->win_maximise_pending) {
  1353. win_set_maximised(term->win, term->win_maximise_enable);
  1354. term->win_maximise_pending = false;
  1355. }
  1356. if (term->win_title_pending) {
  1357. win_set_title(term->win, term->window_title,
  1358. term->wintitle_codepage);
  1359. term->win_title_pending = false;
  1360. }
  1361. if (term->win_icon_title_pending) {
  1362. win_set_icon_title(term->win, term->icon_title,
  1363. term->icontitle_codepage);
  1364. term->win_icon_title_pending = false;
  1365. }
  1366. if (term->win_pointer_shape_pending) {
  1367. win_set_raw_mouse_mode_pointer(term->win, term->win_pointer_shape_raw);
  1368. term->win_pointer_shape_pending = false;
  1369. }
  1370. if (term->win_refresh_pending) {
  1371. win_refresh(term->win);
  1372. term->win_refresh_pending = false;
  1373. }
  1374. if (term->win_palette_pending) {
  1375. unsigned start = term->win_palette_pending_min;
  1376. unsigned ncolours = term->win_palette_pending_limit - start;
  1377. win_palette_set(term->win, start, ncolours, term->palette + start);
  1378. term->win_palette_pending = false;
  1379. }
  1380. if (win_setup_draw_ctx(term->win)) {
  1381. if (term->win_scrollbar_update_pending) {
  1382. term->win_scrollbar_update_pending = false;
  1383. update_sbar(term);
  1384. }
  1385. do_paint(term);
  1386. win_set_cursor_pos(
  1387. term->win, term->curs.x, term->curs.y - term->disptop);
  1388. win_free_draw_ctx(term->win);
  1389. }
  1390. }
  1391. /*
  1392. * Called from front end when a keypress occurs, to trigger
  1393. * anything magical that needs to happen in that situation.
  1394. */
  1395. void term_seen_key_event(Terminal *term)
  1396. {
  1397. /*
  1398. * On any keypress, clear the bell overload mechanism
  1399. * completely, on the grounds that large numbers of
  1400. * beeps coming from deliberate key action are likely
  1401. * to be intended (e.g. beeps from filename completion
  1402. * blocking repeatedly).
  1403. */
  1404. term->beep_overloaded = false;
  1405. while (term->beephead) {
  1406. struct beeptime *tmp = term->beephead;
  1407. term->beephead = tmp->next;
  1408. sfree(tmp);
  1409. }
  1410. term->beeptail = NULL;
  1411. term->nbeeps = 0;
  1412. /*
  1413. * Reset the scrollback on keypress, if we're doing that.
  1414. */
  1415. if (term->scroll_on_key && term->disptop != 0) {
  1416. term->disptop = 0;
  1417. term->win_scrollbar_update_pending = true;
  1418. term_schedule_update(term);
  1419. }
  1420. }
  1421. /*
  1422. * Same as power_on(), but an external function.
  1423. */
  1424. void term_pwron(Terminal *term, bool clear)
  1425. {
  1426. power_on(term, clear);
  1427. if (term->ldisc) /* cause ldisc to notice changes */
  1428. ldisc_echoedit_update(term->ldisc);
  1429. term->disptop = 0;
  1430. deselect(term);
  1431. term_update(term);
  1432. }
  1433. static void set_erase_char(Terminal *term)
  1434. {
  1435. term->erase_char = term->basic_erase_char;
  1436. if (term->use_bce) {
  1437. term->erase_char.attr = (term->curr_attr &
  1438. (ATTR_FGMASK | ATTR_BGMASK));
  1439. term->erase_char.truecolour.bg = term->curr_truecolour.bg;
  1440. }
  1441. }
  1442. /*
  1443. * We copy a bunch of stuff out of the Conf structure into local
  1444. * fields in the Terminal structure, to avoid the repeated tree234
  1445. * lookups which would be involved in fetching them from the former
  1446. * every time.
  1447. */
  1448. static void term_copy_stuff_from_conf(Terminal *term)
  1449. {
  1450. term->ansi_colour = conf_get_bool(term->conf, CONF_ansi_colour);
  1451. term->no_arabicshaping = conf_get_bool(term->conf, CONF_no_arabicshaping);
  1452. term->beep = conf_get_int(term->conf, CONF_beep);
  1453. term->bellovl = conf_get_bool(term->conf, CONF_bellovl);
  1454. term->bellovl_n = conf_get_int(term->conf, CONF_bellovl_n);
  1455. term->bellovl_s = conf_get_int(term->conf, CONF_bellovl_s);
  1456. term->bellovl_t = conf_get_int(term->conf, CONF_bellovl_t);
  1457. term->no_bidi = conf_get_bool(term->conf, CONF_no_bidi);
  1458. term->no_bracketed_paste = conf_get_bool(term->conf, CONF_no_bracketed_paste);
  1459. term->bksp_is_delete = conf_get_bool(term->conf, CONF_bksp_is_delete);
  1460. term->blink_cur = conf_get_bool(term->conf, CONF_blink_cur);
  1461. term->blinktext = conf_get_bool(term->conf, CONF_blinktext);
  1462. term->cjk_ambig_wide = conf_get_bool(term->conf, CONF_cjk_ambig_wide);
  1463. term->conf_height = conf_get_int(term->conf, CONF_height);
  1464. term->conf_width = conf_get_int(term->conf, CONF_width);
  1465. term->crhaslf = conf_get_bool(term->conf, CONF_crhaslf);
  1466. term->erase_to_scrollback = conf_get_bool(term->conf, CONF_erase_to_scrollback);
  1467. term->funky_type = conf_get_int(term->conf, CONF_funky_type);
  1468. term->sharrow_type = conf_get_int(term->conf, CONF_sharrow_type);
  1469. term->lfhascr = conf_get_bool(term->conf, CONF_lfhascr);
  1470. term->logflush = conf_get_bool(term->conf, CONF_logflush);
  1471. term->logtype = conf_get_int(term->conf, CONF_logtype);
  1472. term->mouse_override = conf_get_bool(term->conf, CONF_mouse_override);
  1473. term->nethack_keypad = conf_get_bool(term->conf, CONF_nethack_keypad);
  1474. term->no_alt_screen = conf_get_bool(term->conf, CONF_no_alt_screen);
  1475. term->no_applic_c = conf_get_bool(term->conf, CONF_no_applic_c);
  1476. term->no_applic_k = conf_get_bool(term->conf, CONF_no_applic_k);
  1477. term->no_dbackspace = conf_get_bool(term->conf, CONF_no_dbackspace);
  1478. term->no_mouse_rep = conf_get_bool(term->conf, CONF_no_mouse_rep);
  1479. term->no_remote_charset = conf_get_bool(term->conf, CONF_no_remote_charset);
  1480. term->no_remote_resize = conf_get_bool(term->conf, CONF_no_remote_resize);
  1481. term->no_remote_wintitle = conf_get_bool(term->conf, CONF_no_remote_wintitle);
  1482. term->no_remote_clearscroll = conf_get_bool(term->conf, CONF_no_remote_clearscroll);
  1483. term->rawcnp = conf_get_bool(term->conf, CONF_rawcnp);
  1484. term->utf8linedraw = conf_get_bool(term->conf, CONF_utf8linedraw);
  1485. term->rect_select = conf_get_bool(term->conf, CONF_rect_select);
  1486. term->remote_qtitle_action = conf_get_int(term->conf, CONF_remote_qtitle_action);
  1487. term->rxvt_homeend = conf_get_bool(term->conf, CONF_rxvt_homeend);
  1488. term->scroll_on_disp = conf_get_bool(term->conf, CONF_scroll_on_disp);
  1489. term->scroll_on_key = conf_get_bool(term->conf, CONF_scroll_on_key);
  1490. term->xterm_mouse_forbidden = conf_get_bool(term->conf, CONF_no_mouse_rep);
  1491. term->xterm_256_colour = conf_get_bool(term->conf, CONF_xterm_256_colour);
  1492. term->true_colour = conf_get_bool(term->conf, CONF_true_colour);
  1493. /*
  1494. * Parse the control-character escapes in the configured
  1495. * answerback string.
  1496. */
  1497. {
  1498. char *answerback = conf_get_str(term->conf, CONF_answerback);
  1499. strbuf_clear(term->answerback);
  1500. while (*answerback) {
  1501. char *n;
  1502. char c = ctrlparse(answerback, &n);
  1503. if (n) {
  1504. put_byte(term->answerback, c);
  1505. answerback = n;
  1506. } else {
  1507. put_byte(term->answerback, *answerback++);
  1508. }
  1509. }
  1510. }
  1511. }
  1512. void term_pre_reconfig(Terminal *term, Conf *conf)
  1513. {
  1514. /*
  1515. * Copy the current window title into the stored previous
  1516. * configuration, so that doing nothing to the window title field
  1517. * in the config box doesn't reset the title to its startup state.
  1518. */
  1519. conf_set_str(conf, CONF_wintitle, term->window_title);
  1520. }
  1521. /*
  1522. * When the user reconfigures us, we need to check the forbidden-
  1523. * alternate-screen config option, disable raw mouse mode if the
  1524. * user has disabled mouse reporting, and abandon a print job if
  1525. * the user has disabled printing.
  1526. */
  1527. void term_reconfig(Terminal *term, Conf *conf)
  1528. {
  1529. /*
  1530. * Before adopting the new config, check all those terminal
  1531. * settings which control power-on defaults; and if they've
  1532. * changed, we will modify the current state as well as the
  1533. * default one. The full list is: Auto wrap mode, DEC Origin
  1534. * Mode, BCE, blinking text, character classes.
  1535. */
  1536. bool reset_wrap, reset_decom, reset_bce, reset_tblink, reset_charclass;
  1537. bool palette_changed = false;
  1538. int i;
  1539. reset_wrap = (conf_get_bool(term->conf, CONF_wrap_mode) !=
  1540. conf_get_bool(conf, CONF_wrap_mode));
  1541. reset_decom = (conf_get_bool(term->conf, CONF_dec_om) !=
  1542. conf_get_bool(conf, CONF_dec_om));
  1543. reset_bce = (conf_get_bool(term->conf, CONF_bce) !=
  1544. conf_get_bool(conf, CONF_bce));
  1545. reset_tblink = (conf_get_bool(term->conf, CONF_blinktext) !=
  1546. conf_get_bool(conf, CONF_blinktext));
  1547. reset_charclass = false;
  1548. for (i = 0; i < 256; i++)
  1549. if (conf_get_int_int(term->conf, CONF_wordness, i) !=
  1550. conf_get_int_int(conf, CONF_wordness, i))
  1551. reset_charclass = true;
  1552. /*
  1553. * If the bidi or shaping settings have changed, flush the bidi
  1554. * cache completely.
  1555. */
  1556. if (conf_get_bool(term->conf, CONF_no_arabicshaping) !=
  1557. conf_get_bool(conf, CONF_no_arabicshaping) ||
  1558. conf_get_bool(term->conf, CONF_no_bidi) !=
  1559. conf_get_bool(conf, CONF_no_bidi)) {
  1560. for (i = 0; i < term->bidi_cache_size; i++) {
  1561. sfree(term->pre_bidi_cache[i].chars);
  1562. sfree(term->post_bidi_cache[i].chars);
  1563. term->pre_bidi_cache[i].width = -1;
  1564. term->pre_bidi_cache[i].chars = NULL;
  1565. term->post_bidi_cache[i].width = -1;
  1566. term->post_bidi_cache[i].chars = NULL;
  1567. }
  1568. }
  1569. {
  1570. const char *old_title = conf_get_str(term->conf, CONF_wintitle);
  1571. const char *new_title = conf_get_str(conf, CONF_wintitle);
  1572. if (strcmp(old_title, new_title)) {
  1573. sfree(term->window_title);
  1574. term->window_title = dupstr(new_title);
  1575. term->wintitle_codepage = DEFAULT_CODEPAGE;
  1576. term->win_title_pending = true;
  1577. term_schedule_update(term);
  1578. }
  1579. }
  1580. /*
  1581. * Just setting conf is sufficient to cause colour setting changes
  1582. * to appear on the next ESC]R palette reset. But we should also
  1583. * check whether any colour settings have been changed, so that
  1584. * they can be updated immediately if they haven't been overridden
  1585. * by some escape sequence.
  1586. */
  1587. {
  1588. int i, j;
  1589. for (i = 0; i < CONF_NCOLOURS; i++) {
  1590. for (j = 0; j < 3; j++)
  1591. if (conf_get_int_int(term->conf, CONF_colours, i*3+j) !=
  1592. conf_get_int_int(conf, CONF_colours, i*3+j))
  1593. break;
  1594. if (j < 3) {
  1595. /* Actually enacting the change has to be deferred
  1596. * until the new conf is installed. */
  1597. palette_changed = true;
  1598. break;
  1599. }
  1600. }
  1601. }
  1602. conf_free(term->conf);
  1603. term->conf = conf_copy(conf);
  1604. if (reset_wrap) {
  1605. term->alt_wrap = term->wrap = conf_get_bool(term->conf, CONF_wrap_mode);
  1606. if (!term->wrap)
  1607. term->wrapnext = false;
  1608. if (!term->alt_wrap)
  1609. term->alt_wnext = false;
  1610. }
  1611. if (reset_decom)
  1612. term->alt_om = term->dec_om = conf_get_bool(term->conf, CONF_dec_om);
  1613. if (reset_bce) {
  1614. term->use_bce = conf_get_bool(term->conf, CONF_bce);
  1615. set_erase_char(term);
  1616. }
  1617. if (reset_tblink) {
  1618. term->blink_is_real = conf_get_bool(term->conf, CONF_blinktext);
  1619. }
  1620. if (reset_charclass)
  1621. for (i = 0; i < 256; i++)
  1622. term->wordness[i] = conf_get_int_int(term->conf, CONF_wordness, i);
  1623. if (conf_get_bool(term->conf, CONF_no_alt_screen))
  1624. swap_screen(term, 0, false, false);
  1625. if (conf_get_bool(term->conf, CONF_no_remote_charset)) {
  1626. term->cset_attr[0] = term->cset_attr[1] = CSET_ASCII;
  1627. term->sco_acs = term->alt_sco_acs = 0;
  1628. term->utf = false;
  1629. }
  1630. if (!conf_get_str(term->conf, CONF_printer)) {
  1631. term_print_finish(term);
  1632. }
  1633. if (palette_changed)
  1634. term_notify_palette_changed(term);
  1635. term_schedule_tblink(term);
  1636. term_schedule_cblink(term);
  1637. term_copy_stuff_from_conf(term);
  1638. term_update_raw_mouse_mode(term);
  1639. }
  1640. /*
  1641. * Clear the scrollback.
  1642. */
  1643. void term_clrsb(Terminal *term)
  1644. {
  1645. unsigned char *line;
  1646. int i;
  1647. /*
  1648. * Scroll forward to the current screen, if we were back in the
  1649. * scrollback somewhere until now.
  1650. */
  1651. term->disptop = 0;
  1652. /*
  1653. * Clear the actual scrollback.
  1654. */
  1655. while ((line = delpos234(term->scrollback, 0)) != NULL) {
  1656. sfree(line); /* this is compressed data, not a termline */
  1657. }
  1658. /*
  1659. * When clearing the scrollback, we also truncate any termlines on
  1660. * the current screen which have remembered data from a previous
  1661. * larger window size. Rationale: clearing the scrollback is
  1662. * sometimes done to protect privacy, so the user intention is
  1663. * specifically that we should not retain evidence of what
  1664. * previously happened in the terminal, and that ought to include
  1665. * evidence to the right as well as evidence above.
  1666. */
  1667. for (i = 0; i < term->rows; i++)
  1668. check_line_size(term, scrlineptr(i));
  1669. /*
  1670. * That operation has invalidated the selection, if it overlapped
  1671. * the scrollback at all.
  1672. */
  1673. if (term->selstate != NO_SELECTION && term->selstart.y < 0)
  1674. deselect(term);
  1675. /*
  1676. * There are now no lines of real scrollback which can be pulled
  1677. * back into the screen by a resize, and no lines of the alternate
  1678. * screen which should be displayed as if part of the scrollback.
  1679. */
  1680. term->tempsblines = 0;
  1681. term->alt_sblines = 0;
  1682. /*
  1683. * The scrollbar will need updating to reflect the new state of
  1684. * the world.
  1685. */
  1686. term->win_scrollbar_update_pending = true;
  1687. term_schedule_update(term);
  1688. }
  1689. const optionalrgb optionalrgb_none = {0, 0, 0, 0};
  1690. void term_setup_window_titles(Terminal *term, const char *title_hostname)
  1691. {
  1692. const char *conf_title = conf_get_str(term->conf, CONF_wintitle);
  1693. sfree(term->window_title);
  1694. sfree(term->icon_title);
  1695. if (*conf_title) {
  1696. term->window_title = dupstr(conf_title);
  1697. term->icon_title = dupstr(conf_title);
  1698. } else {
  1699. if (title_hostname && *title_hostname)
  1700. term->window_title = dupcat(title_hostname, " - ", appname);
  1701. else
  1702. term->window_title = dupstr(appname);
  1703. term->icon_title = dupstr(term->window_title);
  1704. }
  1705. term->wintitle_codepage = term->icontitle_codepage = DEFAULT_CODEPAGE;
  1706. term->win_title_pending = true;
  1707. term->win_icon_title_pending = true;
  1708. }
  1709. static void palette_rebuild(Terminal *term)
  1710. {
  1711. unsigned min_changed = OSC4_NCOLOURS, max_changed = 0;
  1712. if (term->win_palette_pending) {
  1713. /* Possibly extend existing range. */
  1714. min_changed = term->win_palette_pending_min;
  1715. max_changed = term->win_palette_pending_limit - 1;
  1716. } else {
  1717. /* Start with empty range. */
  1718. min_changed = OSC4_NCOLOURS;
  1719. max_changed = 0;
  1720. }
  1721. for (unsigned i = 0; i < OSC4_NCOLOURS; i++) {
  1722. rgb new_value;
  1723. bool found = false;
  1724. for (unsigned j = lenof(term->subpalettes); j-- > 0 ;) {
  1725. if (term->subpalettes[j].present[i]) {
  1726. new_value = term->subpalettes[j].values[i];
  1727. found = true;
  1728. break;
  1729. }
  1730. }
  1731. assert(found); /* we expect SUBPAL_CONF to always be set */
  1732. if (new_value.r != term->palette[i].r ||
  1733. new_value.g != term->palette[i].g ||
  1734. new_value.b != term->palette[i].b) {
  1735. term->palette[i] = new_value;
  1736. if (min_changed > i)
  1737. min_changed = i;
  1738. if (max_changed < i)
  1739. max_changed = i;
  1740. }
  1741. }
  1742. if (min_changed <= max_changed) {
  1743. /*
  1744. * At least one colour changed (or we had an update scheduled
  1745. * already). Schedule a redraw event to pass the result back
  1746. * to the TermWin. This also requires invalidating the rest
  1747. * of the window, because usually all the text will need
  1748. * redrawing in the new colours.
  1749. * (If there was an update pending and this palette rebuild
  1750. * didn't actually change anything, we'll harmlessly reinforce
  1751. * the existing update request.)
  1752. */
  1753. term->win_palette_pending = true;
  1754. term->win_palette_pending_min = min_changed;
  1755. term->win_palette_pending_limit = max_changed + 1;
  1756. term_invalidate(term);
  1757. }
  1758. }
  1759. /*
  1760. * Rebuild the palette from configuration and platform colours.
  1761. * If 'keep_overrides' set, any escape-sequence-specified overrides will
  1762. * remain in place.
  1763. */
  1764. static void palette_reset(Terminal *term, bool keep_overrides)
  1765. {
  1766. for (unsigned i = 0; i < OSC4_NCOLOURS; i++)
  1767. term->subpalettes[SUBPAL_CONF].present[i] = true;
  1768. /*
  1769. * Copy all the palette information out of the Conf.
  1770. */
  1771. for (unsigned i = 0; i < CONF_NCOLOURS; i++) {
  1772. rgb *col = &term->subpalettes[SUBPAL_CONF].values[
  1773. colour_indices_conf_to_osc4[i]];
  1774. col->r = conf_get_int_int(term->conf, CONF_colours, i*3+0);
  1775. col->g = conf_get_int_int(term->conf, CONF_colours, i*3+1);
  1776. col->b = conf_get_int_int(term->conf, CONF_colours, i*3+2);
  1777. }
  1778. /*
  1779. * Directly invent the rest of the xterm-256 colours.
  1780. */
  1781. for (unsigned i = 0; i < 216; i++) {
  1782. rgb *col = &term->subpalettes[SUBPAL_CONF].values[i + 16];
  1783. int r = i / 36, g = (i / 6) % 6, b = i % 6;
  1784. col->r = r ? r * 40 + 55 : 0;
  1785. col->g = g ? g * 40 + 55 : 0;
  1786. col->b = b ? b * 40 + 55 : 0;
  1787. }
  1788. for (unsigned i = 0; i < 24; i++) {
  1789. rgb *col = &term->subpalettes[SUBPAL_CONF].values[i + 232];
  1790. int shade = i * 10 + 8;
  1791. col->r = col->g = col->b = shade;
  1792. }
  1793. /*
  1794. * Re-fetch any OS-local overrides.
  1795. */
  1796. for (unsigned i = 0; i < OSC4_NCOLOURS; i++)
  1797. term->subpalettes[SUBPAL_PLATFORM].present[i] = false;
  1798. win_palette_get_overrides(term->win, term);
  1799. if (!keep_overrides) {
  1800. /*
  1801. * Get rid of all escape-sequence configuration.
  1802. */
  1803. for (unsigned i = 0; i < OSC4_NCOLOURS; i++)
  1804. term->subpalettes[SUBPAL_SESSION].present[i] = false;
  1805. }
  1806. /*
  1807. * Rebuild the composite palette.
  1808. */
  1809. palette_rebuild(term);
  1810. }
  1811. void term_palette_override(Terminal *term, unsigned osc4_index, rgb rgb)
  1812. {
  1813. /*
  1814. * We never expect to be called except as re-entry from our own
  1815. * call to win_palette_get_overrides above, so we need not mess
  1816. * about calling palette_rebuild.
  1817. */
  1818. term->subpalettes[SUBPAL_PLATFORM].present[osc4_index] = true;
  1819. term->subpalettes[SUBPAL_PLATFORM].values[osc4_index] = rgb;
  1820. }
  1821. /*
  1822. * Initialise the terminal.
  1823. */
  1824. Terminal *term_init(Conf *myconf, struct unicode_data *ucsdata, TermWin *win)
  1825. {
  1826. Terminal *term;
  1827. /*
  1828. * Allocate a new Terminal structure and initialise the fields
  1829. * that need it.
  1830. */
  1831. term = snew(Terminal);
  1832. memset(term, 0, sizeof(Terminal));
  1833. term->win = win;
  1834. term->ucsdata = ucsdata;
  1835. term->conf = conf_copy(myconf);
  1836. term->compatibility_level = TM_PUTTY;
  1837. strcpy(term->id_string, "\033[?6c");
  1838. bufchain_init(&term->inbuf);
  1839. bufchain_init(&term->printer_buf);
  1840. term->has_focus = true;
  1841. term->termstate = TOPLEVEL;
  1842. term->selstate = NO_SELECTION;
  1843. term->answerback = strbuf_new();
  1844. term_copy_stuff_from_conf(term);
  1845. term->dispcursx = term->dispcursy = -1;
  1846. deselect(term);
  1847. term->rows = term->cols = -1;
  1848. power_on(term, true);
  1849. term->attr_mask = 0xffffffff;
  1850. /* FULL-TERMCHAR */
  1851. term->basic_erase_char.chr = CSET_ASCII | ' ';
  1852. term->basic_erase_char.attr = ATTR_DEFAULT;
  1853. term->basic_erase_char.truecolour.fg = optionalrgb_none;
  1854. term->basic_erase_char.truecolour.bg = optionalrgb_none;
  1855. term->erase_char = term->basic_erase_char;
  1856. /* TermWin implementations will typically extend these with
  1857. * clipboard ids they know about */
  1858. term->mouse_select_clipboards[0] = CLIP_LOCAL;
  1859. term->n_mouse_select_clipboards = 1;
  1860. term->mouse_paste_clipboard = CLIP_NULL;
  1861. term->trusted = true;
  1862. term->window_title = dupstr("");
  1863. term->icon_title = dupstr("");
  1864. term->wintitle_codepage = term->icontitle_codepage = DEFAULT_CODEPAGE;
  1865. term->win_resize_pending = WIN_RESIZE_NO;
  1866. term->bidi_ctx = bidi_new_context();
  1867. palette_reset(term, false);
  1868. return term;
  1869. }
  1870. void term_free(Terminal *term)
  1871. {
  1872. compressed_scrollback_line *cline;
  1873. termline *line;
  1874. struct beeptime *beep;
  1875. int i;
  1876. while ((cline = delpos234(term->scrollback, 0)) != NULL)
  1877. free_compressed_line(cline);
  1878. freetree234(term->scrollback);
  1879. while ((line = delpos234(term->screen, 0)) != NULL)
  1880. freetermline(line);
  1881. freetree234(term->screen);
  1882. while ((line = delpos234(term->alt_screen, 0)) != NULL)
  1883. freetermline(line);
  1884. freetree234(term->alt_screen);
  1885. if (term->disptext) {
  1886. for (i = 0; i < term->rows; i++)
  1887. freetermline(term->disptext[i]);
  1888. }
  1889. sfree(term->disptext);
  1890. while (term->beephead) {
  1891. beep = term->beephead;
  1892. term->beephead = beep->next;
  1893. sfree(beep);
  1894. }
  1895. bufchain_clear(&term->inbuf);
  1896. if (term->print_job)
  1897. printer_finish_job(term->print_job);
  1898. bufchain_clear(&term->printer_buf);
  1899. sfree(term->paste_buffer);
  1900. sfree(term->ltemp);
  1901. sfree(term->wcFrom);
  1902. sfree(term->wcTo);
  1903. strbuf_free(term->answerback);
  1904. for (i = 0; i < term->bidi_cache_size; i++) {
  1905. sfree(term->pre_bidi_cache[i].chars);
  1906. sfree(term->post_bidi_cache[i].chars);
  1907. sfree(term->post_bidi_cache[i].forward);
  1908. sfree(term->post_bidi_cache[i].backward);
  1909. }
  1910. sfree(term->pre_bidi_cache);
  1911. sfree(term->post_bidi_cache);
  1912. sfree(term->tabs);
  1913. expire_timer_context(term);
  1914. delete_callbacks_for_context(term);
  1915. conf_free(term->conf);
  1916. sfree(term->window_title);
  1917. sfree(term->icon_title);
  1918. bidi_free_context(term->bidi_ctx);
  1919. /* In case a term_userpass_state is still around */
  1920. if (term->userpass_state)
  1921. term_userpass_state_free(term->userpass_state);
  1922. sfree(term);
  1923. }
  1924. void term_set_trust_status(Terminal *term, bool trusted)
  1925. {
  1926. term->trusted = trusted;
  1927. }
  1928. void term_get_cursor_position(Terminal *term, int *x, int *y)
  1929. {
  1930. *x = term->curs.x;
  1931. *y = term->curs.y;
  1932. }
  1933. /*
  1934. * Set up the terminal for a given size.
  1935. */
  1936. void term_size(Terminal *term, int newrows, int newcols, int newsavelines)
  1937. {
  1938. tree234 *newalt;
  1939. termline **newdisp, *line;
  1940. int i, j, oldrows = term->rows;
  1941. int sblen;
  1942. int save_alt_which = term->alt_which;
  1943. if (newrows == term->rows && newcols == term->cols &&
  1944. newsavelines == term->savelines)
  1945. return; /* nothing to do */
  1946. /* Behave sensibly if we're given zero (or negative) rows/cols */
  1947. if (newrows < 1) newrows = 1;
  1948. if (newcols < 1) newcols = 1;
  1949. deselect(term);
  1950. swap_screen(term, 0, false, false);
  1951. term->alt_t = term->marg_t = 0;
  1952. term->alt_b = term->marg_b = newrows - 1;
  1953. if (term->rows == -1) {
  1954. term->scrollback = newtree234(NULL);
  1955. term->screen = newtree234(NULL);
  1956. term->tempsblines = 0;
  1957. term->rows = 0;
  1958. }
  1959. /*
  1960. * Resize the screen and scrollback. We only need to shift
  1961. * lines around within our data structures, because lineptr()
  1962. * will take care of resizing each individual line if
  1963. * necessary. So:
  1964. *
  1965. * - If the new screen is longer, we shunt lines in from temporary
  1966. * scrollback if possible, otherwise we add new blank lines at
  1967. * the bottom.
  1968. *
  1969. * - If the new screen is shorter, we remove any blank lines at
  1970. * the bottom if possible, otherwise shunt lines above the cursor
  1971. * to scrollback if possible, otherwise delete lines below the
  1972. * cursor.
  1973. *
  1974. * - Then, if the new scrollback length is less than the
  1975. * amount of scrollback we actually have, we must throw some
  1976. * away.
  1977. */
  1978. sblen = count234(term->scrollback);
  1979. /* Do this loop to expand the screen if newrows > rows */
  1980. assert(term->rows == count234(term->screen));
  1981. while (term->rows < newrows) {
  1982. if (term->tempsblines > 0) {
  1983. compressed_scrollback_line *cline;
  1984. /* Insert a line from the scrollback at the top of the screen. */
  1985. assert(sblen >= term->tempsblines);
  1986. cline = delpos234(term->scrollback, --sblen);
  1987. line = decompressline_and_free(cline);
  1988. line->temporary = false; /* reconstituted line is now real */
  1989. term->tempsblines -= 1;
  1990. addpos234(term->screen, line, 0);
  1991. term->curs.y += 1;
  1992. term->savecurs.y += 1;
  1993. term->alt_y += 1;
  1994. term->alt_savecurs.y += 1;
  1995. } else {
  1996. /* Add a new blank line at the bottom of the screen. */
  1997. line = newtermline(term, newcols, false);
  1998. addpos234(term->screen, line, count234(term->screen));
  1999. }
  2000. term->rows += 1;
  2001. }
  2002. /* Do this loop to shrink the screen if newrows < rows */
  2003. while (term->rows > newrows) {
  2004. if (term->curs.y < term->rows - 1) {
  2005. /* delete bottom row, unless it contains the cursor */
  2006. line = delpos234(term->screen, term->rows - 1);
  2007. freetermline(line);
  2008. } else {
  2009. /* push top row to scrollback */
  2010. line = delpos234(term->screen, 0);
  2011. addpos234(term->scrollback, compressline_and_free(line), sblen++);
  2012. term->tempsblines += 1;
  2013. term->curs.y -= 1;
  2014. term->savecurs.y -= 1;
  2015. term->alt_y -= 1;
  2016. term->alt_savecurs.y -= 1;
  2017. }
  2018. term->rows -= 1;
  2019. }
  2020. assert(term->rows == newrows);
  2021. assert(count234(term->screen) == newrows);
  2022. /* Delete any excess lines from the scrollback. */
  2023. while (sblen > newsavelines) {
  2024. line = delpos234(term->scrollback, 0);
  2025. sfree(line);
  2026. sblen--;
  2027. }
  2028. if (sblen < term->tempsblines)
  2029. term->tempsblines = sblen;
  2030. assert(count234(term->scrollback) <= newsavelines);
  2031. assert(count234(term->scrollback) >= term->tempsblines);
  2032. term->disptop = 0;
  2033. /* Make a new displayed text buffer. */
  2034. newdisp = snewn(newrows, termline *);
  2035. for (i = 0; i < newrows; i++) {
  2036. newdisp[i] = newtermline(term, newcols, false);
  2037. for (j = 0; j < newcols; j++)
  2038. newdisp[i]->chars[j].attr = ATTR_INVALID;
  2039. }
  2040. if (term->disptext) {
  2041. for (i = 0; i < oldrows; i++)
  2042. freetermline(term->disptext[i]);
  2043. }
  2044. sfree(term->disptext);
  2045. term->disptext = newdisp;
  2046. term->dispcursx = term->dispcursy = -1;
  2047. /* Make a new alternate screen. */
  2048. newalt = newtree234(NULL);
  2049. for (i = 0; i < newrows; i++) {
  2050. line = newtermline(term, newcols, true);
  2051. addpos234(newalt, line, i);
  2052. }
  2053. if (term->alt_screen) {
  2054. while (NULL != (line = delpos234(term->alt_screen, 0)))
  2055. freetermline(line);
  2056. freetree234(term->alt_screen);
  2057. }
  2058. term->alt_screen = newalt;
  2059. term->alt_sblines = 0;
  2060. term->tabs = sresize(term->tabs, newcols, unsigned char);
  2061. {
  2062. int i;
  2063. for (i = (term->cols > 0 ? term->cols : 0); i < newcols; i++)
  2064. term->tabs[i] = (i % 8 == 0 ? true : false);
  2065. }
  2066. /* Check that the cursor positions are still valid. */
  2067. if (term->savecurs.y < 0)
  2068. term->savecurs.y = 0;
  2069. if (term->savecurs.y >= newrows)
  2070. term->savecurs.y = newrows - 1;
  2071. if (term->savecurs.x >= newcols)
  2072. term->savecurs.x = newcols - 1;
  2073. if (term->alt_savecurs.y < 0)
  2074. term->alt_savecurs.y = 0;
  2075. if (term->alt_savecurs.y >= newrows)
  2076. term->alt_savecurs.y = newrows - 1;
  2077. if (term->alt_savecurs.x >= newcols)
  2078. term->alt_savecurs.x = newcols - 1;
  2079. if (term->curs.y < 0)
  2080. term->curs.y = 0;
  2081. if (term->curs.y >= newrows)
  2082. term->curs.y = newrows - 1;
  2083. if (term->curs.x >= newcols)
  2084. term->curs.x = newcols - 1;
  2085. if (term->alt_y < 0)
  2086. term->alt_y = 0;
  2087. if (term->alt_y >= newrows)
  2088. term->alt_y = newrows - 1;
  2089. if (term->alt_x >= newcols)
  2090. term->alt_x = newcols - 1;
  2091. term->alt_x = term->alt_y = 0;
  2092. term->wrapnext = false;
  2093. term->alt_wnext = false;
  2094. term->rows = newrows;
  2095. term->cols = newcols;
  2096. term->savelines = newsavelines;
  2097. swap_screen(term, save_alt_which, false, false);
  2098. term->win_scrollbar_update_pending = true;
  2099. term_schedule_update(term);
  2100. if (term->backend)
  2101. backend_size(term->backend, term->cols, term->rows);
  2102. }
  2103. void term_resize_request_completed(Terminal *term)
  2104. {
  2105. assert(term->win_resize_pending == WIN_RESIZE_AWAIT_REPLY);
  2106. term->win_resize_pending = WIN_RESIZE_NO;
  2107. queue_toplevel_callback(term_out_cb, term);
  2108. }
  2109. /*
  2110. * Hand a backend to the terminal, so it can be notified of resizes.
  2111. */
  2112. void term_provide_backend(Terminal *term, Backend *backend)
  2113. {
  2114. term->backend = backend;
  2115. if (term->backend && term->cols > 0 && term->rows > 0)
  2116. backend_size(term->backend, term->cols, term->rows);
  2117. }
  2118. /* Find the bottom line on the screen that has any content.
  2119. * If only the top line has content, returns 0.
  2120. * If no lines have content, return -1.
  2121. */
  2122. static int find_last_nonempty_line(Terminal *term, tree234 *screen)
  2123. {
  2124. int i;
  2125. for (i = count234(screen) - 1; i >= 0; i--) {
  2126. termline *line = index234(screen, i);
  2127. int j;
  2128. for (j = 0; j < line->cols; j++)
  2129. if (!termchars_equal(&line->chars[j], &term->erase_char))
  2130. break;
  2131. if (j != line->cols) break;
  2132. }
  2133. return i;
  2134. }
  2135. /*
  2136. * Swap screens. If `reset' is true and we have been asked to
  2137. * switch to the alternate screen, we must bring most of its
  2138. * configuration from the main screen and erase the contents of the
  2139. * alternate screen completely. (This is even true if we're already
  2140. * on it! Blame xterm.)
  2141. */
  2142. static void swap_screen(Terminal *term, int which,
  2143. bool reset, bool keep_cur_pos)
  2144. {
  2145. int t;
  2146. bool bt;
  2147. pos tp;
  2148. truecolour ttc;
  2149. tree234 *ttr;
  2150. if (!which)
  2151. reset = false; /* do no weird resetting if which==0 */
  2152. if (which != term->alt_which) {
  2153. if (term->erase_to_scrollback && term->alt_screen &&
  2154. term->alt_which && term->disptop < 0) {
  2155. /*
  2156. * We're swapping away from the alternate screen, so some
  2157. * lines are about to vanish from the virtual scrollback.
  2158. * Adjust disptop by that much, so that (if we're not
  2159. * resetting the scrollback anyway on a display event) the
  2160. * current scroll position still ends up pointing at the
  2161. * same text.
  2162. */
  2163. term->disptop += term->alt_sblines;
  2164. if (term->disptop > 0)
  2165. term->disptop = 0;
  2166. }
  2167. term->alt_which = which;
  2168. ttr = term->alt_screen;
  2169. term->alt_screen = term->screen;
  2170. term->screen = ttr;
  2171. term->alt_sblines = (
  2172. term->alt_screen ?
  2173. find_last_nonempty_line(term, term->alt_screen) + 1 : 0);
  2174. t = term->curs.x;
  2175. if (!reset && !keep_cur_pos)
  2176. term->curs.x = term->alt_x;
  2177. term->alt_x = t;
  2178. t = term->curs.y;
  2179. if (!reset && !keep_cur_pos)
  2180. term->curs.y = term->alt_y;
  2181. term->alt_y = t;
  2182. t = term->marg_t;
  2183. if (!reset) term->marg_t = term->alt_t;
  2184. term->alt_t = t;
  2185. t = term->marg_b;
  2186. if (!reset) term->marg_b = term->alt_b;
  2187. term->alt_b = t;
  2188. bt = term->dec_om;
  2189. if (!reset) term->dec_om = term->alt_om;
  2190. term->alt_om = bt;
  2191. bt = term->wrap;
  2192. if (!reset) term->wrap = term->alt_wrap;
  2193. term->alt_wrap = bt;
  2194. bt = term->wrapnext;
  2195. if (!reset) term->wrapnext = term->alt_wnext;
  2196. term->alt_wnext = bt;
  2197. bt = term->insert;
  2198. if (!reset) term->insert = term->alt_ins;
  2199. term->alt_ins = bt;
  2200. t = term->cset;
  2201. if (!reset) term->cset = term->alt_cset;
  2202. term->alt_cset = t;
  2203. bt = term->utf;
  2204. if (!reset) term->utf = term->alt_utf;
  2205. term->alt_utf = bt;
  2206. t = term->sco_acs;
  2207. if (!reset) term->sco_acs = term->alt_sco_acs;
  2208. term->alt_sco_acs = t;
  2209. tp = term->savecurs;
  2210. if (!reset)
  2211. term->savecurs = term->alt_savecurs;
  2212. term->alt_savecurs = tp;
  2213. t = term->save_cset;
  2214. if (!reset)
  2215. term->save_cset = term->alt_save_cset;
  2216. term->alt_save_cset = t;
  2217. t = term->save_csattr;
  2218. if (!reset)
  2219. term->save_csattr = term->alt_save_csattr;
  2220. term->alt_save_csattr = t;
  2221. t = term->save_attr;
  2222. if (!reset)
  2223. term->save_attr = term->alt_save_attr;
  2224. term->alt_save_attr = t;
  2225. ttc = term->save_truecolour;
  2226. if (!reset)
  2227. term->save_truecolour = term->alt_save_truecolour;
  2228. term->alt_save_truecolour = ttc;
  2229. bt = term->save_utf;
  2230. if (!reset)
  2231. term->save_utf = term->alt_save_utf;
  2232. term->alt_save_utf = bt;
  2233. bt = term->save_wnext;
  2234. if (!reset)
  2235. term->save_wnext = term->alt_save_wnext;
  2236. term->alt_save_wnext = bt;
  2237. t = term->save_sco_acs;
  2238. if (!reset)
  2239. term->save_sco_acs = term->alt_save_sco_acs;
  2240. term->alt_save_sco_acs = t;
  2241. if (term->erase_to_scrollback && term->alt_screen &&
  2242. term->alt_which && term->disptop < 0) {
  2243. /*
  2244. * Inverse of the adjustment at the top of this function.
  2245. * This time, we're swapping _to_ the alternate screen, so
  2246. * some lines are about to _appear_ in the virtual
  2247. * scrollback, and we adjust disptop in the other
  2248. * direction.
  2249. *
  2250. * Both these adjustments depend on the value stored in
  2251. * term->alt_sblines while the alt screen is selected,
  2252. * which is why we had to do one _before_ switching away
  2253. * from it and the other _after_ switching to it.
  2254. */
  2255. term->disptop -= term->alt_sblines;
  2256. int limit = -sblines(term);
  2257. if (term->disptop < limit)
  2258. term->disptop = limit;
  2259. }
  2260. }
  2261. if (reset && term->screen) {
  2262. /*
  2263. * Yes, this _is_ supposed to honour background-colour-erase.
  2264. */
  2265. erase_lots(term, false, true, true);
  2266. }
  2267. seen_disp_event(term);
  2268. }
  2269. /*
  2270. * Update the scroll bar.
  2271. */
  2272. static void update_sbar(Terminal *term)
  2273. {
  2274. int nscroll = sblines(term);
  2275. win_set_scrollbar(term->win, nscroll + term->rows,
  2276. nscroll + term->disptop, term->rows);
  2277. }
  2278. /*
  2279. * Check whether the region bounded by the two pointers intersects
  2280. * the scroll region, and de-select the on-screen selection if so.
  2281. */
  2282. static void check_selection(Terminal *term, pos from, pos to)
  2283. {
  2284. if (poslt(from, term->selend) && poslt(term->selstart, to))
  2285. deselect(term);
  2286. }
  2287. static void clear_line(Terminal *term, termline *line)
  2288. {
  2289. resizeline(term, line, term->cols);
  2290. for (int i = 0; i < term->cols; i++)
  2291. copy_termchar(line, i, &term->erase_char);
  2292. line->lattr = LATTR_NORM;
  2293. }
  2294. static void check_trust_status(Terminal *term, termline *line)
  2295. {
  2296. if (line->trusted != term->trusted) {
  2297. /*
  2298. * If we're displaying trusted output on a previously
  2299. * untrusted line, or vice versa, we need to switch the
  2300. * 'trusted' attribute on this terminal line, and also clear
  2301. * all its previous contents.
  2302. */
  2303. clear_line(term, line);
  2304. line->trusted = term->trusted;
  2305. }
  2306. }
  2307. /*
  2308. * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
  2309. * for backward.) `sb' is true if the scrolling is permitted to
  2310. * affect the scrollback buffer.
  2311. */
  2312. static void scroll(Terminal *term, int topline, int botline,
  2313. int lines, bool sb)
  2314. {
  2315. termline *line;
  2316. int seltop, scrollwinsize;
  2317. if (topline != 0 || term->alt_which != 0)
  2318. sb = false;
  2319. scrollwinsize = botline - topline + 1;
  2320. if (lines < 0) {
  2321. lines = -lines;
  2322. if (lines > scrollwinsize)
  2323. lines = scrollwinsize;
  2324. while (lines-- > 0) {
  2325. line = delpos234(term->screen, botline);
  2326. resizeline(term, line, term->cols);
  2327. clear_line(term, line);
  2328. addpos234(term->screen, line, topline);
  2329. if (term->selstart.y >= topline && term->selstart.y <= botline) {
  2330. term->selstart.y++;
  2331. if (term->selstart.y > botline) {
  2332. term->selstart.y = botline + 1;
  2333. term->selstart.x = 0;
  2334. }
  2335. }
  2336. if (term->selend.y >= topline && term->selend.y <= botline) {
  2337. term->selend.y++;
  2338. if (term->selend.y > botline) {
  2339. term->selend.y = botline + 1;
  2340. term->selend.x = 0;
  2341. }
  2342. }
  2343. }
  2344. } else {
  2345. if (lines > scrollwinsize)
  2346. lines = scrollwinsize;
  2347. while (lines-- > 0) {
  2348. line = delpos234(term->screen, topline);
  2349. #ifdef TERM_CC_DIAGS
  2350. cc_check(line);
  2351. #endif
  2352. if (sb && term->savelines > 0) {
  2353. int sblen = count234(term->scrollback);
  2354. /*
  2355. * We must add this line to the scrollback. We'll
  2356. * remove a line from the top of the scrollback if
  2357. * the scrollback is full.
  2358. */
  2359. if (sblen == term->savelines) {
  2360. compressed_scrollback_line *cline;
  2361. sblen--;
  2362. cline = delpos234(term->scrollback, 0);
  2363. free_compressed_line(cline);
  2364. } else
  2365. term->tempsblines += 1;
  2366. addpos234(term->scrollback, compressline_no_free(line), sblen);
  2367. /* now `line' itself can be reused as the bottom line */
  2368. /*
  2369. * If the user is currently looking at part of the
  2370. * scrollback, and they haven't enabled any options
  2371. * that are going to reset the scrollback as a
  2372. * result of this movement, then the chances are
  2373. * they'd like to keep looking at the same line. So
  2374. * we move their viewpoint at the same rate as the
  2375. * scroll, at least until their viewpoint hits the
  2376. * top end of the scrollback buffer, at which point
  2377. * we don't have the choice any more.
  2378. *
  2379. * Thanks to Jan Holmen Holsten for the idea and
  2380. * initial implementation.
  2381. */
  2382. if (term->disptop > -term->savelines && term->disptop < 0)
  2383. term->disptop--;
  2384. /*
  2385. * We've just modified the data that the terminal's
  2386. * scrollbar is based on, so remember to update it.
  2387. */
  2388. term->win_scrollbar_update_pending = true;
  2389. }
  2390. resizeline(term, line, term->cols);
  2391. clear_line(term, line);
  2392. line->trusted = false;
  2393. addpos234(term->screen, line, botline);
  2394. /*
  2395. * If the selection endpoints move into the scrollback,
  2396. * we keep them moving until they hit the top. However,
  2397. * of course, if the line _hasn't_ moved into the
  2398. * scrollback then we don't do this, and cut them off
  2399. * at the top of the scroll region.
  2400. *
  2401. * This applies to selstart and selend (for an existing
  2402. * selection), and also selanchor (for one being
  2403. * selected as we speak).
  2404. */
  2405. seltop = sb ? -term->savelines : topline;
  2406. if (term->selstate != NO_SELECTION) {
  2407. if (term->selstart.y >= seltop &&
  2408. term->selstart.y <= botline) {
  2409. term->selstart.y--;
  2410. if (term->selstart.y < seltop) {
  2411. term->selstart.y = seltop;
  2412. term->selstart.x = 0;
  2413. }
  2414. }
  2415. if (term->selend.y >= seltop && term->selend.y <= botline) {
  2416. term->selend.y--;
  2417. if (term->selend.y < seltop) {
  2418. term->selend.y = seltop;
  2419. term->selend.x = 0;
  2420. }
  2421. }
  2422. if (term->selanchor.y >= seltop &&
  2423. term->selanchor.y <= botline) {
  2424. term->selanchor.y--;
  2425. if (term->selanchor.y < seltop) {
  2426. term->selanchor.y = seltop;
  2427. term->selanchor.x = 0;
  2428. }
  2429. }
  2430. }
  2431. }
  2432. }
  2433. seen_disp_event(term);
  2434. }
  2435. /*
  2436. * Move the cursor to a given position, clipping at boundaries. We
  2437. * may or may not want to clip at the scroll margin: marg_clip is 0
  2438. * not to, 1 to disallow _passing_ the margins, and 2 to disallow
  2439. * even _being_ outside the margins.
  2440. */
  2441. static void move(Terminal *term, int x, int y, int marg_clip)
  2442. {
  2443. if (x < 0)
  2444. x = 0;
  2445. if (x >= term->cols)
  2446. x = term->cols - 1;
  2447. if (marg_clip) {
  2448. if ((term->curs.y >= term->marg_t || marg_clip == 2) &&
  2449. y < term->marg_t)
  2450. y = term->marg_t;
  2451. if ((term->curs.y <= term->marg_b || marg_clip == 2) &&
  2452. y > term->marg_b)
  2453. y = term->marg_b;
  2454. }
  2455. if (y < 0)
  2456. y = 0;
  2457. if (y >= term->rows)
  2458. y = term->rows - 1;
  2459. term->curs.x = x;
  2460. term->curs.y = y;
  2461. term->wrapnext = false;
  2462. seen_disp_event(term);
  2463. }
  2464. /*
  2465. * Save or restore the cursor and SGR mode.
  2466. */
  2467. static void save_cursor(Terminal *term, bool save)
  2468. {
  2469. if (save) {
  2470. term->savecurs = term->curs;
  2471. term->save_attr = term->curr_attr;
  2472. term->save_truecolour = term->curr_truecolour;
  2473. term->save_cset = term->cset;
  2474. term->save_utf = term->utf;
  2475. term->save_wnext = term->wrapnext;
  2476. term->save_csattr = term->cset_attr[term->cset];
  2477. term->save_sco_acs = term->sco_acs;
  2478. } else {
  2479. term->curs = term->savecurs;
  2480. /* Make sure the window hasn't shrunk since the save */
  2481. if (term->curs.x >= term->cols)
  2482. term->curs.x = term->cols - 1;
  2483. if (term->curs.y >= term->rows)
  2484. term->curs.y = term->rows - 1;
  2485. term->curr_attr = term->save_attr;
  2486. term->curr_truecolour = term->save_truecolour;
  2487. term->cset = term->save_cset;
  2488. term->utf = term->save_utf;
  2489. term->wrapnext = term->save_wnext;
  2490. /*
  2491. * wrapnext might reset to False if the x position is no
  2492. * longer at the rightmost edge.
  2493. */
  2494. if (term->wrapnext && term->curs.x < term->cols-1)
  2495. term->wrapnext = false;
  2496. term->cset_attr[term->cset] = term->save_csattr;
  2497. term->sco_acs = term->save_sco_acs;
  2498. set_erase_char(term);
  2499. seen_disp_event(term);
  2500. }
  2501. }
  2502. /*
  2503. * This function is called before doing _anything_ which affects
  2504. * only part of a line of text. It is used to mark the boundary
  2505. * between two character positions, and it indicates that some sort
  2506. * of effect is going to happen on only one side of that boundary.
  2507. *
  2508. * The effect of this function is to check whether a CJK
  2509. * double-width character is straddling the boundary, and to remove
  2510. * it and replace it with two spaces if so. (Of course, one or
  2511. * other of those spaces is then likely to be replaced with
  2512. * something else again, as a result of whatever happens next.)
  2513. *
  2514. * Also, if the boundary is at the right-hand _edge_ of the screen,
  2515. * it implies something deliberate is being done to the rightmost
  2516. * column position; hence we must clear LATTR_WRAPPED2.
  2517. *
  2518. * The input to the function is the coordinates of the _second_
  2519. * character of the pair.
  2520. */
  2521. static void check_boundary(Terminal *term, int x, int y)
  2522. {
  2523. termline *ldata;
  2524. /* Validate input coordinates, just in case. */
  2525. if (x <= 0 || x > term->cols)
  2526. return;
  2527. ldata = scrlineptr(y);
  2528. check_trust_status(term, ldata);
  2529. check_line_size(term, ldata);
  2530. if (x == term->cols) {
  2531. ldata->lattr &= ~LATTR_WRAPPED2;
  2532. } else {
  2533. if (ldata->chars[x].chr == UCSWIDE) {
  2534. clear_cc(ldata, x-1);
  2535. clear_cc(ldata, x);
  2536. ldata->chars[x-1].chr = ' ' | CSET_ASCII;
  2537. ldata->chars[x] = ldata->chars[x-1];
  2538. }
  2539. }
  2540. }
  2541. /*
  2542. * Erase a large portion of the screen: the whole screen, or the
  2543. * whole line, or parts thereof.
  2544. */
  2545. static void erase_lots(Terminal *term,
  2546. bool line_only, bool from_begin, bool to_end)
  2547. {
  2548. pos start, end;
  2549. bool erase_lattr;
  2550. bool erasing_lines_from_top = false;
  2551. if (line_only) {
  2552. start.y = term->curs.y;
  2553. start.x = 0;
  2554. end.y = term->curs.y + 1;
  2555. end.x = 0;
  2556. erase_lattr = false;
  2557. } else {
  2558. start.y = 0;
  2559. start.x = 0;
  2560. end.y = term->rows;
  2561. end.x = 0;
  2562. erase_lattr = true;
  2563. }
  2564. /* This is the endpoint of the clearing operation that is not
  2565. * either the start or end of the line / screen. */
  2566. pos boundary = term->curs;
  2567. if (!from_begin) {
  2568. /*
  2569. * If we're erasing from the current char to the end of
  2570. * line/screen, then we take account of wrapnext, so as to
  2571. * maintain the invariant that writing a printing character
  2572. * followed by ESC[K should not overwrite the character you
  2573. * _just wrote_. That is, when wrapnext says the cursor is
  2574. * 'logically' at the very rightmost edge of the screen
  2575. * instead of just before the last printing char, ESC[K should
  2576. * do nothing at all, and ESC[J should clear the next line but
  2577. * leave this one unchanged.
  2578. *
  2579. * This adjusted position will also be the position we use for
  2580. * check_boundary (i.e. the thing we ensure isn't in the
  2581. * middle of a double-width printing char).
  2582. */
  2583. if (term->wrapnext)
  2584. incpos(boundary);
  2585. start = boundary;
  2586. }
  2587. if (!to_end) {
  2588. /*
  2589. * If we're erasing from the start of (at least) the line _to_
  2590. * the current position, then that is taken to mean 'inclusive
  2591. * of the cell under the cursor', which means we don't
  2592. * consider wrapnext at all: whether it's set or not, we still
  2593. * clear the cell under the cursor.
  2594. *
  2595. * Again, that incremented boundary position is where we
  2596. * should be careful of a straddling wide character.
  2597. */
  2598. incpos(boundary);
  2599. end = boundary;
  2600. }
  2601. if (!from_begin || !to_end)
  2602. check_boundary(term, boundary.x, boundary.y);
  2603. check_selection(term, start, end);
  2604. /* Clear screen also forces a full window redraw, just in case. */
  2605. if (start.y == 0 && start.x == 0 && end.y == term->rows)
  2606. term_invalidate(term);
  2607. /* Lines scrolled away shouldn't be brought back on if the terminal
  2608. * resizes. */
  2609. if (start.y == 0 && start.x == 0 && end.x == 0 && erase_lattr)
  2610. erasing_lines_from_top = true;
  2611. if (term->erase_to_scrollback && erasing_lines_from_top) {
  2612. /* If it's a whole number of lines, starting at the top, and
  2613. * we're fully erasing them, erase by scrolling and keep the
  2614. * lines in the scrollback. */
  2615. int scrolllines = end.y;
  2616. if (end.y == term->rows) {
  2617. /* Shrink until we find a non-empty row.*/
  2618. scrolllines = find_last_nonempty_line(term, term->screen) + 1;
  2619. }
  2620. if (scrolllines > 0)
  2621. scroll(term, 0, scrolllines - 1, scrolllines, true);
  2622. } else {
  2623. termline *ldata = scrlineptr(start.y);
  2624. check_trust_status(term, ldata);
  2625. while (poslt(start, end)) {
  2626. check_line_size(term, ldata);
  2627. if (start.x == term->cols) {
  2628. if (!erase_lattr)
  2629. ldata->lattr &= ~(LATTR_WRAPPED | LATTR_WRAPPED2);
  2630. else
  2631. ldata->lattr = LATTR_NORM;
  2632. } else {
  2633. copy_termchar(ldata, start.x, &term->erase_char);
  2634. }
  2635. if (incpos(start) && start.y < term->rows) {
  2636. ldata = scrlineptr(start.y);
  2637. check_trust_status(term, ldata);
  2638. }
  2639. }
  2640. }
  2641. /* After an erase of lines from the top of the screen, we shouldn't
  2642. * bring the lines back again if the terminal enlarges (since the user or
  2643. * application has explicitly thrown them away). */
  2644. if (erasing_lines_from_top && !(term->alt_which))
  2645. term->tempsblines = 0;
  2646. seen_disp_event(term);
  2647. }
  2648. /*
  2649. * Insert or delete characters within the current line. n is +ve if
  2650. * insertion is desired, and -ve for deletion.
  2651. */
  2652. static void insch(Terminal *term, int n)
  2653. {
  2654. int dir = (n < 0 ? -1 : +1);
  2655. int m, j;
  2656. pos eol;
  2657. termline *ldata;
  2658. n = (n < 0 ? -n : n);
  2659. if (n > term->cols - term->curs.x)
  2660. n = term->cols - term->curs.x;
  2661. m = term->cols - term->curs.x - n;
  2662. /*
  2663. * We must de-highlight the selection if it overlaps any part of
  2664. * the region affected by this operation, i.e. the region from the
  2665. * current cursor position to end-of-line, _unless_ the entirety
  2666. * of the selection is going to be moved to the left or right by
  2667. * this operation but otherwise unchanged, in which case we can
  2668. * simply move the highlight with the text.
  2669. */
  2670. eol.y = term->curs.y;
  2671. eol.x = term->cols;
  2672. if (poslt(term->curs, term->selend) && poslt(term->selstart, eol)) {
  2673. pos okstart = term->curs;
  2674. pos okend = eol;
  2675. if (dir > 0) {
  2676. /* Insertion: n characters at EOL will be splatted. */
  2677. okend.x -= n;
  2678. } else {
  2679. /* Deletion: n characters at cursor position will be splatted. */
  2680. okstart.x += n;
  2681. }
  2682. if (posle(okstart, term->selstart) && posle(term->selend, okend)) {
  2683. /* Selection is contained entirely in the interval
  2684. * [okstart,okend), so we need only adjust the selection
  2685. * bounds. */
  2686. term->selstart.x += dir * n;
  2687. term->selend.x += dir * n;
  2688. assert(term->selstart.x >= term->curs.x);
  2689. assert(term->selstart.x < term->cols);
  2690. assert(term->selend.x > term->curs.x);
  2691. assert(term->selend.x <= term->cols);
  2692. } else {
  2693. /* Selection is not wholly contained in that interval, so
  2694. * we must unhighlight it. */
  2695. deselect(term);
  2696. }
  2697. }
  2698. check_boundary(term, term->curs.x, term->curs.y);
  2699. if (dir < 0)
  2700. check_boundary(term, term->curs.x + n, term->curs.y);
  2701. ldata = scrlineptr(term->curs.y);
  2702. check_trust_status(term, ldata);
  2703. if (dir < 0) {
  2704. for (j = 0; j < m; j++)
  2705. move_termchar(ldata,
  2706. ldata->chars + term->curs.x + j,
  2707. ldata->chars + term->curs.x + j + n);
  2708. while (n--)
  2709. copy_termchar(ldata, term->curs.x + m++, &term->erase_char);
  2710. } else {
  2711. for (j = m; j-- ;)
  2712. move_termchar(ldata,
  2713. ldata->chars + term->curs.x + j + n,
  2714. ldata->chars + term->curs.x + j);
  2715. while (n--)
  2716. copy_termchar(ldata, term->curs.x + n, &term->erase_char);
  2717. }
  2718. }
  2719. static void term_update_raw_mouse_mode(Terminal *term)
  2720. {
  2721. bool want_raw = (term->xterm_mouse != 0 && !term->xterm_mouse_forbidden);
  2722. win_set_raw_mouse_mode(term->win, want_raw);
  2723. term->win_pointer_shape_pending = true;
  2724. term->win_pointer_shape_raw = want_raw;
  2725. term_schedule_update(term);
  2726. }
  2727. static void term_request_resize(Terminal *term, int cols, int rows)
  2728. {
  2729. if (term->cols == cols && term->rows == rows)
  2730. return; /* don't need to do anything */
  2731. term->win_resize_pending = WIN_RESIZE_NEED_SEND;
  2732. term->win_resize_pending_w = cols;
  2733. term->win_resize_pending_h = rows;
  2734. term_schedule_update(term);
  2735. }
  2736. /*
  2737. * Toggle terminal mode `mode' to state `state'. (`query' indicates
  2738. * whether the mode is a DEC private one or a normal one.)
  2739. */
  2740. static void toggle_mode(Terminal *term, int mode, int query, bool state)
  2741. {
  2742. if (query == 1) {
  2743. switch (mode) {
  2744. case 1: /* DECCKM: application cursor keys */
  2745. term->app_cursor_keys = state;
  2746. break;
  2747. case 2: /* DECANM: VT52 mode */
  2748. term->vt52_mode = !state;
  2749. if (term->vt52_mode) {
  2750. term->blink_is_real = false;
  2751. term->vt52_bold = false;
  2752. } else {
  2753. term->blink_is_real = term->blinktext;
  2754. }
  2755. term_schedule_tblink(term);
  2756. break;
  2757. case 3: /* DECCOLM: 80/132 columns */
  2758. deselect(term);
  2759. if (!term->no_remote_resize)
  2760. term_request_resize(term, state ? 132 : 80, term->rows);
  2761. term->reset_132 = state;
  2762. term->alt_t = term->marg_t = 0;
  2763. term->alt_b = term->marg_b = term->rows - 1;
  2764. move(term, 0, 0, 0);
  2765. erase_lots(term, false, true, true);
  2766. break;
  2767. case 5: /* DECSCNM: reverse video */
  2768. /*
  2769. * Toggle reverse video. If we receive an OFF within the
  2770. * visual bell timeout period after an ON, we trigger an
  2771. * effective visual bell, so that ESC[?5hESC[?5l will
  2772. * always be an actually _visible_ visual bell.
  2773. */
  2774. if (term->rvideo && !state) {
  2775. /* This is an OFF, so set up a vbell */
  2776. term_schedule_vbell(term, true, term->rvbell_startpoint);
  2777. } else if (!term->rvideo && state) {
  2778. /* This is an ON, so we notice the time and save it. */
  2779. term->rvbell_startpoint = GETTICKCOUNT();
  2780. }
  2781. term->rvideo = state;
  2782. seen_disp_event(term);
  2783. break;
  2784. case 6: /* DECOM: DEC origin mode */
  2785. term->dec_om = state;
  2786. break;
  2787. case 7: /* DECAWM: auto wrap */
  2788. term->wrap = state;
  2789. if (!term->wrap)
  2790. term->wrapnext = false;
  2791. break;
  2792. case 8: /* DECARM: auto key repeat */
  2793. term->repeat_off = !state;
  2794. break;
  2795. case 25: /* DECTCEM: enable/disable cursor */
  2796. compatibility2(OTHER, VT220);
  2797. term->cursor_on = state;
  2798. seen_disp_event(term);
  2799. break;
  2800. case 47: /* alternate screen */
  2801. compatibility(OTHER);
  2802. deselect(term);
  2803. swap_screen(term, term->no_alt_screen ? 0 : state, false, false);
  2804. if (term->scroll_on_disp)
  2805. term->disptop = 0;
  2806. break;
  2807. case 1000: /* xterm mouse 1 (normal) */
  2808. term->xterm_mouse = state ? 1 : 0;
  2809. term_update_raw_mouse_mode(term);
  2810. break;
  2811. case 1002: /* xterm mouse 2 (inc. button drags) */
  2812. term->xterm_mouse = state ? 2 : 0;
  2813. term_update_raw_mouse_mode(term);
  2814. break;
  2815. case 1003: /* xterm mouse any-event tracking */
  2816. term->xterm_mouse = state ? 3 : 0;
  2817. term_update_raw_mouse_mode(term);
  2818. break;
  2819. case 1006: /* xterm extended mouse */
  2820. term->xterm_extended_mouse = state;
  2821. break;
  2822. case 1015: /* urxvt extended mouse */
  2823. term->urxvt_extended_mouse = state;
  2824. break;
  2825. case 1047: /* alternate screen */
  2826. compatibility(OTHER);
  2827. deselect(term);
  2828. swap_screen(term, term->no_alt_screen ? 0 : state, true, true);
  2829. if (term->scroll_on_disp)
  2830. term->disptop = 0;
  2831. break;
  2832. case 1048: /* save/restore cursor */
  2833. if (!term->no_alt_screen)
  2834. save_cursor(term, state);
  2835. if (!state) seen_disp_event(term);
  2836. break;
  2837. case 1049: /* cursor & alternate screen */
  2838. if (state && !term->no_alt_screen)
  2839. save_cursor(term, state);
  2840. if (!state) seen_disp_event(term);
  2841. compatibility(OTHER);
  2842. deselect(term);
  2843. swap_screen(term, term->no_alt_screen ? 0 : state, true, false);
  2844. if (!state && !term->no_alt_screen)
  2845. save_cursor(term, state);
  2846. if (term->scroll_on_disp)
  2847. term->disptop = 0;
  2848. break;
  2849. case 2004: /* xterm bracketed paste */
  2850. term->bracketed_paste = state ? true : false;
  2851. break;
  2852. }
  2853. } else if (query == 0) {
  2854. switch (mode) {
  2855. case 4: /* IRM: set insert mode */
  2856. compatibility(VT102);
  2857. term->insert = state;
  2858. break;
  2859. case 12: /* SRM: set echo mode */
  2860. term->srm_echo = !state;
  2861. break;
  2862. case 20: /* LNM: Return sends ... */
  2863. term->cr_lf_return = state;
  2864. break;
  2865. case 34: /* WYULCURM: Make cursor BIG */
  2866. compatibility2(OTHER, VT220);
  2867. term->big_cursor = !state;
  2868. }
  2869. }
  2870. }
  2871. /*
  2872. * Process an OSC sequence: set window title or icon name.
  2873. */
  2874. static void do_osc(Terminal *term)
  2875. {
  2876. if (term->osc_is_apc) {
  2877. /* This OSC was really an APC, and we don't support that
  2878. * sequence at all. We only recognise it in order to ignore it
  2879. * and filter it out of input. */
  2880. return;
  2881. }
  2882. if (term->osc_w) {
  2883. while (term->osc_strlen--)
  2884. term->wordness[(unsigned char)term->osc_string[term->osc_strlen]] =
  2885. term->esc_args[0];
  2886. } else {
  2887. term->osc_string[term->osc_strlen] = '\0';
  2888. switch (term->esc_args[0]) {
  2889. case 0:
  2890. case 1:
  2891. if (!term->no_remote_wintitle) {
  2892. sfree(term->icon_title);
  2893. term->icon_title = dupstr(term->osc_string);
  2894. term->icontitle_codepage = term->ucsdata->line_codepage;
  2895. term->win_icon_title_pending = true;
  2896. term_schedule_update(term);
  2897. }
  2898. if (term->esc_args[0] == 1)
  2899. break;
  2900. /* fall through: parameter 0 means set both */
  2901. case 2:
  2902. case 21:
  2903. if (!term->no_remote_wintitle) {
  2904. sfree(term->window_title);
  2905. term->window_title = dupstr(term->osc_string);
  2906. term->wintitle_codepage = term->ucsdata->line_codepage;
  2907. term->win_title_pending = true;
  2908. term_schedule_update(term);
  2909. }
  2910. break;
  2911. case 4:
  2912. if (term->ldisc && !strcmp(term->osc_string, "?")) {
  2913. unsigned index = term->esc_args[1];
  2914. if (index < OSC4_NCOLOURS) {
  2915. rgb colour = term->palette[index];
  2916. char *reply_buf = dupprintf(
  2917. "\033]4;%u;rgb:%04x/%04x/%04x\007", index,
  2918. (unsigned)colour.r * 0x0101,
  2919. (unsigned)colour.g * 0x0101,
  2920. (unsigned)colour.b * 0x0101);
  2921. ldisc_send(term->ldisc, reply_buf, strlen(reply_buf),
  2922. false);
  2923. sfree(reply_buf);
  2924. }
  2925. }
  2926. break;
  2927. }
  2928. }
  2929. }
  2930. /*
  2931. * ANSI printing routines.
  2932. */
  2933. static void term_print_setup(Terminal *term, char *printer)
  2934. {
  2935. bufchain_clear(&term->printer_buf);
  2936. term->print_job = printer_start_job(printer);
  2937. }
  2938. static void term_print_flush(Terminal *term)
  2939. {
  2940. size_t size;
  2941. while ((size = bufchain_size(&term->printer_buf)) > 5) {
  2942. ptrlen data = bufchain_prefix(&term->printer_buf);
  2943. if (data.len > size-5)
  2944. data.len = size-5;
  2945. printer_job_data(term->print_job, data.ptr, data.len);
  2946. bufchain_consume(&term->printer_buf, data.len);
  2947. }
  2948. }
  2949. static void term_print_finish(Terminal *term)
  2950. {
  2951. size_t size;
  2952. char c;
  2953. if (!term->printing && !term->only_printing)
  2954. return; /* we need do nothing */
  2955. term_print_flush(term);
  2956. while ((size = bufchain_size(&term->printer_buf)) > 0) {
  2957. ptrlen data = bufchain_prefix(&term->printer_buf);
  2958. c = *(char *)data.ptr;
  2959. if (c == '\033' || c == '\233') {
  2960. bufchain_consume(&term->printer_buf, size);
  2961. break;
  2962. } else {
  2963. printer_job_data(term->print_job, &c, 1);
  2964. bufchain_consume(&term->printer_buf, 1);
  2965. }
  2966. }
  2967. printer_finish_job(term->print_job);
  2968. term->print_job = NULL;
  2969. term->printing = term->only_printing = false;
  2970. }
  2971. static void term_display_graphic_char(Terminal *term, unsigned long c)
  2972. {
  2973. termline *cline = scrlineptr(term->curs.y);
  2974. int width = 0;
  2975. if (DIRECT_CHAR(c))
  2976. width = 1;
  2977. if (!width)
  2978. width = term_char_width(term, c);
  2979. if (term->wrapnext && term->wrap && width > 0) {
  2980. cline->lattr |= LATTR_WRAPPED;
  2981. if (term->curs.y == term->marg_b)
  2982. scroll(term, term->marg_t, term->marg_b, 1, true);
  2983. else if (term->curs.y < term->rows - 1)
  2984. term->curs.y++;
  2985. term->curs.x = 0;
  2986. term->wrapnext = false;
  2987. cline = scrlineptr(term->curs.y);
  2988. }
  2989. if (term->insert && width > 0)
  2990. insch(term, width);
  2991. if (term->selstate != NO_SELECTION) {
  2992. pos cursplus = term->curs;
  2993. incpos(cursplus);
  2994. check_selection(term, term->curs, cursplus);
  2995. }
  2996. if (((c & CSET_MASK) == CSET_ASCII ||
  2997. (c & CSET_MASK) == 0) && term->logctx)
  2998. logtraffic(term->logctx, (unsigned char) c, LGTYP_ASCII);
  2999. check_trust_status(term, cline);
  3000. int linecols = term->cols;
  3001. if (cline->trusted)
  3002. linecols -= TRUST_SIGIL_WIDTH;
  3003. /*
  3004. * Before we switch on the character width, do a preliminary check for
  3005. * cases where we might have no room at all to display a double-width
  3006. * character. Our fallback is to substitute REPLACEMENT CHARACTER,
  3007. * which is single-width, and it's easiest to do that _before_ having
  3008. * to 'goto' from one switch case to another.
  3009. */
  3010. if (width == 2 && term->curs.x >= linecols-1) {
  3011. /*
  3012. * If we're in wrapping mode and the terminal is at least 2 cells
  3013. * wide, it's OK, we have a fallback. But otherwise, substitute.
  3014. */
  3015. if (linecols < 2 || !term->wrap) {
  3016. width = 1;
  3017. c = 0xFFFD;
  3018. }
  3019. }
  3020. switch (width) {
  3021. case 2:
  3022. /*
  3023. * If we're about to display a double-width character starting in
  3024. * the rightmost column (and we're in wrapping mode - the other
  3025. * case was disposed of above), then we do something special
  3026. * instead. We must print a space in the last column of the screen,
  3027. * then wrap; and we also set LATTR_WRAPPED2 which instructs
  3028. * subsequent cut-and-pasting not only to splice this line to the
  3029. * one after it, but to ignore the space in the last character
  3030. * position as well. (Because what was actually output to the
  3031. * terminal was presumably just a sequence of CJK characters, and
  3032. * we don't want a space to be pasted in the middle of those just
  3033. * because they had the misfortune to start in the wrong parity
  3034. * column. xterm concurs.)
  3035. */
  3036. check_boundary(term, term->curs.x, term->curs.y);
  3037. check_boundary(term, term->curs.x+2, term->curs.y);
  3038. if (term->curs.x >= linecols-1) {
  3039. assert(term->wrap); /* we handled the non-wrapping case above */
  3040. copy_termchar(cline, term->curs.x,
  3041. &term->erase_char);
  3042. cline->lattr |= LATTR_WRAPPED | LATTR_WRAPPED2;
  3043. if (term->curs.y == term->marg_b)
  3044. scroll(term, term->marg_t, term->marg_b,
  3045. 1, true);
  3046. else if (term->curs.y < term->rows - 1)
  3047. term->curs.y++;
  3048. term->curs.x = 0;
  3049. cline = scrlineptr(term->curs.y);
  3050. /* Now we must check_boundary again, of course. */
  3051. check_boundary(term, term->curs.x, term->curs.y);
  3052. check_boundary(term, term->curs.x+2, term->curs.y);
  3053. }
  3054. /* FULL-TERMCHAR */
  3055. clear_cc(cline, term->curs.x);
  3056. cline->chars[term->curs.x].chr = c;
  3057. cline->chars[term->curs.x].attr = term->curr_attr;
  3058. cline->chars[term->curs.x].truecolour =
  3059. term->curr_truecolour;
  3060. term->curs.x++;
  3061. /* FULL-TERMCHAR */
  3062. clear_cc(cline, term->curs.x);
  3063. cline->chars[term->curs.x].chr = UCSWIDE;
  3064. cline->chars[term->curs.x].attr = term->curr_attr;
  3065. cline->chars[term->curs.x].truecolour =
  3066. term->curr_truecolour;
  3067. break;
  3068. case 1:
  3069. check_boundary(term, term->curs.x, term->curs.y);
  3070. check_boundary(term, term->curs.x+1, term->curs.y);
  3071. /* FULL-TERMCHAR */
  3072. clear_cc(cline, term->curs.x);
  3073. cline->chars[term->curs.x].chr = c;
  3074. cline->chars[term->curs.x].attr = term->curr_attr;
  3075. cline->chars[term->curs.x].truecolour =
  3076. term->curr_truecolour;
  3077. break;
  3078. case 0:
  3079. if (term->curs.x > 0) {
  3080. int x = term->curs.x - 1;
  3081. /* If we're in wrapnext state, the character to combine
  3082. * with is _here_, not to our left. */
  3083. if (term->wrapnext)
  3084. x++;
  3085. /*
  3086. * If the previous character is UCSWIDE, back up another
  3087. * one.
  3088. */
  3089. if (cline->chars[x].chr == UCSWIDE) {
  3090. assert(x > 0);
  3091. x--;
  3092. }
  3093. add_cc(cline, x, c);
  3094. seen_disp_event(term);
  3095. }
  3096. return;
  3097. default:
  3098. return;
  3099. }
  3100. term->curs.x++;
  3101. if (term->curs.x >= linecols) {
  3102. term->curs.x = linecols - 1;
  3103. if (term->wrap) {
  3104. if (!term->vt52_mode) {
  3105. /* Set the wrapnext flag, so that the next character
  3106. * wraps, but this one doesn't. */
  3107. term->wrapnext = true;
  3108. } else {
  3109. /* VT52 mode expects simpler handling, and we just
  3110. * wrap straight away. */
  3111. cline->lattr |= LATTR_WRAPPED;
  3112. if (term->curs.y == term->marg_b)
  3113. scroll(term, term->marg_t, term->marg_b, 1, true);
  3114. else if (term->curs.y < term->rows - 1)
  3115. term->curs.y++;
  3116. term->curs.x = 0;
  3117. term->wrapnext = false;
  3118. }
  3119. }
  3120. }
  3121. seen_disp_event(term);
  3122. }
  3123. static strbuf *term_input_data_from_unicode(
  3124. Terminal *term, const wchar_t *widebuf, size_t len)
  3125. {
  3126. strbuf *buf = strbuf_new();
  3127. if (in_utf(term)) {
  3128. /*
  3129. * Translate input wide characters into UTF-8 to go in the
  3130. * terminal's input data queue.
  3131. */
  3132. for (size_t i = 0; i < len; i++) {
  3133. unsigned long ch = widebuf[i];
  3134. if (IS_SURROGATE(ch)) {
  3135. #ifdef PLATFORM_IS_UTF16
  3136. if (i+1 < len) {
  3137. unsigned long ch2 = widebuf[i+1];
  3138. if (IS_SURROGATE_PAIR(ch, ch2)) {
  3139. ch = FROM_SURROGATES(ch, ch2);
  3140. i++;
  3141. }
  3142. } else
  3143. #endif
  3144. {
  3145. /* Unrecognised UTF-16 sequence */
  3146. ch = '.';
  3147. }
  3148. }
  3149. put_utf8_char(buf, ch);
  3150. }
  3151. } else {
  3152. /*
  3153. * Call to the character-set subsystem to translate into
  3154. * whatever charset the terminal is currently configured in.
  3155. *
  3156. * Since the terminal doesn't currently support any multibyte
  3157. * character set other than UTF-8, we can assume here that
  3158. * there will be at most one output byte per input wchar_t.
  3159. * (But also we must allow space for the trailing NUL that
  3160. * wc_to_mb will write.)
  3161. */
  3162. put_wc_to_mb(buf, term->ucsdata->line_codepage, widebuf, len, "");
  3163. }
  3164. return buf;
  3165. }
  3166. static strbuf *term_input_data_from_charset(
  3167. Terminal *term, int codepage, const char *str, size_t len)
  3168. {
  3169. strbuf *buf = strbuf_new();
  3170. if (codepage < 0)
  3171. put_data(buf, str, len);
  3172. else
  3173. put_mb_to_wc(buf, codepage, str, len);
  3174. return buf;
  3175. }
  3176. static inline void term_bracketed_paste_start(Terminal *term)
  3177. {
  3178. ptrlen seq = PTRLEN_LITERAL("\033[200~");
  3179. if (term->ldisc)
  3180. ldisc_send(term->ldisc, seq.ptr, seq.len, false);
  3181. term->bracketed_paste_active = true;
  3182. }
  3183. static inline void term_bracketed_paste_stop(Terminal *term)
  3184. {
  3185. if (!term->bracketed_paste_active)
  3186. return;
  3187. ptrlen seq = PTRLEN_LITERAL("\033[201~");
  3188. if (term->ldisc)
  3189. ldisc_send(term->ldisc, seq.ptr, seq.len, false);
  3190. term->bracketed_paste_active = false;
  3191. }
  3192. static inline void term_keyinput_internal(
  3193. Terminal *term, const void *buf, int len, bool interactive)
  3194. {
  3195. if (term->srm_echo) {
  3196. /*
  3197. * Implement the terminal-level local echo behaviour that
  3198. * ECMA-48 specifies when terminal mode 12 is configured off
  3199. * (ESC[12l). In this mode, data input to the terminal via the
  3200. * keyboard is also added to the output buffer. But this
  3201. * doesn't apply to escape sequences generated as session
  3202. * input _within_ the terminal, e.g. in response to terminal
  3203. * query sequences, or the bracketing sequences of bracketed
  3204. * paste mode. Those will be sent directly via
  3205. * ldisc_send(term->ldisc, ...) and won't go through this
  3206. * function.
  3207. */
  3208. /* Mimic the special case of negative length in ldisc_send */
  3209. int true_len = len >= 0 ? len : strlen(buf);
  3210. bufchain_add(&term->inbuf, buf, true_len);
  3211. term_added_data(term, false);
  3212. }
  3213. if (interactive)
  3214. term_bracketed_paste_stop(term);
  3215. if (term->ldisc)
  3216. ldisc_send(term->ldisc, buf, len, interactive);
  3217. term_seen_key_event(term);
  3218. }
  3219. unsigned long term_translate(
  3220. Terminal *term, struct term_utf8_decode *utf8, unsigned char c)
  3221. {
  3222. if (in_utf(term)) {
  3223. switch (utf8->state) {
  3224. case 0:
  3225. if (c < 0x80) {
  3226. /* UTF-8 must be stateless so we ignore iso2022. */
  3227. if (term->ucsdata->unitab_ctrl[c] != 0xFF) {
  3228. return term->ucsdata->unitab_ctrl[c];
  3229. } else if ((term->utf8linedraw) &&
  3230. (term->cset_attr[term->cset] == CSET_LINEDRW)) {
  3231. /* Linedraw characters are explicitly enabled */
  3232. return c | CSET_LINEDRW;
  3233. } else {
  3234. return c | CSET_ASCII;
  3235. }
  3236. } else if ((c & 0xe0) == 0xc0) {
  3237. utf8->size = utf8->state = 1;
  3238. utf8->chr = (c & 0x1f);
  3239. } else if ((c & 0xf0) == 0xe0) {
  3240. utf8->size = utf8->state = 2;
  3241. utf8->chr = (c & 0x0f);
  3242. } else if ((c & 0xf8) == 0xf0) {
  3243. utf8->size = utf8->state = 3;
  3244. utf8->chr = (c & 0x07);
  3245. } else if ((c & 0xfc) == 0xf8) {
  3246. utf8->size = utf8->state = 4;
  3247. utf8->chr = (c & 0x03);
  3248. } else if ((c & 0xfe) == 0xfc) {
  3249. utf8->size = utf8->state = 5;
  3250. utf8->chr = (c & 0x01);
  3251. } else {
  3252. return UCSINVALID;
  3253. }
  3254. return UCSINCOMPLETE;
  3255. case 1:
  3256. case 2:
  3257. case 3:
  3258. case 4:
  3259. case 5:
  3260. if ((c & 0xC0) != 0x80) {
  3261. utf8->state = 0;
  3262. return UCSTRUNCATED; /* caller will then give us the
  3263. * same byte again */
  3264. }
  3265. utf8->chr = (utf8->chr << 6) | (c & 0x3f);
  3266. if (--utf8->state)
  3267. return UCSINCOMPLETE;
  3268. unsigned long t = utf8->chr;
  3269. /* Is somebody trying to be evil! */
  3270. if (t < 0x80 ||
  3271. (t < 0x800 && utf8->size >= 2) ||
  3272. (t < 0x10000 && utf8->size >= 3) ||
  3273. (t < 0x200000 && utf8->size >= 4) ||
  3274. (t < 0x4000000 && utf8->size >= 5))
  3275. return UCSINVALID;
  3276. /* Unicode line separator and paragraph separator are CR-LF */
  3277. if (t == 0x2028 || t == 0x2029)
  3278. return 0x85;
  3279. /* High controls are probably a Baaad idea too. */
  3280. if (t < 0xA0)
  3281. return 0xFFFD;
  3282. /* The UTF-16 surrogates are not nice either. */
  3283. /* The standard give the option of decoding these:
  3284. * I don't want to! */
  3285. if (t >= 0xD800 && t < 0xE000)
  3286. return UCSINVALID;
  3287. /* ISO 10646 characters now limited to UTF-16 range. */
  3288. if (t > 0x10FFFF)
  3289. return UCSINVALID;
  3290. /* U+FEFF is best seen as a null. */
  3291. if (t == 0xFEFF)
  3292. return UCSINCOMPLETE;
  3293. /* But U+FFFE is an error. */
  3294. if (t == 0xFFFE || t == 0xFFFF)
  3295. return UCSINVALID;
  3296. return t;
  3297. }
  3298. } else if (term->sco_acs &&
  3299. (c!='\033' && c!='\012' && c!='\015' && c!='\b')) {
  3300. /* Are we in the nasty ACS mode? Note: no sco in utf mode. */
  3301. if (term->sco_acs == 2)
  3302. c |= 0x80;
  3303. return c | CSET_SCOACS;
  3304. } else {
  3305. switch (term->cset_attr[term->cset]) {
  3306. /*
  3307. * Linedraw characters are different from 'ESC ( B'
  3308. * only for a small range. For ones outside that
  3309. * range, make sure we use the same font as well as
  3310. * the same encoding.
  3311. */
  3312. case CSET_LINEDRW:
  3313. if (term->ucsdata->unitab_ctrl[c] != 0xFF)
  3314. return term->ucsdata->unitab_ctrl[c];
  3315. else
  3316. return c | CSET_LINEDRW;
  3317. break;
  3318. case CSET_GBCHR:
  3319. /* If UK-ASCII, make the '#' a LineDraw Pound */
  3320. if (c == '#')
  3321. return '}' | CSET_LINEDRW;
  3322. /* fall through */
  3323. case CSET_ASCII:
  3324. if (term->ucsdata->unitab_ctrl[c] != 0xFF)
  3325. return term->ucsdata->unitab_ctrl[c];
  3326. else
  3327. return c | CSET_ASCII;
  3328. break;
  3329. case CSET_SCOACS:
  3330. if (c >= ' ')
  3331. return c | CSET_SCOACS;
  3332. break;
  3333. }
  3334. }
  3335. return c;
  3336. }
  3337. /*
  3338. * Remove everything currently in `inbuf' and stick it up on the
  3339. * in-memory display. There's a big state machine in here to
  3340. * process escape sequences...
  3341. */
  3342. static void term_out(Terminal *term, bool called_from_term_data)
  3343. {
  3344. unsigned long c;
  3345. int unget;
  3346. const unsigned char *chars;
  3347. size_t nchars_got = 0, nchars_used = 0;
  3348. /*
  3349. * During drag-selects, we do not process terminal input, because
  3350. * the user will want the screen to hold still to be selected.
  3351. */
  3352. if (term->selstate == DRAGGING)
  3353. return;
  3354. unget = -1;
  3355. chars = NULL; /* placate compiler warnings */
  3356. while (nchars_got < nchars_used ||
  3357. unget != -1 ||
  3358. bufchain_size(&term->inbuf) > 0) {
  3359. if (unget != -1) {
  3360. /*
  3361. * Handle a character we left in 'unget' the last time
  3362. * round this loop. This happens if a UTF-8 sequence is
  3363. * aborted early, by containing fewer continuation bytes
  3364. * than its introducer expected: the non-continuation byte
  3365. * that interrupted the sequence must now be processed
  3366. * as a fresh piece of input in its own right.
  3367. */
  3368. c = unget;
  3369. unget = -1;
  3370. } else {
  3371. /*
  3372. * If we're waiting for a terminal resize triggered by an
  3373. * escape sequence, we defer processing the terminal
  3374. * output until we receive acknowledgment from the front
  3375. * end that the resize has happened, so that further
  3376. * output will be processed in the context of the new
  3377. * size.
  3378. *
  3379. * This test goes inside the main while-loop, so that we
  3380. * exit early if we encounter a resize escape sequence
  3381. * part way through term->inbuf.
  3382. *
  3383. * It's also in the branch of this if statement that
  3384. * doesn't deal with a character left in 'unget' by the
  3385. * previous loop iteration, because if we break out of
  3386. * this loop with an ungot character still pending, we'll
  3387. * lose it. (And in any case, if the previous thing that
  3388. * happened was a truncated UTF-8 sequence, then it won't
  3389. * have scheduled a pending resize.)
  3390. */
  3391. if (term->win_resize_pending != WIN_RESIZE_NO)
  3392. break;
  3393. if (nchars_got == nchars_used) {
  3394. /* Delete the previous chunk from the bufchain */
  3395. bufchain_consume(&term->inbuf, nchars_used);
  3396. nchars_used = 0;
  3397. if (bufchain_size(&term->inbuf) == 0)
  3398. break; /* no more data */
  3399. ptrlen data = bufchain_prefix(&term->inbuf);
  3400. chars = data.ptr;
  3401. nchars_got = data.len;
  3402. assert(chars != NULL);
  3403. assert(nchars_used < nchars_got);
  3404. }
  3405. c = chars[nchars_used++];
  3406. /*
  3407. * Optionally log the session traffic to a file. Useful for
  3408. * debugging and possibly also useful for actual logging.
  3409. */
  3410. if (term->logtype == LGTYP_DEBUG && term->logctx)
  3411. logtraffic(term->logctx, (unsigned char) c, LGTYP_DEBUG);
  3412. }
  3413. /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
  3414. * be able to display 8-bit characters, but I'll let that go 'cause
  3415. * of i18n.
  3416. */
  3417. /*
  3418. * If we're printing, add the character to the printer
  3419. * buffer.
  3420. */
  3421. if (term->printing) {
  3422. bufchain_add(&term->printer_buf, &c, 1);
  3423. /*
  3424. * If we're in print-only mode, we use a much simpler
  3425. * state machine designed only to recognise the ESC[4i
  3426. * termination sequence.
  3427. */
  3428. if (term->only_printing) {
  3429. if (c == '\033')
  3430. term->print_state = 1;
  3431. else if (c == (unsigned char)'\233')
  3432. term->print_state = 2;
  3433. else if (c == '[' && term->print_state == 1)
  3434. term->print_state = 2;
  3435. else if (c == '4' && term->print_state == 2)
  3436. term->print_state = 3;
  3437. else if (c == 'i' && term->print_state == 3)
  3438. term->print_state = 4;
  3439. else
  3440. term->print_state = 0;
  3441. if (term->print_state == 4) {
  3442. term_print_finish(term);
  3443. }
  3444. continue;
  3445. }
  3446. }
  3447. /* Do character-set translation. */
  3448. if (term->termstate == TOPLEVEL) {
  3449. unsigned long t = term_translate(term, &term->utf8, c);
  3450. switch (t) {
  3451. case UCSINCOMPLETE:
  3452. continue; /* didn't complete a multibyte char */
  3453. case UCSTRUNCATED:
  3454. unget = c;
  3455. /* fall through */
  3456. case UCSINVALID:
  3457. c = UCSERR;
  3458. break;
  3459. default:
  3460. c = t;
  3461. break;
  3462. }
  3463. }
  3464. /*
  3465. * How about C1 controls?
  3466. * Explicitly ignore SCI (0x9a), which we don't translate to DECID.
  3467. */
  3468. if ((c & -32) == 0x80 && term->termstate < DO_CTRLS &&
  3469. !term->vt52_mode && has_compat(VT220)) {
  3470. if (c == 0x9a)
  3471. c = 0;
  3472. else {
  3473. term->termstate = SEEN_ESC;
  3474. term->esc_query = 0;
  3475. c = '@' + (c & 0x1F);
  3476. }
  3477. }
  3478. /* Or the GL control. */
  3479. if (c == '\177' && term->termstate < DO_CTRLS && has_compat(OTHER)) {
  3480. if (term->curs.x && !term->wrapnext)
  3481. term->curs.x--;
  3482. term->wrapnext = false;
  3483. /* destructive backspace might be disabled */
  3484. if (!term->no_dbackspace) {
  3485. check_boundary(term, term->curs.x, term->curs.y);
  3486. check_boundary(term, term->curs.x+1, term->curs.y);
  3487. copy_termchar(scrlineptr(term->curs.y),
  3488. term->curs.x, &term->erase_char);
  3489. }
  3490. seen_disp_event(term);
  3491. } else
  3492. /* Or normal C0 controls. */
  3493. if ((c & ~0x1F) == 0 && term->termstate < DO_CTRLS) {
  3494. switch (c) {
  3495. case '\005': /* ENQ: terminal type query */
  3496. /*
  3497. * Strictly speaking this is VT100 but a VT100 defaults to
  3498. * no response. Other terminals respond at their option.
  3499. *
  3500. * Don't put a CR in the default string as this tends to
  3501. * upset some weird software.
  3502. */
  3503. compatibility(ANSIMIN);
  3504. if (term->ldisc) {
  3505. strbuf *buf = term_input_data_from_charset(
  3506. term, DEFAULT_CODEPAGE,
  3507. term->answerback->s, term->answerback->len);
  3508. ldisc_send(term->ldisc, buf->s, buf->len, false);
  3509. strbuf_free(buf);
  3510. }
  3511. break;
  3512. case '\007': { /* BEL: Bell */
  3513. if (term->termstate == SEEN_OSC ||
  3514. term->termstate == SEEN_OSC_W) {
  3515. /*
  3516. * In an OSC context, BEL is one of the ways to terminate
  3517. * the whole sequence. We process it as such even if we
  3518. * haven't got into the final OSC_STRING state yet, so that
  3519. * OSC sequences without a string will be handled cleanly.
  3520. */
  3521. do_osc(term);
  3522. term->termstate = TOPLEVEL;
  3523. break;
  3524. }
  3525. struct beeptime *newbeep;
  3526. unsigned long ticks;
  3527. ticks = GETTICKCOUNT();
  3528. if (!term->beep_overloaded) {
  3529. newbeep = snew(struct beeptime);
  3530. newbeep->ticks = ticks;
  3531. newbeep->next = NULL;
  3532. if (!term->beephead)
  3533. term->beephead = newbeep;
  3534. else
  3535. term->beeptail->next = newbeep;
  3536. term->beeptail = newbeep;
  3537. term->nbeeps++;
  3538. }
  3539. /*
  3540. * Throw out any beeps that happened more than
  3541. * t seconds ago.
  3542. */
  3543. while (term->beephead &&
  3544. term->beephead->ticks < ticks - term->bellovl_t) {
  3545. struct beeptime *tmp = term->beephead;
  3546. term->beephead = tmp->next;
  3547. sfree(tmp);
  3548. if (!term->beephead)
  3549. term->beeptail = NULL;
  3550. term->nbeeps--;
  3551. }
  3552. if (term->bellovl && term->beep_overloaded &&
  3553. ticks - term->lastbeep >= (unsigned)term->bellovl_s) {
  3554. /*
  3555. * If we're currently overloaded and the
  3556. * last beep was more than s seconds ago,
  3557. * leave overload mode.
  3558. */
  3559. term->beep_overloaded = false;
  3560. } else if (term->bellovl && !term->beep_overloaded &&
  3561. term->nbeeps >= term->bellovl_n) {
  3562. /*
  3563. * Now, if we have n or more beeps
  3564. * remaining in the queue, go into overload
  3565. * mode.
  3566. */
  3567. term->beep_overloaded = true;
  3568. }
  3569. term->lastbeep = ticks;
  3570. /*
  3571. * Perform an actual beep if we're not overloaded.
  3572. */
  3573. if (!term->bellovl || !term->beep_overloaded) {
  3574. win_bell(term->win, term->beep);
  3575. if (term->beep == BELL_VISUAL) {
  3576. term_schedule_vbell(term, false, 0);
  3577. }
  3578. }
  3579. seen_disp_event(term);
  3580. break;
  3581. }
  3582. case '\b': /* BS: Back space */
  3583. if (term->wrapnext) {
  3584. term->wrapnext = false;
  3585. } else if (term->curs.x == 0 &&
  3586. (term->curs.y == 0 || !term->wrap)) {
  3587. /* do nothing */
  3588. } else if (term->curs.x == 0 && term->curs.y > 0) {
  3589. term->curs.x = term->cols - 1, term->curs.y--;
  3590. /*
  3591. * If the line we've just wrapped back on to had the
  3592. * LATTR_WRAPPED2 flag set, it means that the line wrapped
  3593. * because a double-width character was printed with the
  3594. * cursor in the rightmost column, and the best handling
  3595. * available was to leave that column empty and move the
  3596. * whole character to the next line. In that situation,
  3597. * backspacing needs to put the cursor on the previous
  3598. * _logical_ character, i.e. skip the empty space left by
  3599. * the wrapping. This arranges that if an application
  3600. * unaware of the terminal width or cursor position prints
  3601. * a number of printing characters and then tries to return
  3602. * to a particular one of them by emitting the right number
  3603. * of backspaces, it's still the right number even if a
  3604. * line break appeared in a maximally awkward position.
  3605. */
  3606. termline *ldata = scrlineptr(term->curs.y);
  3607. if (term->curs.x > 0 && (ldata->lattr & LATTR_WRAPPED2))
  3608. term->curs.x--;
  3609. } else {
  3610. term->curs.x--;
  3611. }
  3612. seen_disp_event(term);
  3613. break;
  3614. case '\016': /* LS1: Locking-shift one */
  3615. compatibility(VT100);
  3616. term->cset = 1;
  3617. break;
  3618. case '\017': /* LS0: Locking-shift zero */
  3619. compatibility(VT100);
  3620. term->cset = 0;
  3621. break;
  3622. case '\033': /* ESC: Escape */
  3623. if (term->vt52_mode)
  3624. term->termstate = VT52_ESC;
  3625. else if (term->termstate == SEEN_OSC ||
  3626. term->termstate == SEEN_OSC_W) {
  3627. /* Be prepared to terminate an OSC early */
  3628. term->termstate = OSC_MAYBE_ST;
  3629. } else {
  3630. compatibility(ANSIMIN);
  3631. term->termstate = SEEN_ESC;
  3632. term->esc_query = 0;
  3633. }
  3634. break;
  3635. case '\015': /* CR: Carriage return */
  3636. term->curs.x = 0;
  3637. term->wrapnext = false;
  3638. seen_disp_event(term);
  3639. if (term->crhaslf) {
  3640. if (term->curs.y == term->marg_b)
  3641. scroll(term, term->marg_t, term->marg_b, 1, true);
  3642. else if (term->curs.y < term->rows - 1)
  3643. term->curs.y++;
  3644. }
  3645. if (term->logctx)
  3646. logtraffic(term->logctx, (unsigned char) c, LGTYP_ASCII);
  3647. break;
  3648. case '\014': /* FF: Form feed */
  3649. if (has_compat(SCOANSI)) {
  3650. move(term, 0, 0, 0);
  3651. erase_lots(term, false, false, true);
  3652. if (term->scroll_on_disp)
  3653. term->disptop = 0;
  3654. term->wrapnext = false;
  3655. seen_disp_event(term);
  3656. break;
  3657. }
  3658. case '\013': /* VT: Line tabulation */
  3659. compatibility(VT100);
  3660. case '\012': /* LF: Line feed */
  3661. if (term->curs.y == term->marg_b)
  3662. scroll(term, term->marg_t, term->marg_b, 1, true);
  3663. else if (term->curs.y < term->rows - 1)
  3664. term->curs.y++;
  3665. if (term->lfhascr)
  3666. term->curs.x = 0;
  3667. term->wrapnext = false;
  3668. seen_disp_event(term);
  3669. if (term->logctx)
  3670. logtraffic(term->logctx, (unsigned char) c, LGTYP_ASCII);
  3671. break;
  3672. case '\t': { /* HT: Character tabulation */
  3673. pos old_curs = term->curs;
  3674. termline *ldata = scrlineptr(term->curs.y);
  3675. do {
  3676. term->curs.x++;
  3677. } while (term->curs.x < term->cols - 1 &&
  3678. !term->tabs[term->curs.x]);
  3679. if ((ldata->lattr & LATTR_MODE) != LATTR_NORM) {
  3680. if (term->curs.x >= term->cols / 2)
  3681. term->curs.x = term->cols / 2 - 1;
  3682. } else {
  3683. if (term->curs.x >= term->cols)
  3684. term->curs.x = term->cols - 1;
  3685. }
  3686. check_selection(term, old_curs, term->curs);
  3687. seen_disp_event(term);
  3688. break;
  3689. }
  3690. }
  3691. } else
  3692. switch (term->termstate) {
  3693. case TOPLEVEL:
  3694. /* Only graphic characters get this far;
  3695. * ctrls are stripped above */
  3696. term_display_graphic_char(term, c);
  3697. term->last_graphic_char = c;
  3698. break;
  3699. case OSC_MAYBE_ST:
  3700. /*
  3701. * This state is virtually identical to SEEN_ESC, with the
  3702. * exception that we have an OSC sequence in the pipeline,
  3703. * and _if_ we see a backslash, we process it.
  3704. */
  3705. if (c == '\\') {
  3706. do_osc(term);
  3707. term->termstate = TOPLEVEL;
  3708. break;
  3709. }
  3710. /* else fall through */
  3711. case SEEN_ESC:
  3712. if (c >= ' ' && c <= '/') {
  3713. if (term->esc_query)
  3714. term->esc_query = -1;
  3715. else
  3716. term->esc_query = c;
  3717. break;
  3718. }
  3719. term->termstate = TOPLEVEL;
  3720. switch (ANSI(c, term->esc_query)) {
  3721. case '[': /* enter CSI mode */
  3722. term->termstate = SEEN_CSI;
  3723. term->esc_nargs = 1;
  3724. term->esc_args[0] = ARG_DEFAULT;
  3725. term->esc_query = 0;
  3726. break;
  3727. case ']': /* OSC: xterm escape sequences */
  3728. /* Compatibility is nasty here, xterm, linux, decterm yuk! */
  3729. compatibility(OTHER);
  3730. term->termstate = SEEN_OSC;
  3731. term->osc_is_apc = false;
  3732. term->osc_strlen = 0;
  3733. term->esc_args[0] = 0;
  3734. term->esc_nargs = 1;
  3735. break;
  3736. case '_': /* APC: application program command */
  3737. /* APC sequences are just a string, terminated by
  3738. * ST or (I've observed in practice) ^G. That is,
  3739. * they have the same termination convention as
  3740. * OSC. So we handle them by going straight into
  3741. * OSC_STRING state and setting a flag indicating
  3742. * that it's not really an OSC. */
  3743. compatibility(OTHER);
  3744. term->termstate = SEEN_OSC;
  3745. term->osc_is_apc = true;
  3746. term->osc_strlen = 0;
  3747. term->esc_args[0] = 0;
  3748. term->esc_nargs = 1;
  3749. break;
  3750. case '7': /* DECSC: save cursor */
  3751. compatibility(VT100);
  3752. save_cursor(term, true);
  3753. break;
  3754. case '8': /* DECRC: restore cursor */
  3755. compatibility(VT100);
  3756. save_cursor(term, false);
  3757. break;
  3758. case '=': /* DECKPAM: Keypad application mode */
  3759. compatibility(VT100);
  3760. term->app_keypad_keys = true;
  3761. break;
  3762. case '>': /* DECKPNM: Keypad numeric mode */
  3763. compatibility(VT100);
  3764. term->app_keypad_keys = false;
  3765. break;
  3766. case 'D': /* IND: exactly equivalent to LF */
  3767. compatibility(VT100);
  3768. if (term->curs.y == term->marg_b)
  3769. scroll(term, term->marg_t, term->marg_b, 1, true);
  3770. else if (term->curs.y < term->rows - 1)
  3771. term->curs.y++;
  3772. term->wrapnext = false;
  3773. seen_disp_event(term);
  3774. break;
  3775. case 'E': /* NEL: exactly equivalent to CR-LF */
  3776. compatibility(VT100);
  3777. term->curs.x = 0;
  3778. if (term->curs.y == term->marg_b)
  3779. scroll(term, term->marg_t, term->marg_b, 1, true);
  3780. else if (term->curs.y < term->rows - 1)
  3781. term->curs.y++;
  3782. term->wrapnext = false;
  3783. seen_disp_event(term);
  3784. break;
  3785. case 'M': /* RI: reverse index - backwards LF */
  3786. compatibility(VT100);
  3787. if (term->curs.y == term->marg_t)
  3788. scroll(term, term->marg_t, term->marg_b, -1, true);
  3789. else if (term->curs.y > 0)
  3790. term->curs.y--;
  3791. term->wrapnext = false;
  3792. seen_disp_event(term);
  3793. break;
  3794. case 'Z': /* DECID: terminal type query */
  3795. compatibility(VT100);
  3796. if (term->ldisc)
  3797. ldisc_send(term->ldisc, term->id_string,
  3798. strlen(term->id_string), false);
  3799. break;
  3800. case 'c': /* RIS: restore power-on settings */
  3801. compatibility(VT100);
  3802. power_on(term, true);
  3803. if (term->ldisc) /* cause ldisc to notice changes */
  3804. ldisc_echoedit_update(term->ldisc);
  3805. if (term->reset_132) {
  3806. if (!term->no_remote_resize)
  3807. term_request_resize(term, 80, term->rows);
  3808. term->reset_132 = false;
  3809. }
  3810. if (term->scroll_on_disp)
  3811. term->disptop = 0;
  3812. seen_disp_event(term);
  3813. break;
  3814. case 'H': /* HTS: set a tab */
  3815. compatibility(VT100);
  3816. term->tabs[term->curs.x] = true;
  3817. break;
  3818. case ANSI('8', '#'): { /* DECALN: fills screen with Es :-) */
  3819. compatibility(VT100);
  3820. termline *ldata;
  3821. int i, j;
  3822. pos scrtop, scrbot;
  3823. for (i = 0; i < term->rows; i++) {
  3824. ldata = scrlineptr(i);
  3825. check_line_size(term, ldata);
  3826. for (j = 0; j < term->cols; j++) {
  3827. copy_termchar(ldata, j,
  3828. &term->basic_erase_char);
  3829. ldata->chars[j].chr = 'E';
  3830. }
  3831. ldata->lattr = LATTR_NORM;
  3832. }
  3833. if (term->scroll_on_disp)
  3834. term->disptop = 0;
  3835. seen_disp_event(term);
  3836. scrtop.x = scrtop.y = 0;
  3837. scrbot.x = 0;
  3838. scrbot.y = term->rows;
  3839. check_selection(term, scrtop, scrbot);
  3840. break;
  3841. }
  3842. case ANSI('3', '#'):
  3843. case ANSI('4', '#'):
  3844. case ANSI('5', '#'):
  3845. case ANSI('6', '#'): {
  3846. compatibility(VT100);
  3847. int nlattr;
  3848. termline *ldata;
  3849. switch (ANSI(c, term->esc_query)) {
  3850. case ANSI('3', '#'): /* DECDHL: 2*height, top */
  3851. nlattr = LATTR_TOP;
  3852. break;
  3853. case ANSI('4', '#'): /* DECDHL: 2*height, bottom */
  3854. nlattr = LATTR_BOT;
  3855. break;
  3856. case ANSI('5', '#'): /* DECSWL: normal */
  3857. nlattr = LATTR_NORM;
  3858. break;
  3859. default: /* case ANSI('6', '#'): DECDWL: 2*width */
  3860. nlattr = LATTR_WIDE;
  3861. break;
  3862. }
  3863. ldata = scrlineptr(term->curs.y);
  3864. check_line_size(term, ldata);
  3865. check_trust_status(term, ldata);
  3866. ldata->lattr = nlattr;
  3867. seen_disp_event(term);
  3868. break;
  3869. }
  3870. /* GZD4: G0 designate 94-set */
  3871. case ANSI('A', '('):
  3872. compatibility(VT100);
  3873. if (!term->no_remote_charset)
  3874. term->cset_attr[0] = CSET_GBCHR;
  3875. break;
  3876. case ANSI('B', '('):
  3877. compatibility(VT100);
  3878. if (!term->no_remote_charset)
  3879. term->cset_attr[0] = CSET_ASCII;
  3880. break;
  3881. case ANSI('0', '('):
  3882. compatibility(VT100);
  3883. if (!term->no_remote_charset)
  3884. term->cset_attr[0] = CSET_LINEDRW;
  3885. break;
  3886. case ANSI('U', '('):
  3887. compatibility(OTHER);
  3888. if (!term->no_remote_charset)
  3889. term->cset_attr[0] = CSET_SCOACS;
  3890. break;
  3891. /* G1D4: G1-designate 94-set */
  3892. case ANSI('A', ')'):
  3893. compatibility(VT100);
  3894. if (!term->no_remote_charset)
  3895. term->cset_attr[1] = CSET_GBCHR;
  3896. break;
  3897. case ANSI('B', ')'):
  3898. compatibility(VT100);
  3899. if (!term->no_remote_charset)
  3900. term->cset_attr[1] = CSET_ASCII;
  3901. break;
  3902. case ANSI('0', ')'):
  3903. compatibility(VT100);
  3904. if (!term->no_remote_charset)
  3905. term->cset_attr[1] = CSET_LINEDRW;
  3906. break;
  3907. case ANSI('U', ')'):
  3908. compatibility(OTHER);
  3909. if (!term->no_remote_charset)
  3910. term->cset_attr[1] = CSET_SCOACS;
  3911. break;
  3912. /* DOCS: Designate other coding system */
  3913. case ANSI('8', '%'): /* Old Linux code */
  3914. case ANSI('G', '%'):
  3915. compatibility(OTHER);
  3916. if (!term->no_remote_charset)
  3917. term->utf = true;
  3918. break;
  3919. case ANSI('@', '%'):
  3920. compatibility(OTHER);
  3921. if (!term->no_remote_charset)
  3922. term->utf = false;
  3923. break;
  3924. }
  3925. break;
  3926. case SEEN_CSI:
  3927. term->termstate = TOPLEVEL; /* default */
  3928. if (isdigit(c)) {
  3929. if (term->esc_nargs <= ARGS_MAX) {
  3930. if (term->esc_args[term->esc_nargs - 1] == ARG_DEFAULT)
  3931. term->esc_args[term->esc_nargs - 1] = 0;
  3932. if (term->esc_args[term->esc_nargs - 1] <=
  3933. UINT_MAX / 10 &&
  3934. term->esc_args[term->esc_nargs - 1] * 10 <=
  3935. UINT_MAX - c - '0')
  3936. term->esc_args[term->esc_nargs - 1] =
  3937. 10 * term->esc_args[term->esc_nargs - 1] +
  3938. c - '0';
  3939. else
  3940. term->esc_args[term->esc_nargs - 1] = UINT_MAX;
  3941. }
  3942. term->termstate = SEEN_CSI;
  3943. } else if (c == ';') {
  3944. if (term->esc_nargs < ARGS_MAX)
  3945. term->esc_args[term->esc_nargs++] = ARG_DEFAULT;
  3946. term->termstate = SEEN_CSI;
  3947. } else if (c < '@') {
  3948. if (term->esc_query)
  3949. term->esc_query = -1;
  3950. else if (c == '?')
  3951. term->esc_query = 1;
  3952. else
  3953. term->esc_query = c;
  3954. term->termstate = SEEN_CSI;
  3955. } else
  3956. #define CLAMP(arg, lim) ((arg) = ((arg) > (lim)) ? (lim) : (arg))
  3957. switch (ANSI(c, term->esc_query)) {
  3958. case 'A': /* CUU: move up N lines */
  3959. CLAMP(term->esc_args[0], term->rows);
  3960. move(term, term->curs.x,
  3961. term->curs.y - def(term->esc_args[0], 1), 1);
  3962. seen_disp_event(term);
  3963. break;
  3964. case 'e': /* VPR: move down N lines */
  3965. compatibility(ANSI);
  3966. /* FALLTHROUGH */
  3967. case 'B': /* CUD: Cursor down */
  3968. CLAMP(term->esc_args[0], term->rows);
  3969. move(term, term->curs.x,
  3970. term->curs.y + def(term->esc_args[0], 1), 1);
  3971. seen_disp_event(term);
  3972. break;
  3973. case 'b': /* REP: repeat previous grap */
  3974. CLAMP(term->esc_args[0], term->rows * term->cols);
  3975. if (term->last_graphic_char) {
  3976. unsigned i;
  3977. for (i = 0; i < term->esc_args[0]; i++)
  3978. term_display_graphic_char(
  3979. term, term->last_graphic_char);
  3980. }
  3981. break;
  3982. case ANSI('c', '>'): /* DA: report xterm version */
  3983. compatibility(OTHER);
  3984. /* this reports xterm version 136 so that VIM can
  3985. use the drag messages from the mouse reporting */
  3986. if (term->ldisc)
  3987. ldisc_send(term->ldisc, "\033[>0;136;0c", 11,
  3988. false);
  3989. break;
  3990. case 'a': /* HPR: move right N cols */
  3991. compatibility(ANSI);
  3992. /* FALLTHROUGH */
  3993. case 'C': /* CUF: Cursor right */
  3994. CLAMP(term->esc_args[0], term->cols);
  3995. move(term, term->curs.x + def(term->esc_args[0], 1),
  3996. term->curs.y, 1);
  3997. seen_disp_event(term);
  3998. break;
  3999. case 'D': /* CUB: move left N cols */
  4000. CLAMP(term->esc_args[0], term->cols);
  4001. move(term, term->curs.x - def(term->esc_args[0], 1),
  4002. term->curs.y, 1);
  4003. seen_disp_event(term);
  4004. break;
  4005. case 'E': /* CNL: move down N lines and CR */
  4006. compatibility(ANSI);
  4007. CLAMP(term->esc_args[0], term->rows);
  4008. move(term, 0,
  4009. term->curs.y + def(term->esc_args[0], 1), 1);
  4010. seen_disp_event(term);
  4011. break;
  4012. case 'F': /* CPL: move up N lines and CR */
  4013. compatibility(ANSI);
  4014. CLAMP(term->esc_args[0], term->rows);
  4015. move(term, 0,
  4016. term->curs.y - def(term->esc_args[0], 1), 1);
  4017. seen_disp_event(term);
  4018. break;
  4019. case 'G': /* CHA */
  4020. case '`': /* HPA: set horizontal posn */
  4021. compatibility(ANSI);
  4022. CLAMP(term->esc_args[0], term->cols);
  4023. move(term, def(term->esc_args[0], 1) - 1,
  4024. term->curs.y, 0);
  4025. seen_disp_event(term);
  4026. break;
  4027. case 'd': /* VPA: set vertical posn */
  4028. compatibility(ANSI);
  4029. CLAMP(term->esc_args[0], term->rows);
  4030. move(term, term->curs.x,
  4031. ((term->dec_om ? term->marg_t : 0) +
  4032. def(term->esc_args[0], 1) - 1),
  4033. (term->dec_om ? 2 : 0));
  4034. seen_disp_event(term);
  4035. break;
  4036. case 'H': /* CUP */
  4037. case 'f': /* HVP: set horz and vert posns at once */
  4038. if (term->esc_nargs < 2)
  4039. term->esc_args[1] = ARG_DEFAULT;
  4040. CLAMP(term->esc_args[0], term->rows);
  4041. CLAMP(term->esc_args[1], term->cols);
  4042. move(term, def(term->esc_args[1], 1) - 1,
  4043. ((term->dec_om ? term->marg_t : 0) +
  4044. def(term->esc_args[0], 1) - 1),
  4045. (term->dec_om ? 2 : 0));
  4046. seen_disp_event(term);
  4047. break;
  4048. case 'J': { /* ED: erase screen or parts of it */
  4049. unsigned int i = def(term->esc_args[0], 0);
  4050. if (i == 3) {
  4051. /* Erase Saved Lines (xterm)
  4052. * This follows Thomas Dickey's xterm. */
  4053. if (!term->no_remote_clearscroll)
  4054. term_clrsb(term);
  4055. } else {
  4056. i++;
  4057. if (i > 3)
  4058. i = 0;
  4059. erase_lots(term, false, !!(i & 2), !!(i & 1));
  4060. }
  4061. if (term->scroll_on_disp)
  4062. term->disptop = 0;
  4063. seen_disp_event(term);
  4064. break;
  4065. }
  4066. case 'K': { /* EL: erase line or parts of it */
  4067. unsigned int i = def(term->esc_args[0], 0) + 1;
  4068. if (i > 3)
  4069. i = 0;
  4070. erase_lots(term, true, !!(i & 2), !!(i & 1));
  4071. seen_disp_event(term);
  4072. break;
  4073. }
  4074. case 'L': /* IL: insert lines */
  4075. compatibility(VT102);
  4076. CLAMP(term->esc_args[0], term->rows);
  4077. if (term->curs.y <= term->marg_b)
  4078. scroll(term, term->curs.y, term->marg_b,
  4079. -def(term->esc_args[0], 1), false);
  4080. seen_disp_event(term);
  4081. break;
  4082. case 'M': /* DL: delete lines */
  4083. compatibility(VT102);
  4084. CLAMP(term->esc_args[0], term->rows);
  4085. if (term->curs.y <= term->marg_b)
  4086. scroll(term, term->curs.y, term->marg_b,
  4087. def(term->esc_args[0], 1),
  4088. true);
  4089. seen_disp_event(term);
  4090. break;
  4091. case '@': /* ICH: insert chars */
  4092. /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
  4093. compatibility(VT102);
  4094. CLAMP(term->esc_args[0], term->cols);
  4095. insch(term, def(term->esc_args[0], 1));
  4096. seen_disp_event(term);
  4097. break;
  4098. case 'P': /* DCH: delete chars */
  4099. compatibility(VT102);
  4100. CLAMP(term->esc_args[0], term->cols);
  4101. insch(term, -def(term->esc_args[0], 1));
  4102. seen_disp_event(term);
  4103. break;
  4104. case 'c': /* DA: terminal type query */
  4105. compatibility(VT100);
  4106. /* This is the response for a VT102 */
  4107. if (term->ldisc)
  4108. ldisc_send(term->ldisc, term->id_string,
  4109. strlen(term->id_string), false);
  4110. break;
  4111. case 'n': /* DSR: cursor position query */
  4112. if (term->ldisc) {
  4113. if (term->esc_args[0] == 6) {
  4114. char buf[32];
  4115. sprintf(buf, "\033[%d;%dR", term->curs.y + 1,
  4116. term->curs.x + 1);
  4117. ldisc_send(term->ldisc, buf, strlen(buf),
  4118. false);
  4119. } else if (term->esc_args[0] == 5) {
  4120. ldisc_send(term->ldisc, "\033[0n", 4, false);
  4121. }
  4122. }
  4123. break;
  4124. case 'h': /* SM: toggle modes to high */
  4125. case ANSI_QUE('h'):
  4126. compatibility(VT100);
  4127. for (int i = 0; i < term->esc_nargs; i++)
  4128. toggle_mode(term, term->esc_args[i],
  4129. term->esc_query, true);
  4130. break;
  4131. case 'i': /* MC: Media copy */
  4132. case ANSI_QUE('i'): {
  4133. compatibility(VT100);
  4134. char *printer;
  4135. if (term->esc_nargs != 1) break;
  4136. if (term->esc_args[0] == 5 &&
  4137. (printer = conf_get_str(term->conf,
  4138. CONF_printer))[0]) {
  4139. term->printing = true;
  4140. term->only_printing = !term->esc_query;
  4141. term->print_state = 0;
  4142. term_print_setup(term, printer);
  4143. } else if (term->esc_args[0] == 4 &&
  4144. term->printing) {
  4145. term_print_finish(term);
  4146. }
  4147. break;
  4148. }
  4149. case 'l': /* RM: toggle modes to low */
  4150. case ANSI_QUE('l'):
  4151. compatibility(VT100);
  4152. for (int i = 0; i < term->esc_nargs; i++)
  4153. toggle_mode(term, term->esc_args[i],
  4154. term->esc_query, false);
  4155. break;
  4156. case 'g': /* TBC: clear tabs */
  4157. compatibility(VT100);
  4158. if (term->esc_nargs == 1) {
  4159. if (term->esc_args[0] == 0) {
  4160. term->tabs[term->curs.x] = false;
  4161. } else if (term->esc_args[0] == 3) {
  4162. int i;
  4163. for (i = 0; i < term->cols; i++)
  4164. term->tabs[i] = false;
  4165. }
  4166. }
  4167. break;
  4168. case 'r': /* DECSTBM: set scroll margins */
  4169. compatibility(VT100);
  4170. if (term->esc_nargs <= 2) {
  4171. int top, bot;
  4172. CLAMP(term->esc_args[0], term->rows);
  4173. CLAMP(term->esc_args[1], term->rows);
  4174. top = def(term->esc_args[0], 1) - 1;
  4175. bot = (term->esc_nargs <= 1
  4176. || term->esc_args[1] == 0 ?
  4177. term->rows :
  4178. def(term->esc_args[1], term->rows)) - 1;
  4179. if (bot >= term->rows)
  4180. bot = term->rows - 1;
  4181. /* VTTEST Bug 9 - if region is less than 2 lines
  4182. * don't change region.
  4183. */
  4184. if (bot - top > 0) {
  4185. term->marg_t = top;
  4186. term->marg_b = bot;
  4187. term->curs.x = 0;
  4188. /*
  4189. * I used to think the cursor should be
  4190. * placed at the top of the newly marginned
  4191. * area. Apparently not: VMS TPU falls over
  4192. * if so.
  4193. *
  4194. * Well actually it should for
  4195. * Origin mode - RDB
  4196. */
  4197. term->curs.y = (term->dec_om ?
  4198. term->marg_t : 0);
  4199. seen_disp_event(term);
  4200. }
  4201. }
  4202. break;
  4203. case 'm': /* SGR: set graphics rendition */
  4204. /*
  4205. * A VT100 without the AVO only had one
  4206. * attribute, either underline or reverse
  4207. * video depending on the cursor type, this
  4208. * was selected by CSI 7m.
  4209. *
  4210. * case 2:
  4211. * This is sometimes DIM, eg on the GIGI and
  4212. * Linux
  4213. * case 8:
  4214. * This is sometimes INVIS various ANSI.
  4215. * case 21:
  4216. * This like 22 disables BOLD, DIM and INVIS
  4217. *
  4218. * The ANSI colours appear on any terminal
  4219. * that has colour (obviously) but the
  4220. * interaction between sgr0 and the colours
  4221. * varies but is usually related to the
  4222. * background colour erase item. The
  4223. * interaction between colour attributes and
  4224. * the mono ones is also very implementation
  4225. * dependent.
  4226. *
  4227. * The 39 and 49 attributes are likely to be
  4228. * unimplemented.
  4229. */
  4230. for (int i = 0; i < term->esc_nargs; i++)
  4231. switch (def(term->esc_args[i], 0)) {
  4232. case 0: /* restore defaults */
  4233. term->curr_attr = term->default_attr;
  4234. term->curr_truecolour =
  4235. term->basic_erase_char.truecolour;
  4236. break;
  4237. case 1: /* enable bold */
  4238. compatibility(VT100AVO);
  4239. term->curr_attr |= ATTR_BOLD;
  4240. break;
  4241. case 2: /* enable dim */
  4242. compatibility(OTHER);
  4243. term->curr_attr |= ATTR_DIM;
  4244. break;
  4245. case 21: /* (enable double underline) */
  4246. compatibility(OTHER);
  4247. case 4: /* enable underline */
  4248. compatibility(VT100AVO);
  4249. term->curr_attr |= ATTR_UNDER;
  4250. break;
  4251. case 5: /* enable blink */
  4252. compatibility(VT100AVO);
  4253. term->curr_attr |= ATTR_BLINK;
  4254. break;
  4255. case 6: /* SCO light bkgrd */
  4256. compatibility(SCOANSI);
  4257. term->blink_is_real = false;
  4258. term->curr_attr |= ATTR_BLINK;
  4259. term_schedule_tblink(term);
  4260. break;
  4261. case 7: /* enable reverse video */
  4262. term->curr_attr |= ATTR_REVERSE;
  4263. break;
  4264. case 9: /* enable strikethrough */
  4265. term->curr_attr |= ATTR_STRIKE;
  4266. break;
  4267. case 10: /* SCO acs off */
  4268. compatibility(SCOANSI);
  4269. if (term->no_remote_charset) break;
  4270. term->sco_acs = 0; break;
  4271. case 11: /* SCO acs on */
  4272. compatibility(SCOANSI);
  4273. if (term->no_remote_charset) break;
  4274. term->sco_acs = 1; break;
  4275. case 12: /* SCO acs on, |0x80 */
  4276. compatibility(SCOANSI);
  4277. if (term->no_remote_charset) break;
  4278. term->sco_acs = 2; break;
  4279. case 22: /* disable bold and dim */
  4280. compatibility2(OTHER, VT220);
  4281. term->curr_attr &= ~(ATTR_BOLD | ATTR_DIM);
  4282. break;
  4283. case 24: /* disable underline */
  4284. compatibility2(OTHER, VT220);
  4285. term->curr_attr &= ~ATTR_UNDER;
  4286. break;
  4287. case 25: /* disable blink */
  4288. compatibility2(OTHER, VT220);
  4289. term->curr_attr &= ~ATTR_BLINK;
  4290. break;
  4291. case 27: /* disable reverse video */
  4292. compatibility2(OTHER, VT220);
  4293. term->curr_attr &= ~ATTR_REVERSE;
  4294. break;
  4295. case 29: /* disable strikethrough */
  4296. term->curr_attr &= ~ATTR_STRIKE;
  4297. break;
  4298. case 30:
  4299. case 31:
  4300. case 32:
  4301. case 33:
  4302. case 34:
  4303. case 35:
  4304. case 36:
  4305. case 37:
  4306. /* foreground */
  4307. term->curr_truecolour.fg.enabled = false;
  4308. term->curr_attr &= ~ATTR_FGMASK;
  4309. term->curr_attr |=
  4310. (term->esc_args[i] - 30)<<ATTR_FGSHIFT;
  4311. break;
  4312. case 90:
  4313. case 91:
  4314. case 92:
  4315. case 93:
  4316. case 94:
  4317. case 95:
  4318. case 96:
  4319. case 97:
  4320. /* aixterm-style bright foreground */
  4321. term->curr_truecolour.fg.enabled = false;
  4322. term->curr_attr &= ~ATTR_FGMASK;
  4323. term->curr_attr |=
  4324. ((term->esc_args[i] - 90 + 8)
  4325. << ATTR_FGSHIFT);
  4326. break;
  4327. case 39: /* default-foreground */
  4328. term->curr_truecolour.fg.enabled = false;
  4329. term->curr_attr &= ~ATTR_FGMASK;
  4330. term->curr_attr |= ATTR_DEFFG;
  4331. break;
  4332. case 40:
  4333. case 41:
  4334. case 42:
  4335. case 43:
  4336. case 44:
  4337. case 45:
  4338. case 46:
  4339. case 47:
  4340. /* background */
  4341. term->curr_truecolour.bg.enabled = false;
  4342. term->curr_attr &= ~ATTR_BGMASK;
  4343. term->curr_attr |=
  4344. (term->esc_args[i] - 40)<<ATTR_BGSHIFT;
  4345. break;
  4346. case 100:
  4347. case 101:
  4348. case 102:
  4349. case 103:
  4350. case 104:
  4351. case 105:
  4352. case 106:
  4353. case 107:
  4354. /* aixterm-style bright background */
  4355. term->curr_truecolour.bg.enabled = false;
  4356. term->curr_attr &= ~ATTR_BGMASK;
  4357. term->curr_attr |=
  4358. ((term->esc_args[i] - 100 + 8)
  4359. << ATTR_BGSHIFT);
  4360. break;
  4361. case 49: /* default-background */
  4362. term->curr_truecolour.bg.enabled = false;
  4363. term->curr_attr &= ~ATTR_BGMASK;
  4364. term->curr_attr |= ATTR_DEFBG;
  4365. break;
  4366. /*
  4367. * 256-colour and true-colour
  4368. * sequences. A 256-colour
  4369. * foreground is selected by a
  4370. * sequence of 3 arguments in the
  4371. * form 38;5;n, where n is in the
  4372. * range 0-255. A true-colour RGB
  4373. * triple is selected by 5 args of
  4374. * the form 38;2;r;g;b. Replacing
  4375. * the initial 38 with 48 in both
  4376. * cases selects the same colour
  4377. * as the background.
  4378. */
  4379. case 38:
  4380. if (i+2 < term->esc_nargs &&
  4381. term->esc_args[i+1] == 5) {
  4382. term->curr_attr &= ~ATTR_FGMASK;
  4383. term->curr_attr |=
  4384. ((term->esc_args[i+2] & 0xFF)
  4385. << ATTR_FGSHIFT);
  4386. term->curr_truecolour.fg =
  4387. optionalrgb_none;
  4388. i += 2;
  4389. }
  4390. if (i + 4 < term->esc_nargs &&
  4391. term->esc_args[i + 1] == 2) {
  4392. parse_optionalrgb(
  4393. &term->curr_truecolour.fg,
  4394. term->esc_args + (i+2));
  4395. i += 4;
  4396. }
  4397. break;
  4398. case 48:
  4399. if (i+2 < term->esc_nargs &&
  4400. term->esc_args[i+1] == 5) {
  4401. term->curr_attr &= ~ATTR_BGMASK;
  4402. term->curr_attr |=
  4403. ((term->esc_args[i+2] & 0xFF)
  4404. << ATTR_BGSHIFT);
  4405. term->curr_truecolour.bg =
  4406. optionalrgb_none;
  4407. i += 2;
  4408. }
  4409. if (i + 4 < term->esc_nargs &&
  4410. term->esc_args[i+1] == 2) {
  4411. parse_optionalrgb(
  4412. &term->curr_truecolour.bg,
  4413. term->esc_args + (i+2));
  4414. i += 4;
  4415. }
  4416. break;
  4417. }
  4418. set_erase_char(term);
  4419. break;
  4420. case 's': /* save cursor */
  4421. save_cursor(term, true);
  4422. break;
  4423. case 'u': /* restore cursor */
  4424. save_cursor(term, false);
  4425. break;
  4426. case 't': /* DECSLPP: set page size - ie window height */
  4427. /*
  4428. * VT340/VT420 sequence DECSLPP, DEC only allows values
  4429. * 24/25/36/48/72/144 other emulators (eg dtterm) use
  4430. * illegal values (eg first arg 1..9) for window changing
  4431. * and reports.
  4432. */
  4433. if (term->esc_nargs <= 1
  4434. && (term->esc_args[0] < 1 ||
  4435. term->esc_args[0] >= 24)) {
  4436. compatibility(VT340TEXT);
  4437. if (!term->no_remote_resize)
  4438. term_request_resize(term, term->cols, 24);
  4439. deselect(term);
  4440. } else if (term->esc_nargs >= 1 &&
  4441. term->esc_args[0] >= 1 &&
  4442. term->esc_args[0] < 24) {
  4443. compatibility(OTHER);
  4444. switch (term->esc_args[0]) {
  4445. int len;
  4446. char buf[80];
  4447. const char *p;
  4448. case 1:
  4449. term->win_minimise_pending = true;
  4450. term->win_minimise_enable = false;
  4451. term_schedule_update(term);
  4452. break;
  4453. case 2:
  4454. term->win_minimise_pending = true;
  4455. term->win_minimise_enable = true;
  4456. term_schedule_update(term);
  4457. break;
  4458. case 3:
  4459. if (term->esc_nargs >= 3) {
  4460. if (!term->no_remote_resize) {
  4461. term->win_move_pending = true;
  4462. term->win_move_pending_x =
  4463. def(term->esc_args[1], 0);
  4464. term->win_move_pending_y =
  4465. def(term->esc_args[2], 0);
  4466. term_schedule_update(term);
  4467. }
  4468. }
  4469. break;
  4470. case 4:
  4471. /* We should resize the window to a given
  4472. * size in pixels here, but currently our
  4473. * resizing code isn't healthy enough to
  4474. * manage it. */
  4475. break;
  4476. case 5:
  4477. /* move to top */
  4478. term->win_zorder_pending = true;
  4479. term->win_zorder_top = true;
  4480. term_schedule_update(term);
  4481. break;
  4482. case 6:
  4483. /* move to bottom */
  4484. term->win_zorder_pending = true;
  4485. term->win_zorder_top = false;
  4486. term_schedule_update(term);
  4487. break;
  4488. case 7:
  4489. term->win_refresh_pending = true;
  4490. term_schedule_update(term);
  4491. break;
  4492. case 8:
  4493. if (term->esc_nargs >= 3 &&
  4494. !term->no_remote_resize) {
  4495. term_request_resize(
  4496. term,
  4497. def(term->esc_args[2],
  4498. term->conf_width),
  4499. def(term->esc_args[1],
  4500. term->conf_height));
  4501. }
  4502. break;
  4503. case 9:
  4504. if (term->esc_nargs >= 2) {
  4505. term->win_maximise_pending = true;
  4506. term->win_maximise_enable =
  4507. term->esc_args[1];
  4508. term_schedule_update(term);
  4509. }
  4510. break;
  4511. case 11:
  4512. if (term->ldisc)
  4513. ldisc_send(term->ldisc, term->minimised ?
  4514. "\033[2t" : "\033[1t", 4,
  4515. false);
  4516. break;
  4517. case 13:
  4518. if (term->ldisc) {
  4519. len = sprintf(buf, "\033[3;%u;%ut",
  4520. term->winpos_x,
  4521. term->winpos_y);
  4522. ldisc_send(term->ldisc, buf, len, false);
  4523. }
  4524. break;
  4525. case 14:
  4526. if (term->ldisc) {
  4527. len = sprintf(buf, "\033[4;%u;%ut",
  4528. term->winpixsize_y,
  4529. term->winpixsize_x);
  4530. ldisc_send(term->ldisc, buf, len, false);
  4531. }
  4532. break;
  4533. case 18:
  4534. if (term->ldisc) {
  4535. len = sprintf(buf, "\033[8;%d;%dt",
  4536. term->rows, term->cols);
  4537. ldisc_send(term->ldisc, buf, len, false);
  4538. }
  4539. break;
  4540. case 19:
  4541. /*
  4542. * Hmmm. Strictly speaking we
  4543. * should return `the size of the
  4544. * screen in characters', but
  4545. * that's not easy: (a) window
  4546. * furniture being what it is it's
  4547. * hard to compute, and (b) in
  4548. * resize-font mode maximising the
  4549. * window wouldn't change the
  4550. * number of characters. *shrug*. I
  4551. * think we'll ignore it for the
  4552. * moment and see if anyone
  4553. * complains, and then ask them
  4554. * what they would like it to do.
  4555. */
  4556. break;
  4557. case 20:
  4558. if (term->ldisc &&
  4559. term->remote_qtitle_action != TITLE_NONE) {
  4560. if(term->remote_qtitle_action == TITLE_REAL)
  4561. p = term->icon_title;
  4562. else
  4563. p = EMPTY_WINDOW_TITLE;
  4564. len = strlen(p);
  4565. ldisc_send(term->ldisc, "\033]L", 3,
  4566. false);
  4567. ldisc_send(term->ldisc, p, len, false);
  4568. ldisc_send(term->ldisc, "\033\\", 2,
  4569. false);
  4570. }
  4571. break;
  4572. case 21:
  4573. if (term->ldisc &&
  4574. term->remote_qtitle_action != TITLE_NONE) {
  4575. if(term->remote_qtitle_action == TITLE_REAL)
  4576. p = term->window_title;
  4577. else
  4578. p = EMPTY_WINDOW_TITLE;
  4579. len = strlen(p);
  4580. ldisc_send(term->ldisc, "\033]l", 3,
  4581. false);
  4582. ldisc_send(term->ldisc, p, len, false);
  4583. ldisc_send(term->ldisc, "\033\\", 2,
  4584. false);
  4585. }
  4586. break;
  4587. }
  4588. }
  4589. break;
  4590. case 'S': /* SU: Scroll up */
  4591. CLAMP(term->esc_args[0], term->rows);
  4592. compatibility(SCOANSI);
  4593. scroll(term, term->marg_t, term->marg_b,
  4594. def(term->esc_args[0], 1), true);
  4595. term->wrapnext = false;
  4596. break;
  4597. case 'T': /* SD: Scroll down */
  4598. CLAMP(term->esc_args[0], term->rows);
  4599. compatibility(SCOANSI);
  4600. scroll(term, term->marg_t, term->marg_b,
  4601. -def(term->esc_args[0], 1), true);
  4602. term->wrapnext = false;
  4603. break;
  4604. case ANSI('|', '*'): /* DECSNLS */
  4605. /*
  4606. * Set number of lines on screen
  4607. * VT420 uses VGA like hardware and can
  4608. * support any size in reasonable range
  4609. * (24..49 AIUI) with no default specified.
  4610. */
  4611. compatibility(VT420);
  4612. if (term->esc_nargs == 1 && term->esc_args[0] > 0) {
  4613. if (!term->no_remote_resize)
  4614. term_request_resize(
  4615. term,
  4616. term->cols,
  4617. def(term->esc_args[0], term->conf_height));
  4618. deselect(term);
  4619. }
  4620. break;
  4621. case ANSI('|', '$'): /* DECSCPP */
  4622. /*
  4623. * Set number of columns per page
  4624. * Docs imply range is only 80 or 132, but
  4625. * I'll allow any.
  4626. */
  4627. compatibility(VT340TEXT);
  4628. if (term->esc_nargs <= 1) {
  4629. if (!term->no_remote_resize)
  4630. term_request_resize(
  4631. term,
  4632. def(term->esc_args[0], term->conf_width),
  4633. term->rows);
  4634. deselect(term);
  4635. }
  4636. break;
  4637. case 'X': { /* ECH: write N spaces w/o moving cursor */
  4638. /* XXX VTTEST says this is vt220, vt510 manual
  4639. * says vt100 */
  4640. compatibility(ANSIMIN);
  4641. CLAMP(term->esc_args[0], term->cols);
  4642. int n = def(term->esc_args[0], 1);
  4643. pos cursplus;
  4644. int p = term->curs.x;
  4645. termline *cline = scrlineptr(term->curs.y);
  4646. check_trust_status(term, cline);
  4647. if (n > term->cols - term->curs.x)
  4648. n = term->cols - term->curs.x;
  4649. cursplus = term->curs;
  4650. cursplus.x += n;
  4651. check_boundary(term, term->curs.x, term->curs.y);
  4652. check_boundary(term, term->curs.x+n, term->curs.y);
  4653. check_selection(term, term->curs, cursplus);
  4654. while (n--)
  4655. copy_termchar(cline, p++,
  4656. &term->erase_char);
  4657. seen_disp_event(term);
  4658. break;
  4659. }
  4660. case 'x': /* DECREQTPARM: report terminal characteristics */
  4661. compatibility(VT100);
  4662. if (term->ldisc) {
  4663. char buf[32];
  4664. int i = def(term->esc_args[0], 0);
  4665. if (i == 0 || i == 1) {
  4666. strcpy(buf, "\033[2;1;1;112;112;1;0x");
  4667. buf[2] += i;
  4668. ldisc_send(term->ldisc, buf, 20, false);
  4669. }
  4670. }
  4671. break;
  4672. case 'Z': { /* CBT */
  4673. compatibility(OTHER);
  4674. CLAMP(term->esc_args[0], term->cols);
  4675. int i = def(term->esc_args[0], 1);
  4676. pos old_curs = term->curs;
  4677. for (; i>0 && term->curs.x>0; i--) {
  4678. do {
  4679. term->curs.x--;
  4680. } while (term->curs.x >0 &&
  4681. !term->tabs[term->curs.x]);
  4682. }
  4683. check_selection(term, old_curs, term->curs);
  4684. break;
  4685. }
  4686. case ANSI('c', '='): /* Hide or Show Cursor */
  4687. compatibility(SCOANSI);
  4688. switch(term->esc_args[0]) {
  4689. case 0: /* hide cursor */
  4690. term->cursor_on = false;
  4691. break;
  4692. case 1: /* restore cursor */
  4693. term->big_cursor = false;
  4694. term->cursor_on = true;
  4695. break;
  4696. case 2: /* block cursor */
  4697. term->big_cursor = true;
  4698. term->cursor_on = true;
  4699. break;
  4700. }
  4701. break;
  4702. case ANSI('C', '='):
  4703. /*
  4704. * set cursor start on scanline esc_args[0] and
  4705. * end on scanline esc_args[1].If you set
  4706. * the bottom scan line to a value less than
  4707. * the top scan line, the cursor will disappear.
  4708. */
  4709. compatibility(SCOANSI);
  4710. if (term->esc_nargs >= 2) {
  4711. if (term->esc_args[0] > term->esc_args[1])
  4712. term->cursor_on = false;
  4713. else
  4714. term->cursor_on = true;
  4715. }
  4716. break;
  4717. case ANSI('D', '='):
  4718. compatibility(SCOANSI);
  4719. term->blink_is_real = false;
  4720. term_schedule_tblink(term);
  4721. if (term->esc_args[0]>=1)
  4722. term->curr_attr |= ATTR_BLINK;
  4723. else
  4724. term->curr_attr &= ~ATTR_BLINK;
  4725. break;
  4726. case ANSI('E', '='):
  4727. compatibility(SCOANSI);
  4728. term->blink_is_real = (term->esc_args[0] >= 1);
  4729. term_schedule_tblink(term);
  4730. break;
  4731. case ANSI('F', '='): /* set normal foreground */
  4732. compatibility(SCOANSI);
  4733. if (term->esc_args[0] < 16) {
  4734. long colour =
  4735. (sco2ansicolour[term->esc_args[0] & 0x7] |
  4736. (term->esc_args[0] & 0x8)) <<
  4737. ATTR_FGSHIFT;
  4738. term->curr_attr &= ~ATTR_FGMASK;
  4739. term->curr_attr |= colour;
  4740. term->curr_truecolour.fg = optionalrgb_none;
  4741. term->default_attr &= ~ATTR_FGMASK;
  4742. term->default_attr |= colour;
  4743. set_erase_char(term);
  4744. }
  4745. break;
  4746. case ANSI('G', '='): /* set normal background */
  4747. compatibility(SCOANSI);
  4748. if (term->esc_args[0] < 16) {
  4749. long colour =
  4750. (sco2ansicolour[term->esc_args[0] & 0x7] |
  4751. (term->esc_args[0] & 0x8)) <<
  4752. ATTR_BGSHIFT;
  4753. term->curr_attr &= ~ATTR_BGMASK;
  4754. term->curr_attr |= colour;
  4755. term->curr_truecolour.bg = optionalrgb_none;
  4756. term->default_attr &= ~ATTR_BGMASK;
  4757. term->default_attr |= colour;
  4758. set_erase_char(term);
  4759. }
  4760. break;
  4761. case ANSI('L', '='):
  4762. compatibility(SCOANSI);
  4763. term->use_bce = (term->esc_args[0] <= 0);
  4764. set_erase_char(term);
  4765. break;
  4766. case ANSI('p', '"'): /* DECSCL: set compat level */
  4767. /*
  4768. * Allow the host to make this emulator a
  4769. * 'perfect' VT102. This first appeared in
  4770. * the VT220, but we do need to get back to
  4771. * PuTTY mode so I won't check it.
  4772. *
  4773. * The arg in 40..42,50 are a PuTTY extension.
  4774. * The 2nd arg, 8bit vs 7bit is not checked.
  4775. *
  4776. * Setting VT102 mode should also change
  4777. * the Fkeys to generate PF* codes as a
  4778. * real VT102 has no Fkeys. The VT220 does
  4779. * this, F11..F13 become ESC,BS,LF other
  4780. * Fkeys send nothing.
  4781. *
  4782. * Note ESC c will NOT change this!
  4783. */
  4784. switch (term->esc_args[0]) {
  4785. case 61:
  4786. term->compatibility_level &= ~TM_VTXXX;
  4787. term->compatibility_level |= TM_VT102;
  4788. break;
  4789. case 62:
  4790. term->compatibility_level &= ~TM_VTXXX;
  4791. term->compatibility_level |= TM_VT220;
  4792. break;
  4793. default:
  4794. if (term->esc_args[0] > 60 &&
  4795. term->esc_args[0] < 70)
  4796. term->compatibility_level |= TM_VTXXX;
  4797. break;
  4798. case 40:
  4799. term->compatibility_level &= TM_VTXXX;
  4800. break;
  4801. case 41:
  4802. term->compatibility_level = TM_PUTTY;
  4803. break;
  4804. case 42:
  4805. term->compatibility_level = TM_SCOANSI;
  4806. break;
  4807. case ARG_DEFAULT:
  4808. term->compatibility_level = TM_PUTTY;
  4809. break;
  4810. case 50:
  4811. break;
  4812. }
  4813. /* Change the response to CSI c */
  4814. if (term->esc_args[0] == 50) {
  4815. int i;
  4816. char lbuf[64];
  4817. strcpy(term->id_string, "\033[?");
  4818. for (i = 1; i < term->esc_nargs; i++) {
  4819. if (i != 1)
  4820. strcat(term->id_string, ";");
  4821. sprintf(lbuf, "%u", term->esc_args[i]);
  4822. strcat(term->id_string, lbuf);
  4823. }
  4824. strcat(term->id_string, "c");
  4825. }
  4826. #if 0
  4827. /* Is this a good idea ?
  4828. * Well we should do a soft reset at this point ...
  4829. */
  4830. if (!has_compat(VT420) && has_compat(VT100)) {
  4831. if (!term->no_remote_resize)
  4832. term_request_resize(term,
  4833. term->reset_132 ? 132 : 80,
  4834. 24);
  4835. }
  4836. #endif
  4837. break;
  4838. }
  4839. break;
  4840. case SEEN_OSC:
  4841. term->osc_w = false;
  4842. switch (c) {
  4843. case 'P': /* Linux palette sequence */
  4844. term->termstate = SEEN_OSC_P;
  4845. term->osc_strlen = 0;
  4846. break;
  4847. case 'R': /* Linux palette reset */
  4848. palette_reset(term, false);
  4849. term_invalidate(term);
  4850. term->termstate = TOPLEVEL;
  4851. break;
  4852. case 'W': /* word-set */
  4853. term->termstate = SEEN_OSC_W;
  4854. term->osc_w = true;
  4855. break;
  4856. case '0':
  4857. case '1':
  4858. case '2':
  4859. case '3':
  4860. case '4':
  4861. case '5':
  4862. case '6':
  4863. case '7':
  4864. case '8':
  4865. case '9':
  4866. if (term->esc_args[term->esc_nargs-1] <= UINT_MAX / 10 &&
  4867. term->esc_args[term->esc_nargs-1] * 10 <= UINT_MAX - c - '0')
  4868. term->esc_args[term->esc_nargs-1] =
  4869. 10 * term->esc_args[term->esc_nargs-1] + c - '0';
  4870. else
  4871. term->esc_args[term->esc_nargs-1] = UINT_MAX;
  4872. break;
  4873. case 0x9C:
  4874. /* Terminate even though we aren't in OSC_STRING yet */
  4875. do_osc(term);
  4876. term->termstate = TOPLEVEL;
  4877. break;
  4878. case 0xC2:
  4879. if (in_utf(term)) {
  4880. /* Or be prepared for the UTF-8 version of that */
  4881. term->termstate = OSC_MAYBE_ST_UTF8;
  4882. }
  4883. break;
  4884. default:
  4885. /*
  4886. * _Most_ other characters here terminate the
  4887. * immediate parsing of the OSC sequence and go
  4888. * into OSC_STRING state, but we deal with a
  4889. * couple of exceptions first.
  4890. */
  4891. if (c == 'L' && term->esc_args[0] == 2) {
  4892. /*
  4893. * Grotty hack to support xterm and DECterm title
  4894. * sequences concurrently.
  4895. */
  4896. term->esc_args[0] = 1;
  4897. } else if (c == ';' && term->esc_nargs == 1 &&
  4898. term->esc_args[0] == 4) {
  4899. /*
  4900. * xterm's OSC 4 sequence to query the current
  4901. * RGB value of a colour takes a second
  4902. * numeric argument which is easiest to parse
  4903. * using the existing system rather than in
  4904. * do_osc.
  4905. */
  4906. term->esc_args[term->esc_nargs++] = 0;
  4907. } else {
  4908. term->termstate = OSC_STRING;
  4909. term->osc_strlen = 0;
  4910. }
  4911. }
  4912. break;
  4913. case OSC_STRING:
  4914. /*
  4915. * OSC sequences can be terminated or aborted in
  4916. * various ways.
  4917. *
  4918. * The official way to terminate an OSC, per written
  4919. * standards, is the String Terminator, SC. That can
  4920. * appear in a 7-bit two-character form ESC \, or as
  4921. * an 8-bit C1 control 0x9C.
  4922. *
  4923. * We only accept 0x9C in circumstances where it
  4924. * doesn't interfere with our main character set
  4925. * processing: so in ISO 8859-1, for example, the byte
  4926. * 0x9C is interpreted as ST, but in CP437 it's
  4927. * interpreted as an ordinary printing character (as
  4928. * it happens, the pound sign), because you might
  4929. * perfectly well want to put it in the window title
  4930. * like any other printing character.
  4931. *
  4932. * In particular, in UTF-8 mode, 0x9C is a perfectly
  4933. * valid continuation byte for an ordinary printing
  4934. * character, so we don't accept the C1 control form
  4935. * of ST unless it appears as a full UTF-8 character
  4936. * in its own right, i.e. bytes 0xC2 0x9C.
  4937. *
  4938. * BEL is also treated as a clean termination of OSC,
  4939. * which I believe was a behaviour introduced by
  4940. * xterm.
  4941. *
  4942. * To prevent run-on storage of OSC data forever if
  4943. * emission of a control sequence is interrupted, we
  4944. * also treat various control characters as illegal,
  4945. * so that they abort the OSC without processing it
  4946. * and return to TOPLEVEL state. These are CR, LF, and
  4947. * any ESC that is *not* followed by \.
  4948. */
  4949. if (c == '\012' || c == '\015') {
  4950. /* CR or LF aborts */
  4951. term->termstate = TOPLEVEL;
  4952. break;
  4953. }
  4954. if (c == '\033') {
  4955. /* ESC goes into a state where we wait to see if
  4956. * the next character is \ */
  4957. term->termstate = OSC_MAYBE_ST;
  4958. break;
  4959. }
  4960. if (c == '\007' || (c == 0x9C && !in_utf(term) &&
  4961. term->ucsdata->unitab_ctrl[c] != 0xFF)) {
  4962. /* BEL, or the C1 ST appearing as a one-byte
  4963. * encoding, cleanly terminates the OSC right here */
  4964. do_osc(term);
  4965. term->termstate = TOPLEVEL;
  4966. break;
  4967. }
  4968. if (c == 0xC2 && in_utf(term)) {
  4969. /* 0xC2 is the UTF-8 character that might
  4970. * introduce the encoding of C1 ST */
  4971. term->termstate = OSC_MAYBE_ST_UTF8;
  4972. break;
  4973. }
  4974. /* Anything else gets added to the string */
  4975. if (term->osc_strlen < OSC_STR_MAX)
  4976. term->osc_string[term->osc_strlen++] = (char)c;
  4977. break;
  4978. case OSC_MAYBE_ST_UTF8:
  4979. /* In UTF-8 mode, we've seen C2, so are we now seeing
  4980. * 9C? */
  4981. if (c == 0x9C) {
  4982. /* Yes, so cleanly terminate the OSC */
  4983. do_osc(term);
  4984. term->termstate = TOPLEVEL;
  4985. break;
  4986. }
  4987. /* No, so append the pending C2 byte to the OSC string
  4988. * followed by the current character, and go back to
  4989. * OSC string accumulation */
  4990. if (term->osc_strlen < OSC_STR_MAX)
  4991. term->osc_string[term->osc_strlen++] = 0xC2;
  4992. if (term->osc_strlen < OSC_STR_MAX)
  4993. term->osc_string[term->osc_strlen++] = (char)c;
  4994. term->termstate = OSC_STRING;
  4995. break;
  4996. case SEEN_OSC_P: {
  4997. int max = (term->osc_strlen == 0 ? 21 : 15);
  4998. int val;
  4999. if ((int)c >= '0' && (int)c <= '9')
  5000. val = c - '0';
  5001. else if ((int)c >= 'A' && (int)c <= 'A' + max - 10)
  5002. val = c - 'A' + 10;
  5003. else if ((int)c >= 'a' && (int)c <= 'a' + max - 10)
  5004. val = c - 'a' + 10;
  5005. else {
  5006. term->termstate = TOPLEVEL;
  5007. break;
  5008. }
  5009. term->osc_string[term->osc_strlen++] = val;
  5010. if (term->osc_strlen >= 7) {
  5011. unsigned oscp_index = term->osc_string[0];
  5012. assert(oscp_index < OSCP_NCOLOURS);
  5013. unsigned osc4_index =
  5014. colour_indices_oscp_to_osc4[oscp_index];
  5015. rgb *value = &term->subpalettes[SUBPAL_SESSION].values[
  5016. osc4_index];
  5017. value->r = term->osc_string[1] * 16 + term->osc_string[2];
  5018. value->g = term->osc_string[3] * 16 + term->osc_string[4];
  5019. value->b = term->osc_string[5] * 16 + term->osc_string[6];
  5020. term->subpalettes[SUBPAL_SESSION].present[
  5021. osc4_index] = true;
  5022. palette_rebuild(term);
  5023. term->termstate = TOPLEVEL;
  5024. }
  5025. break;
  5026. }
  5027. case SEEN_OSC_W:
  5028. switch (c) {
  5029. case '0':
  5030. case '1':
  5031. case '2':
  5032. case '3':
  5033. case '4':
  5034. case '5':
  5035. case '6':
  5036. case '7':
  5037. case '8':
  5038. case '9':
  5039. if (term->esc_args[0] <= UINT_MAX / 10 &&
  5040. term->esc_args[0] * 10 <= UINT_MAX - c - '0')
  5041. term->esc_args[0] = 10 * term->esc_args[0] + c - '0';
  5042. else
  5043. term->esc_args[0] = UINT_MAX;
  5044. break;
  5045. case 0x9C:
  5046. /* Terminate even though we aren't in OSC_STRING yet */
  5047. do_osc(term);
  5048. term->termstate = TOPLEVEL;
  5049. break;
  5050. case 0xC2:
  5051. if (in_utf(term)) {
  5052. /* Or be prepared for the UTF-8 version of that */
  5053. term->termstate = OSC_MAYBE_ST_UTF8;
  5054. }
  5055. break;
  5056. default:
  5057. term->termstate = OSC_STRING;
  5058. term->osc_strlen = 0;
  5059. }
  5060. break;
  5061. case VT52_ESC:
  5062. term->termstate = TOPLEVEL;
  5063. switch (c) {
  5064. case 'A':
  5065. move(term, term->curs.x, term->curs.y - 1, 1);
  5066. break;
  5067. case 'B':
  5068. move(term, term->curs.x, term->curs.y + 1, 1);
  5069. break;
  5070. case 'C':
  5071. move(term, term->curs.x + 1, term->curs.y, 1);
  5072. break;
  5073. case 'D':
  5074. move(term, term->curs.x - 1, term->curs.y, 1);
  5075. break;
  5076. /*
  5077. * From the VT100 Manual
  5078. * NOTE: The special graphics characters in the VT100
  5079. * are different from those in the VT52
  5080. *
  5081. * From VT102 manual:
  5082. * 137 _ Blank - Same
  5083. * 140 ` Reserved - Humm.
  5084. * 141 a Solid rectangle - Similar
  5085. * 142 b 1/ - Top half of fraction for the
  5086. * 143 c 3/ - subscript numbers below.
  5087. * 144 d 5/
  5088. * 145 e 7/
  5089. * 146 f Degrees - Same
  5090. * 147 g Plus or minus - Same
  5091. * 150 h Right arrow
  5092. * 151 i Ellipsis (dots)
  5093. * 152 j Divide by
  5094. * 153 k Down arrow
  5095. * 154 l Bar at scan 0
  5096. * 155 m Bar at scan 1
  5097. * 156 n Bar at scan 2
  5098. * 157 o Bar at scan 3 - Similar
  5099. * 160 p Bar at scan 4 - Similar
  5100. * 161 q Bar at scan 5 - Similar
  5101. * 162 r Bar at scan 6 - Same
  5102. * 163 s Bar at scan 7 - Similar
  5103. * 164 t Subscript 0
  5104. * 165 u Subscript 1
  5105. * 166 v Subscript 2
  5106. * 167 w Subscript 3
  5107. * 170 x Subscript 4
  5108. * 171 y Subscript 5
  5109. * 172 z Subscript 6
  5110. * 173 { Subscript 7
  5111. * 174 | Subscript 8
  5112. * 175 } Subscript 9
  5113. * 176 ~ Paragraph
  5114. *
  5115. */
  5116. case 'F':
  5117. term->cset_attr[term->cset = 0] = CSET_LINEDRW;
  5118. break;
  5119. case 'G':
  5120. term->cset_attr[term->cset = 0] = CSET_ASCII;
  5121. break;
  5122. case 'H':
  5123. move(term, 0, 0, 0);
  5124. break;
  5125. case 'I':
  5126. if (term->curs.y == 0) {
  5127. scroll(term, 0, term->rows - 1, -1, true);
  5128. } else if (term->curs.y > 0) {
  5129. term->curs.y--;
  5130. seen_disp_event(term);
  5131. }
  5132. term->wrapnext = false;
  5133. break;
  5134. case 'J':
  5135. erase_lots(term, false, false, true);
  5136. if (term->scroll_on_disp)
  5137. term->disptop = 0;
  5138. break;
  5139. case 'K':
  5140. erase_lots(term, true, false, true);
  5141. break;
  5142. #if 0
  5143. case 'V':
  5144. /* XXX Print cursor line */
  5145. break;
  5146. case 'W':
  5147. /* XXX Start controller mode */
  5148. break;
  5149. case 'X':
  5150. /* XXX Stop controller mode */
  5151. break;
  5152. #endif
  5153. case 'Y':
  5154. term->termstate = VT52_Y1;
  5155. break;
  5156. case 'Z':
  5157. if (term->ldisc)
  5158. ldisc_send(term->ldisc, "\033/Z", 3, false);
  5159. break;
  5160. case '=':
  5161. term->app_keypad_keys = true;
  5162. break;
  5163. case '>':
  5164. term->app_keypad_keys = false;
  5165. break;
  5166. case '<':
  5167. /* XXX This should switch to VT100 mode not current or default
  5168. * VT mode. But this will only have effect in a VT220+
  5169. * emulation.
  5170. */
  5171. term->vt52_mode = false;
  5172. term->blink_is_real = term->blinktext;
  5173. term_schedule_tblink(term);
  5174. break;
  5175. #if 0
  5176. case '^':
  5177. /* XXX Enter auto print mode */
  5178. break;
  5179. case '_':
  5180. /* XXX Exit auto print mode */
  5181. break;
  5182. case ']':
  5183. /* XXX Print screen */
  5184. break;
  5185. #endif
  5186. #ifdef VT52_PLUS
  5187. case 'E':
  5188. /* compatibility(ATARI) */
  5189. move(term, 0, 0, 0);
  5190. erase_lots(term, false, false, true);
  5191. if (term->scroll_on_disp)
  5192. term->disptop = 0;
  5193. break;
  5194. case 'L':
  5195. /* compatibility(ATARI) */
  5196. if (term->curs.y <= term->marg_b)
  5197. scroll(term, term->curs.y, term->marg_b, -1, false);
  5198. break;
  5199. case 'M':
  5200. /* compatibility(ATARI) */
  5201. if (term->curs.y <= term->marg_b)
  5202. scroll(term, term->curs.y, term->marg_b, 1, true);
  5203. break;
  5204. case 'b':
  5205. /* compatibility(ATARI) */
  5206. term->termstate = VT52_FG;
  5207. break;
  5208. case 'c':
  5209. /* compatibility(ATARI) */
  5210. term->termstate = VT52_BG;
  5211. break;
  5212. case 'd':
  5213. /* compatibility(ATARI) */
  5214. erase_lots(term, false, true, false);
  5215. if (term->scroll_on_disp)
  5216. term->disptop = 0;
  5217. break;
  5218. case 'e':
  5219. /* compatibility(ATARI) */
  5220. term->cursor_on = true;
  5221. seen_disp_event(term);
  5222. break;
  5223. case 'f':
  5224. /* compatibility(ATARI) */
  5225. term->cursor_on = false;
  5226. seen_disp_event(term);
  5227. break;
  5228. /* case 'j': Save cursor position - broken on ST */
  5229. /* case 'k': Restore cursor position */
  5230. case 'l':
  5231. /* compatibility(ATARI) */
  5232. erase_lots(term, true, true, true);
  5233. term->curs.x = 0;
  5234. term->wrapnext = false;
  5235. break;
  5236. case 'o':
  5237. /* compatibility(ATARI) */
  5238. erase_lots(term, true, true, false);
  5239. break;
  5240. case 'p':
  5241. /* compatibility(ATARI) */
  5242. term->curr_attr |= ATTR_REVERSE;
  5243. break;
  5244. case 'q':
  5245. /* compatibility(ATARI) */
  5246. term->curr_attr &= ~ATTR_REVERSE;
  5247. break;
  5248. case 'v': /* wrap Autowrap on - Wyse style */
  5249. /* compatibility(ATARI) */
  5250. term->wrap = true;
  5251. break;
  5252. case 'w': /* Autowrap off */
  5253. /* compatibility(ATARI) */
  5254. term->wrap = false;
  5255. term->wrapnext = false;
  5256. break;
  5257. case 'R':
  5258. /* compatibility(OTHER) */
  5259. term->vt52_bold = false;
  5260. term->curr_attr = ATTR_DEFAULT;
  5261. term->curr_truecolour.fg = optionalrgb_none;
  5262. term->curr_truecolour.bg = optionalrgb_none;
  5263. set_erase_char(term);
  5264. break;
  5265. case 'S':
  5266. /* compatibility(VI50) */
  5267. term->curr_attr |= ATTR_UNDER;
  5268. break;
  5269. case 'W':
  5270. /* compatibility(VI50) */
  5271. term->curr_attr &= ~ATTR_UNDER;
  5272. break;
  5273. case 'U':
  5274. /* compatibility(VI50) */
  5275. term->vt52_bold = true;
  5276. term->curr_attr |= ATTR_BOLD;
  5277. break;
  5278. case 'T':
  5279. /* compatibility(VI50) */
  5280. term->vt52_bold = false;
  5281. term->curr_attr &= ~ATTR_BOLD;
  5282. break;
  5283. #endif
  5284. }
  5285. break;
  5286. case VT52_Y1:
  5287. term->termstate = VT52_Y2;
  5288. move(term, term->curs.x, c - ' ', 0);
  5289. break;
  5290. case VT52_Y2:
  5291. term->termstate = TOPLEVEL;
  5292. move(term, c - ' ', term->curs.y, 0);
  5293. break;
  5294. #ifdef VT52_PLUS
  5295. case VT52_FG:
  5296. term->termstate = TOPLEVEL;
  5297. term->curr_attr &= ~ATTR_FGMASK;
  5298. term->curr_attr &= ~ATTR_BOLD;
  5299. term->curr_attr |= (c & 0xF) << ATTR_FGSHIFT;
  5300. set_erase_char(term);
  5301. break;
  5302. case VT52_BG:
  5303. term->termstate = TOPLEVEL;
  5304. term->curr_attr &= ~ATTR_BGMASK;
  5305. term->curr_attr &= ~ATTR_BLINK;
  5306. term->curr_attr |= (c & 0xF) << ATTR_BGSHIFT;
  5307. set_erase_char(term);
  5308. break;
  5309. #endif
  5310. default: break; /* placate gcc warning about enum use */
  5311. }
  5312. if (term->selstate != NO_SELECTION) {
  5313. pos cursplus = term->curs;
  5314. incpos(cursplus);
  5315. check_selection(term, term->curs, cursplus);
  5316. }
  5317. }
  5318. bufchain_consume(&term->inbuf, nchars_used);
  5319. if (!called_from_term_data)
  5320. win_unthrottle(term->win, bufchain_size(&term->inbuf));
  5321. term_print_flush(term);
  5322. if (term->logflush && term->logctx)
  5323. logflush(term->logctx);
  5324. }
  5325. /* Wrapper on term_out with the right prototype to be a toplevel callback */
  5326. void term_out_cb(void *ctx)
  5327. {
  5328. term_out((Terminal *)ctx, false);
  5329. }
  5330. /*
  5331. * Small subroutine to parse three consecutive escape-sequence
  5332. * arguments representing a true-colour RGB triple into an
  5333. * optionalrgb.
  5334. */
  5335. static void parse_optionalrgb(optionalrgb *out, unsigned *values)
  5336. {
  5337. out->enabled = true;
  5338. out->r = values[0] < 256 ? values[0] : 0;
  5339. out->g = values[1] < 256 ? values[1] : 0;
  5340. out->b = values[2] < 256 ? values[2] : 0;
  5341. }
  5342. /*
  5343. * To prevent having to run the reasonably tricky bidi algorithm
  5344. * too many times, we maintain a cache of the last lineful of data
  5345. * fed to the algorithm on each line of the display.
  5346. */
  5347. static bool term_bidi_cache_hit(Terminal *term, int line,
  5348. termchar *lbefore, int width, bool trusted)
  5349. {
  5350. int i;
  5351. if (!term->pre_bidi_cache)
  5352. return false; /* cache doesn't even exist yet! */
  5353. if (line >= term->bidi_cache_size)
  5354. return false; /* cache doesn't have this many lines */
  5355. if (!term->pre_bidi_cache[line].chars)
  5356. return false; /* cache doesn't contain _this_ line */
  5357. if (term->pre_bidi_cache[line].width != width)
  5358. return false; /* line is wrong width */
  5359. if (term->pre_bidi_cache[line].trusted != trusted)
  5360. return false; /* line has wrong trust state */
  5361. for (i = 0; i < width; i++)
  5362. if (!termchars_equal(term->pre_bidi_cache[line].chars+i, lbefore+i))
  5363. return false; /* line doesn't match cache */
  5364. return true; /* it didn't match. */
  5365. }
  5366. static void term_bidi_cache_store(Terminal *term, int line, termchar *lbefore,
  5367. termchar *lafter, bidi_char *wcTo,
  5368. int width, int size, bool trusted)
  5369. {
  5370. size_t i, j;
  5371. if (!term->pre_bidi_cache || term->bidi_cache_size <= line) {
  5372. j = term->bidi_cache_size;
  5373. sgrowarray(term->pre_bidi_cache, term->bidi_cache_size, line);
  5374. term->post_bidi_cache = sresize(term->post_bidi_cache,
  5375. term->bidi_cache_size,
  5376. struct bidi_cache_entry);
  5377. while (j < term->bidi_cache_size) {
  5378. term->pre_bidi_cache[j].chars =
  5379. term->post_bidi_cache[j].chars = NULL;
  5380. term->pre_bidi_cache[j].width =
  5381. term->post_bidi_cache[j].width = -1;
  5382. term->pre_bidi_cache[j].trusted = false;
  5383. term->post_bidi_cache[j].trusted = false;
  5384. term->pre_bidi_cache[j].forward =
  5385. term->post_bidi_cache[j].forward = NULL;
  5386. term->pre_bidi_cache[j].backward =
  5387. term->post_bidi_cache[j].backward = NULL;
  5388. j++;
  5389. }
  5390. }
  5391. sfree(term->pre_bidi_cache[line].chars);
  5392. sfree(term->post_bidi_cache[line].chars);
  5393. sfree(term->post_bidi_cache[line].forward);
  5394. sfree(term->post_bidi_cache[line].backward);
  5395. term->pre_bidi_cache[line].width = width;
  5396. term->pre_bidi_cache[line].trusted = trusted;
  5397. term->pre_bidi_cache[line].chars = snewn(size, termchar);
  5398. term->post_bidi_cache[line].width = width;
  5399. term->post_bidi_cache[line].trusted = trusted;
  5400. term->post_bidi_cache[line].chars = snewn(size, termchar);
  5401. term->post_bidi_cache[line].forward = snewn(width, int);
  5402. term->post_bidi_cache[line].backward = snewn(width, int);
  5403. memcpy(term->pre_bidi_cache[line].chars, lbefore, size * TSIZE);
  5404. memcpy(term->post_bidi_cache[line].chars, lafter, size * TSIZE);
  5405. memset(term->post_bidi_cache[line].forward, 0, width * sizeof(int));
  5406. memset(term->post_bidi_cache[line].backward, 0, width * sizeof(int));
  5407. for (i = j = 0; j < width; j += wcTo[i].nchars, i++) {
  5408. int p = wcTo[i].index;
  5409. if (p != BIDI_CHAR_INDEX_NONE) {
  5410. assert(0 <= p && p < width);
  5411. for (int x = 0; x < wcTo[i].nchars; x++) {
  5412. term->post_bidi_cache[line].backward[j+x] = p+x;
  5413. term->post_bidi_cache[line].forward[p+x] = j+x;
  5414. }
  5415. }
  5416. }
  5417. }
  5418. /*
  5419. * Prepare the bidi information for a screen line. Returns the
  5420. * transformed list of termchars, or NULL if no transformation at
  5421. * all took place (because bidi is disabled). If return was
  5422. * non-NULL, auxiliary information such as the forward and reverse
  5423. * mappings of permutation position are available in
  5424. * term->post_bidi_cache[scr_y].*.
  5425. */
  5426. static termchar *term_bidi_line(Terminal *term, struct termline *ldata,
  5427. int scr_y)
  5428. {
  5429. termchar *lchars;
  5430. int it;
  5431. /* Do Arabic shaping and bidi. */
  5432. if (!term->no_bidi || !term->no_arabicshaping ||
  5433. (ldata->trusted && term->cols > TRUST_SIGIL_WIDTH)) {
  5434. if (!term_bidi_cache_hit(term, scr_y, ldata->chars, term->cols,
  5435. ldata->trusted)) {
  5436. if (term->wcFromTo_size < term->cols) {
  5437. term->wcFromTo_size = term->cols;
  5438. term->wcFrom = sresize(term->wcFrom, term->wcFromTo_size,
  5439. bidi_char);
  5440. term->wcTo = sresize(term->wcTo, term->wcFromTo_size,
  5441. bidi_char);
  5442. }
  5443. for (it=0; it<term->cols ; it++) {
  5444. unsigned long uc = (ldata->chars[it].chr);
  5445. switch (uc & CSET_MASK) {
  5446. case CSET_LINEDRW:
  5447. if (!term->rawcnp) {
  5448. uc = term->ucsdata->unitab_xterm[uc & 0xFF];
  5449. break;
  5450. }
  5451. case CSET_ASCII:
  5452. uc = term->ucsdata->unitab_line[uc & 0xFF];
  5453. break;
  5454. case CSET_SCOACS:
  5455. uc = term->ucsdata->unitab_scoacs[uc&0xFF];
  5456. break;
  5457. }
  5458. switch (uc & CSET_MASK) {
  5459. case CSET_ACP:
  5460. uc = term->ucsdata->unitab_font[uc & 0xFF];
  5461. break;
  5462. case CSET_OEMCP:
  5463. uc = term->ucsdata->unitab_oemcp[uc & 0xFF];
  5464. break;
  5465. }
  5466. term->wcFrom[it].origwc = term->wcFrom[it].wc =
  5467. (unsigned int)uc;
  5468. term->wcFrom[it].index = it;
  5469. term->wcFrom[it].nchars = 1;
  5470. }
  5471. if (ldata->trusted && term->cols > TRUST_SIGIL_WIDTH) {
  5472. memmove(
  5473. term->wcFrom + TRUST_SIGIL_WIDTH, term->wcFrom,
  5474. (term->cols - TRUST_SIGIL_WIDTH) * sizeof(*term->wcFrom));
  5475. for (it = 0; it < TRUST_SIGIL_WIDTH; it++) {
  5476. term->wcFrom[it].origwc = term->wcFrom[it].wc =
  5477. (it == 0 ? TRUST_SIGIL_CHAR :
  5478. it == 1 ? UCSWIDE : ' ');
  5479. term->wcFrom[it].index = BIDI_CHAR_INDEX_NONE;
  5480. term->wcFrom[it].nchars = 1;
  5481. }
  5482. }
  5483. int nbc = 0;
  5484. for (it = 0; it < term->cols; it++) {
  5485. term->wcFrom[nbc] = term->wcFrom[it];
  5486. if (it+1 < term->cols && term->wcFrom[it+1].wc == UCSWIDE) {
  5487. term->wcFrom[nbc].nchars++;
  5488. it++;
  5489. }
  5490. nbc++;
  5491. }
  5492. if (!term->no_bidi)
  5493. do_bidi(term->bidi_ctx, term->wcFrom, nbc);
  5494. if (!term->no_arabicshaping) {
  5495. do_shape(term->wcFrom, term->wcTo, nbc);
  5496. } else {
  5497. /* If we're not calling do_shape, we must copy the
  5498. * data into wcTo anyway, unchanged */
  5499. memcpy(term->wcTo, term->wcFrom, nbc * sizeof(*term->wcTo));
  5500. }
  5501. if (term->ltemp_size < ldata->size) {
  5502. term->ltemp_size = ldata->size;
  5503. term->ltemp = sresize(term->ltemp, term->ltemp_size,
  5504. termchar);
  5505. }
  5506. memcpy(term->ltemp, ldata->chars, ldata->size * TSIZE);
  5507. int opos = 0;
  5508. for (it=0; it<nbc; it++) {
  5509. int ipos = term->wcTo[it].index;
  5510. for (int j = 0; j < term->wcTo[it].nchars; j++) {
  5511. if (ipos != BIDI_CHAR_INDEX_NONE) {
  5512. term->ltemp[opos] = ldata->chars[ipos];
  5513. if (term->ltemp[opos].cc_next)
  5514. term->ltemp[opos].cc_next -= opos - ipos;
  5515. if (j > 0)
  5516. term->ltemp[opos].chr = UCSWIDE;
  5517. else if (term->wcTo[it].origwc != term->wcTo[it].wc)
  5518. term->ltemp[opos].chr = term->wcTo[it].wc;
  5519. } else {
  5520. term->ltemp[opos] = term->basic_erase_char;
  5521. term->ltemp[opos].chr =
  5522. j > 0 ? UCSWIDE : term->wcTo[it].origwc;
  5523. }
  5524. opos++;
  5525. }
  5526. }
  5527. assert(opos == term->cols);
  5528. term_bidi_cache_store(term, scr_y, ldata->chars,
  5529. term->ltemp, term->wcTo,
  5530. term->cols, ldata->size, ldata->trusted);
  5531. lchars = term->ltemp;
  5532. } else {
  5533. lchars = term->post_bidi_cache[scr_y].chars;
  5534. }
  5535. } else {
  5536. lchars = NULL;
  5537. }
  5538. return lchars;
  5539. }
  5540. static void do_paint_draw(Terminal *term, termline *ldata, int x, int y,
  5541. wchar_t *ch, int ccount,
  5542. unsigned long attr, truecolour tc)
  5543. {
  5544. if (ch[0] == TRUST_SIGIL_CHAR) {
  5545. assert(ldata->trusted);
  5546. assert(ccount == 1);
  5547. assert(attr & ATTR_WIDE);
  5548. wchar_t tch[2];
  5549. tch[0] = tch[1] = L' ';
  5550. win_draw_text(term->win, x, y, tch, 2, term->basic_erase_char.attr,
  5551. ldata->lattr, term->basic_erase_char.truecolour);
  5552. win_draw_trust_sigil(term->win, x, y);
  5553. } else {
  5554. if (ccount == 2 &&
  5555. IS_REGIONAL_INDICATOR_LETTER(ch[0]) &&
  5556. IS_REGIONAL_INDICATOR_LETTER(ch[1]))
  5557. attr |= ATTR_WIDE | TATTR_COMBINING;
  5558. win_draw_text(term->win, x, y, ch, ccount, attr, ldata->lattr, tc);
  5559. if (attr & (TATTR_ACTCURS | TATTR_PASCURS))
  5560. win_draw_cursor(term->win, x, y, ch, ccount,
  5561. attr, ldata->lattr, tc);
  5562. }
  5563. }
  5564. /*
  5565. * Given a context, update the window.
  5566. */
  5567. static void do_paint(Terminal *term)
  5568. {
  5569. int i, j, our_curs_y, our_curs_x;
  5570. int rv, cursor;
  5571. pos scrpos;
  5572. wchar_t *ch;
  5573. size_t chlen;
  5574. termchar *newline;
  5575. chlen = 1024;
  5576. ch = snewn(chlen, wchar_t);
  5577. newline = snewn(term->cols, termchar);
  5578. rv = (!term->rvideo ^ !term->in_vbell ? ATTR_REVERSE : 0);
  5579. /* Depends on:
  5580. * screen array, disptop, scrtop,
  5581. * selection, rv,
  5582. * blinkpc, blink_is_real, tblinker,
  5583. * curs.y, curs.x, cblinker, blink_cur, cursor_on, has_focus, wrapnext
  5584. */
  5585. /* Has the cursor position or type changed ? */
  5586. if (term->cursor_on) {
  5587. if (term->has_focus) {
  5588. if (term->cblinker || !term->blink_cur)
  5589. cursor = TATTR_ACTCURS;
  5590. else
  5591. cursor = 0;
  5592. } else
  5593. cursor = TATTR_PASCURS;
  5594. if (term->wrapnext)
  5595. cursor |= TATTR_RIGHTCURS;
  5596. } else
  5597. cursor = 0;
  5598. our_curs_y = term->curs.y - term->disptop;
  5599. {
  5600. /*
  5601. * Adjust the cursor position:
  5602. * - for bidi
  5603. * - in the case where it's resting on the right-hand half
  5604. * of a CJK wide character. xterm's behaviour here,
  5605. * which seems adequate to me, is to display the cursor
  5606. * covering the _whole_ character, exactly as if it were
  5607. * one space to the left.
  5608. */
  5609. termline *ldata = lineptr(term->curs.y);
  5610. termchar *lchars;
  5611. our_curs_x = term->curs.x;
  5612. if ( (lchars = term_bidi_line(term, ldata, our_curs_y)) != NULL) {
  5613. our_curs_x = term->post_bidi_cache[our_curs_y].forward[our_curs_x];
  5614. } else
  5615. lchars = ldata->chars;
  5616. if (our_curs_x > 0 &&
  5617. lchars[our_curs_x].chr == UCSWIDE)
  5618. our_curs_x--;
  5619. unlineptr(ldata);
  5620. }
  5621. /*
  5622. * If the cursor is not where it was last time we painted, and
  5623. * its previous position is visible on screen, invalidate its
  5624. * previous position.
  5625. */
  5626. if (term->dispcursy >= 0 &&
  5627. (term->curstype != cursor ||
  5628. term->dispcursy != our_curs_y ||
  5629. term->dispcursx != our_curs_x)) {
  5630. termchar *dispcurs = term->disptext[term->dispcursy]->chars +
  5631. term->dispcursx;
  5632. if (term->dispcursx > 0 && dispcurs->chr == UCSWIDE)
  5633. dispcurs[-1].attr |= ATTR_INVALID;
  5634. if (term->dispcursx < term->cols-1 && dispcurs[1].chr == UCSWIDE)
  5635. dispcurs[1].attr |= ATTR_INVALID;
  5636. dispcurs->attr |= ATTR_INVALID;
  5637. term->curstype = 0;
  5638. }
  5639. term->dispcursx = term->dispcursy = -1;
  5640. /* The normal screen data */
  5641. for (i = 0; i < term->rows; i++) {
  5642. termline *ldata;
  5643. termchar *lchars;
  5644. bool dirty_line, dirty_run, selected;
  5645. unsigned long attr = 0, cset = 0;
  5646. int start = 0;
  5647. int ccount = 0;
  5648. bool last_run_dirty = false;
  5649. int laststart;
  5650. bool dirtyrect;
  5651. int *backward;
  5652. truecolour tc;
  5653. scrpos.y = i + term->disptop;
  5654. ldata = lineptr(scrpos.y);
  5655. /* Do Arabic shaping and bidi. */
  5656. lchars = term_bidi_line(term, ldata, i);
  5657. if (lchars) {
  5658. backward = term->post_bidi_cache[i].backward;
  5659. } else {
  5660. lchars = ldata->chars;
  5661. backward = NULL;
  5662. }
  5663. /*
  5664. * First loop: work along the line deciding what we want
  5665. * each character cell to look like.
  5666. */
  5667. for (j = 0; j < term->cols; j++) {
  5668. unsigned long tattr, tchar;
  5669. termchar *d = lchars + j;
  5670. scrpos.x = backward ? backward[j] : j;
  5671. tchar = d->chr;
  5672. tattr = d->attr;
  5673. if (!term->ansi_colour)
  5674. tattr = (tattr & ~(ATTR_FGMASK | ATTR_BGMASK)) |
  5675. ATTR_DEFFG | ATTR_DEFBG;
  5676. if (!term->xterm_256_colour) {
  5677. int colour;
  5678. colour = (tattr & ATTR_FGMASK) >> ATTR_FGSHIFT;
  5679. if (colour >= 16 && colour < 256)
  5680. tattr = (tattr &~ ATTR_FGMASK) | ATTR_DEFFG;
  5681. colour = (tattr & ATTR_BGMASK) >> ATTR_BGSHIFT;
  5682. if (colour >= 16 && colour < 256)
  5683. tattr = (tattr &~ ATTR_BGMASK) | ATTR_DEFBG;
  5684. }
  5685. if (term->true_colour) {
  5686. tc = d->truecolour;
  5687. } else {
  5688. tc.fg = tc.bg = optionalrgb_none;
  5689. }
  5690. switch (tchar & CSET_MASK) {
  5691. case CSET_ASCII:
  5692. tchar = term->ucsdata->unitab_line[tchar & 0xFF];
  5693. break;
  5694. case CSET_LINEDRW:
  5695. tchar = term->ucsdata->unitab_xterm[tchar & 0xFF];
  5696. break;
  5697. case CSET_SCOACS:
  5698. tchar = term->ucsdata->unitab_scoacs[tchar&0xFF];
  5699. break;
  5700. }
  5701. if (j < term->cols-1 && d[1].chr == UCSWIDE)
  5702. tattr |= ATTR_WIDE;
  5703. /* Video reversing things */
  5704. if (term->selstate == DRAGGING || term->selstate == SELECTED) {
  5705. if (term->seltype == LEXICOGRAPHIC)
  5706. selected = (posle(term->selstart, scrpos) &&
  5707. poslt(scrpos, term->selend));
  5708. else
  5709. selected = (posPle(term->selstart, scrpos) &&
  5710. posPle_left(scrpos, term->selend));
  5711. } else
  5712. selected = false;
  5713. tattr = (tattr ^ rv
  5714. ^ (selected ? ATTR_REVERSE : 0));
  5715. /* 'Real' blinking ? */
  5716. if (term->blink_is_real && (tattr & ATTR_BLINK)) {
  5717. if (term->has_focus && term->tblinker) {
  5718. tchar = term->ucsdata->unitab_line[(unsigned char)' '];
  5719. }
  5720. tattr &= ~ATTR_BLINK;
  5721. }
  5722. /*
  5723. * Check the font we'll _probably_ be using to see if
  5724. * the character is wide when we don't want it to be.
  5725. */
  5726. if (tchar != term->disptext[i]->chars[j].chr ||
  5727. tattr != (term->disptext[i]->chars[j].attr &~
  5728. (ATTR_NARROW | DATTR_MASK))) {
  5729. if ((tattr & ATTR_WIDE) == 0 &&
  5730. win_char_width(term->win, tchar) == 2)
  5731. tattr |= ATTR_NARROW;
  5732. } else if (term->disptext[i]->chars[j].attr & ATTR_NARROW)
  5733. tattr |= ATTR_NARROW;
  5734. if (i == our_curs_y && j == our_curs_x) {
  5735. tattr |= cursor;
  5736. term->curstype = cursor;
  5737. term->dispcursx = j;
  5738. term->dispcursy = i;
  5739. }
  5740. /* FULL-TERMCHAR */
  5741. newline[j].attr = tattr;
  5742. newline[j].chr = tchar;
  5743. newline[j].truecolour = tc;
  5744. /* Combining characters are still read from lchars */
  5745. newline[j].cc_next = 0;
  5746. }
  5747. /*
  5748. * Now loop over the line again, noting where things have
  5749. * changed.
  5750. *
  5751. * During this loop, we keep track of where we last saw
  5752. * DATTR_STARTRUN. Any mismatch automatically invalidates
  5753. * _all_ of the containing run that was last printed: that
  5754. * is, any rectangle that was drawn in one go in the
  5755. * previous update should be either left completely alone
  5756. * or overwritten in its entirety. This, along with the
  5757. * expectation that front ends clip all text runs to their
  5758. * bounding rectangle, should solve any possible problems
  5759. * with fonts that overflow their character cells.
  5760. */
  5761. laststart = 0;
  5762. dirtyrect = false;
  5763. for (j = 0; j < term->cols; j++) {
  5764. if (term->disptext[i]->chars[j].attr & DATTR_STARTRUN) {
  5765. laststart = j;
  5766. dirtyrect = false;
  5767. }
  5768. if (term->disptext[i]->chars[j].chr != newline[j].chr ||
  5769. (term->disptext[i]->chars[j].attr &~ DATTR_MASK)
  5770. != newline[j].attr) {
  5771. int k;
  5772. if (!dirtyrect) {
  5773. for (k = laststart; k < j; k++)
  5774. term->disptext[i]->chars[k].attr |= ATTR_INVALID;
  5775. dirtyrect = true;
  5776. }
  5777. }
  5778. if (dirtyrect)
  5779. term->disptext[i]->chars[j].attr |= ATTR_INVALID;
  5780. }
  5781. /*
  5782. * Finally, loop once more and actually do the drawing.
  5783. */
  5784. dirty_run = dirty_line = (ldata->lattr !=
  5785. term->disptext[i]->lattr);
  5786. term->disptext[i]->lattr = ldata->lattr;
  5787. tc = term->erase_char.truecolour;
  5788. for (j = 0; j < term->cols; j++) {
  5789. unsigned long tattr, tchar;
  5790. bool break_run, do_copy, next_run_dirty = false;
  5791. termchar *d = lchars + j;
  5792. tattr = newline[j].attr;
  5793. tchar = newline[j].chr;
  5794. if ((term->disptext[i]->chars[j].attr ^ tattr) & ATTR_WIDE)
  5795. dirty_line = true;
  5796. break_run = ((tattr ^ attr) & term->attr_mask) != 0;
  5797. if (!truecolour_equal(newline[j].truecolour, tc))
  5798. break_run = true;
  5799. #ifdef USES_VTLINE_HACK
  5800. /* Special hack for VT100 Linedraw glyphs */
  5801. if ((tchar >= 0x23BA && tchar <= 0x23BD) ||
  5802. (j > 0 && (newline[j-1].chr >= 0x23BA &&
  5803. newline[j-1].chr <= 0x23BD)))
  5804. break_run = true;
  5805. #endif
  5806. /*
  5807. * Separate out sequences of characters that have the
  5808. * same CSET, if that CSET is a magic one.
  5809. */
  5810. if (CSET_OF(tchar) != cset)
  5811. break_run = true;
  5812. /*
  5813. * Break on both sides of any combined-character cell.
  5814. */
  5815. if (d->cc_next != 0 ||
  5816. (j > 0 && d[-1].cc_next != 0))
  5817. break_run = true;
  5818. /*
  5819. * Break on both sides of a regional indicator letter.
  5820. */
  5821. if (IS_REGIONAL_INDICATOR_LETTER(tchar)) {
  5822. break_run = true;
  5823. if (j+1 < term->cols) {
  5824. /* Also, check if there are any changes to whether or
  5825. * not we're drawing this and the next character as a
  5826. * single flag glyph. */
  5827. bool flag_now = IS_REGIONAL_INDICATOR_LETTER(d[1].chr);
  5828. bool flag_before = (
  5829. IS_REGIONAL_INDICATOR_LETTER(
  5830. term->disptext[i]->chars[j].chr) &&
  5831. IS_REGIONAL_INDICATOR_LETTER(
  5832. term->disptext[i]->chars[j+1].chr) &&
  5833. (term->disptext[i]->chars[j].attr & DATTR_STARTRUN));
  5834. if (flag_now != flag_before)
  5835. next_run_dirty = true; /* must redraw this flag */
  5836. }
  5837. } else if (j>0 && IS_REGIONAL_INDICATOR_LETTER(d[-1].chr)) {
  5838. break_run = true;
  5839. }
  5840. /*
  5841. * Break on both sides of a trust sigil.
  5842. */
  5843. if (d->chr == TRUST_SIGIL_CHAR ||
  5844. (j >= 2 && d[-1].chr == UCSWIDE &&
  5845. d[-2].chr == TRUST_SIGIL_CHAR))
  5846. break_run = true;
  5847. if (!term->ucsdata->dbcs_screenfont && !dirty_line) {
  5848. if (term->disptext[i]->chars[j].chr == tchar &&
  5849. (term->disptext[i]->chars[j].attr &~ DATTR_MASK)==tattr &&
  5850. truecolour_equal(
  5851. term->disptext[i]->chars[j].truecolour, tc))
  5852. break_run = true;
  5853. else if (!dirty_run && ccount == 1)
  5854. break_run = true;
  5855. }
  5856. if (break_run) {
  5857. if ((dirty_run || last_run_dirty) && ccount > 0)
  5858. do_paint_draw(term, ldata, start, i, ch, ccount, attr, tc);
  5859. start = j;
  5860. ccount = 0;
  5861. attr = tattr;
  5862. tc = newline[j].truecolour;
  5863. cset = CSET_OF(tchar);
  5864. if (term->ucsdata->dbcs_screenfont)
  5865. last_run_dirty = dirty_run;
  5866. dirty_run = dirty_line || next_run_dirty;
  5867. }
  5868. do_copy = false;
  5869. if (!termchars_equal_override(&term->disptext[i]->chars[j],
  5870. d, tchar, tattr)) {
  5871. do_copy = true;
  5872. dirty_run = true;
  5873. }
  5874. sgrowarrayn(ch, chlen, ccount, 2);
  5875. #ifdef PLATFORM_IS_UTF16
  5876. if (tchar > 0x10000 && tchar < 0x110000) {
  5877. ch[ccount++] = (wchar_t) HIGH_SURROGATE_OF(tchar);
  5878. ch[ccount++] = (wchar_t) LOW_SURROGATE_OF(tchar);
  5879. } else
  5880. #endif /* PLATFORM_IS_UTF16 */
  5881. ch[ccount++] = (wchar_t) tchar;
  5882. if (d->cc_next) {
  5883. termchar *dd = d;
  5884. while (dd->cc_next) {
  5885. unsigned long schar;
  5886. dd += dd->cc_next;
  5887. schar = dd->chr;
  5888. switch (schar & CSET_MASK) {
  5889. case CSET_ASCII:
  5890. schar = term->ucsdata->unitab_line[schar & 0xFF];
  5891. break;
  5892. case CSET_LINEDRW:
  5893. schar = term->ucsdata->unitab_xterm[schar & 0xFF];
  5894. break;
  5895. case CSET_SCOACS:
  5896. schar = term->ucsdata->unitab_scoacs[schar&0xFF];
  5897. break;
  5898. }
  5899. sgrowarrayn(ch, chlen, ccount, 2);
  5900. #ifdef PLATFORM_IS_UTF16
  5901. if (schar > 0x10000 && schar < 0x110000) {
  5902. ch[ccount++] = (wchar_t) HIGH_SURROGATE_OF(schar);
  5903. ch[ccount++] = (wchar_t) LOW_SURROGATE_OF(schar);
  5904. } else
  5905. #endif /* PLATFORM_IS_UTF16 */
  5906. ch[ccount++] = (wchar_t) schar;
  5907. }
  5908. attr |= TATTR_COMBINING;
  5909. }
  5910. if (do_copy) {
  5911. copy_termchar(term->disptext[i], j, d);
  5912. term->disptext[i]->chars[j].chr = tchar;
  5913. term->disptext[i]->chars[j].attr = tattr;
  5914. term->disptext[i]->chars[j].truecolour = tc;
  5915. if (start == j)
  5916. term->disptext[i]->chars[j].attr |= DATTR_STARTRUN;
  5917. }
  5918. /* If it's a wide char step along to the next one. */
  5919. if (tattr & ATTR_WIDE) {
  5920. if (++j < term->cols) {
  5921. d++;
  5922. /*
  5923. * By construction above, the cursor should not
  5924. * be on the right-hand half of this character.
  5925. * Ever.
  5926. */
  5927. assert(!(i == our_curs_y && j == our_curs_x));
  5928. if (!termchars_equal(&term->disptext[i]->chars[j], d))
  5929. dirty_run = true;
  5930. copy_termchar(term->disptext[i], j, d);
  5931. }
  5932. }
  5933. /* If it's a regional indicator letter, and so is the next
  5934. * one, then also step to the next one, keeping the flag
  5935. * sequence together. */
  5936. if (IS_REGIONAL_INDICATOR_LETTER(d->chr) &&
  5937. (j+1 < term->cols && IS_REGIONAL_INDICATOR_LETTER(d[1].chr))) {
  5938. j++;
  5939. d++;
  5940. /* Set ATTR_WIDE, so that the pair is displayed as one */
  5941. attr |= ATTR_WIDE;
  5942. /* Include the second letter in the text buffer */
  5943. unsigned long rchar = d->chr;
  5944. #ifdef PLATFORM_IS_UTF16
  5945. sgrowarrayn(ch, chlen, ccount, 2);
  5946. ch[ccount++] = (wchar_t)HIGH_SURROGATE_OF(rchar);
  5947. ch[ccount++] = (wchar_t)LOW_SURROGATE_OF(rchar);
  5948. #else
  5949. sgrowarrayn(ch, chlen, ccount, 1);
  5950. ch[ccount++] = (wchar_t)rchar;
  5951. #endif
  5952. /* Display the cursor, if it's on the right half */
  5953. if (i == our_curs_y && j == our_curs_x) {
  5954. attr |= cursor;
  5955. term->disptext[i]->chars[j-1].attr |= cursor;
  5956. }
  5957. if (!termchars_equal_override(
  5958. &term->disptext[i]->chars[j],
  5959. d, rchar, term->disptext[i]->chars[j-1].attr))
  5960. dirty_run = true;
  5961. copy_termchar(term->disptext[i], j, d);
  5962. term->disptext[i]->chars[j].attr =
  5963. term->disptext[i]->chars[j-1].attr & ~DATTR_STARTRUN;
  5964. }
  5965. }
  5966. if (dirty_run && ccount > 0)
  5967. do_paint_draw(term, ldata, start, i, ch, ccount, attr, tc);
  5968. unlineptr(ldata);
  5969. }
  5970. sfree(newline);
  5971. sfree(ch);
  5972. }
  5973. /*
  5974. * Invalidate the whole screen so it will be repainted in full.
  5975. */
  5976. void term_invalidate(Terminal *term)
  5977. {
  5978. int i, j;
  5979. for (i = 0; i < term->rows; i++)
  5980. for (j = 0; j < term->cols; j++)
  5981. term->disptext[i]->chars[j].attr |= ATTR_INVALID;
  5982. term_schedule_update(term);
  5983. }
  5984. /*
  5985. * Paint the window in response to a WM_PAINT message.
  5986. */
  5987. void term_paint(Terminal *term,
  5988. int left, int top, int right, int bottom, bool immediately)
  5989. {
  5990. int i, j;
  5991. if (left < 0) left = 0;
  5992. if (top < 0) top = 0;
  5993. if (right >= term->cols) right = term->cols-1;
  5994. if (bottom >= term->rows) bottom = term->rows-1;
  5995. for (i = top; i <= bottom && i < term->rows; i++) {
  5996. if ((term->disptext[i]->lattr & LATTR_MODE) == LATTR_NORM)
  5997. for (j = left; j <= right && j < term->cols; j++)
  5998. term->disptext[i]->chars[j].attr |= ATTR_INVALID;
  5999. else
  6000. for (j = left / 2; j <= right / 2 + 1 && j < term->cols; j++)
  6001. term->disptext[i]->chars[j].attr |= ATTR_INVALID;
  6002. }
  6003. if (immediately) {
  6004. do_paint(term);
  6005. } else {
  6006. term_schedule_update(term);
  6007. }
  6008. }
  6009. /*
  6010. * Attempt to scroll the scrollback. The second parameter gives the
  6011. * position we want to scroll to; the first is +1 to denote that
  6012. * this position is relative to the beginning of the scrollback, -1
  6013. * to denote it is relative to the end, and 0 to denote that it is
  6014. * relative to the current position.
  6015. */
  6016. void term_scroll(Terminal *term, int rel, int where)
  6017. {
  6018. int sbtop = -sblines(term);
  6019. term->disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : term->disptop) + where;
  6020. if (term->disptop < sbtop)
  6021. term->disptop = sbtop;
  6022. if (term->disptop > 0)
  6023. term->disptop = 0;
  6024. term->win_scrollbar_update_pending = true;
  6025. term_schedule_update(term);
  6026. }
  6027. /*
  6028. * Scroll the scrollback to centre it on the beginning or end of the
  6029. * current selection, if any.
  6030. */
  6031. void term_scroll_to_selection(Terminal *term, int which_end)
  6032. {
  6033. pos target;
  6034. int y;
  6035. int sbtop = -sblines(term);
  6036. if (term->selstate != SELECTED)
  6037. return;
  6038. if (which_end)
  6039. target = term->selend;
  6040. else
  6041. target = term->selstart;
  6042. y = target.y - term->rows/2;
  6043. if (y < sbtop)
  6044. y = sbtop;
  6045. else if (y > 0)
  6046. y = 0;
  6047. term_scroll(term, -1, y);
  6048. }
  6049. /*
  6050. * Helper routine for clipme(): growing buffer.
  6051. */
  6052. typedef struct {
  6053. size_t bufsize; /* amount of allocated space in textbuf/attrbuf */
  6054. size_t bufpos; /* amount of actual data */
  6055. wchar_t *textbuf; /* buffer for copied text */
  6056. wchar_t *textptr; /* = textbuf + bufpos (current insertion point) */
  6057. int *attrbuf; /* buffer for copied attributes */
  6058. int *attrptr; /* = attrbuf + bufpos */
  6059. truecolour *tcbuf; /* buffer for copied colours */
  6060. truecolour *tcptr; /* = tcbuf + bufpos */
  6061. } clip_workbuf;
  6062. static void clip_addchar(clip_workbuf *b, wchar_t chr, int attr, truecolour tc)
  6063. {
  6064. if (b->bufpos >= b->bufsize) {
  6065. sgrowarray(b->textbuf, b->bufsize, b->bufpos);
  6066. b->textptr = b->textbuf + b->bufpos;
  6067. b->attrbuf = sresize(b->attrbuf, b->bufsize, int);
  6068. b->attrptr = b->attrbuf + b->bufpos;
  6069. b->tcbuf = sresize(b->tcbuf, b->bufsize, truecolour);
  6070. b->tcptr = b->tcbuf + b->bufpos;
  6071. }
  6072. *b->textptr++ = chr;
  6073. *b->attrptr++ = attr;
  6074. *b->tcptr++ = tc;
  6075. b->bufpos++;
  6076. }
  6077. static void clipme(Terminal *term, pos top, pos bottom, bool rect, bool desel,
  6078. const int *clipboards, int n_clipboards)
  6079. {
  6080. clip_workbuf buf;
  6081. int old_top_x;
  6082. int attr;
  6083. truecolour tc;
  6084. buf.bufsize = 5120;
  6085. buf.bufpos = 0;
  6086. buf.textptr = buf.textbuf = snewn(buf.bufsize, wchar_t);
  6087. buf.attrptr = buf.attrbuf = snewn(buf.bufsize, int);
  6088. buf.tcptr = buf.tcbuf = snewn(buf.bufsize, truecolour);
  6089. old_top_x = top.x; /* needed for rect==1 */
  6090. while (poslt(top, bottom)) {
  6091. bool nl = false;
  6092. termline *ldata = lineptr(top.y);
  6093. pos nlpos;
  6094. /*
  6095. * nlpos will point at the maximum position on this line we
  6096. * should copy up to. So we start it at the end of the
  6097. * line...
  6098. */
  6099. nlpos.y = top.y;
  6100. nlpos.x = term->cols;
  6101. /*
  6102. * ... move it backwards if there's unused space at the end
  6103. * of the line (and also set `nl' if this is the case,
  6104. * because in normal selection mode this means we need a
  6105. * newline at the end)...
  6106. */
  6107. if (!(ldata->lattr & LATTR_WRAPPED)) {
  6108. while (nlpos.x &&
  6109. IS_SPACE_CHR(ldata->chars[nlpos.x - 1].chr) &&
  6110. !ldata->chars[nlpos.x - 1].cc_next &&
  6111. poslt(top, nlpos))
  6112. decpos(nlpos);
  6113. if (poslt(nlpos, bottom))
  6114. nl = true;
  6115. } else {
  6116. if (ldata->trusted) {
  6117. /* A wrapped line with a trust sigil on it terminates
  6118. * a few characters earlier. */
  6119. nlpos.x = (nlpos.x < TRUST_SIGIL_WIDTH ? 0 :
  6120. nlpos.x - TRUST_SIGIL_WIDTH);
  6121. }
  6122. if (ldata->lattr & LATTR_WRAPPED2) {
  6123. /* Ignore the last char on the line in a WRAPPED2 line. */
  6124. decpos(nlpos);
  6125. }
  6126. }
  6127. /*
  6128. * ... and then clip it to the terminal x coordinate if
  6129. * we're doing rectangular selection. (In this case we
  6130. * still did the above, so that copying e.g. the right-hand
  6131. * column from a table doesn't fill with spaces on the
  6132. * right.)
  6133. */
  6134. if (rect) {
  6135. if (nlpos.x > bottom.x)
  6136. nlpos.x = bottom.x;
  6137. nl = (top.y < bottom.y);
  6138. }
  6139. while (poslt(top, bottom) && poslt(top, nlpos)) {
  6140. wchar_t cbuf[16], *p;
  6141. int c;
  6142. int x = top.x;
  6143. if (ldata->chars[x].chr == UCSWIDE) {
  6144. top.x++;
  6145. continue;
  6146. }
  6147. while (1) {
  6148. int uc = ldata->chars[x].chr;
  6149. attr = ldata->chars[x].attr;
  6150. tc = ldata->chars[x].truecolour;
  6151. switch (uc & CSET_MASK) {
  6152. case CSET_LINEDRW:
  6153. if (!term->rawcnp) {
  6154. uc = term->ucsdata->unitab_xterm[uc & 0xFF];
  6155. break;
  6156. }
  6157. case CSET_ASCII:
  6158. uc = term->ucsdata->unitab_line[uc & 0xFF];
  6159. break;
  6160. case CSET_SCOACS:
  6161. uc = term->ucsdata->unitab_scoacs[uc&0xFF];
  6162. break;
  6163. }
  6164. switch (uc & CSET_MASK) {
  6165. case CSET_ACP:
  6166. uc = term->ucsdata->unitab_font[uc & 0xFF];
  6167. break;
  6168. case CSET_OEMCP:
  6169. uc = term->ucsdata->unitab_oemcp[uc & 0xFF];
  6170. break;
  6171. }
  6172. c = (uc & ~CSET_MASK);
  6173. #ifdef PLATFORM_IS_UTF16
  6174. if (uc > 0x10000 && uc < 0x110000) {
  6175. cbuf[0] = 0xD800 | ((uc - 0x10000) >> 10);
  6176. cbuf[1] = 0xDC00 | ((uc - 0x10000) & 0x3FF);
  6177. cbuf[2] = 0;
  6178. } else
  6179. #endif
  6180. {
  6181. cbuf[0] = uc;
  6182. cbuf[1] = 0;
  6183. }
  6184. if (DIRECT_FONT(uc)) {
  6185. if (c >= ' ' && c != 0x7F) {
  6186. char buf[2];
  6187. buffer_sink bs[1];
  6188. buffer_sink_init(bs, cbuf,
  6189. sizeof(cbuf) - sizeof(wchar_t));
  6190. if (is_dbcs_leadbyte(term->ucsdata->font_codepage, (BYTE) c)) {
  6191. buf[0] = c;
  6192. buf[1] = (char) (0xFF & ldata->chars[top.x + 1].chr);
  6193. put_mb_to_wc(bs, term->ucsdata->font_codepage,
  6194. buf, 2);
  6195. top.x++;
  6196. } else {
  6197. buf[0] = c;
  6198. put_mb_to_wc(bs, term->ucsdata->font_codepage,
  6199. buf, 1);
  6200. }
  6201. assert(!bs->overflowed);
  6202. *(wchar_t *)bs->out = L'\0';
  6203. }
  6204. }
  6205. for (p = cbuf; *p; p++)
  6206. clip_addchar(&buf, *p, attr, tc);
  6207. if (ldata->chars[x].cc_next)
  6208. x += ldata->chars[x].cc_next;
  6209. else
  6210. break;
  6211. }
  6212. top.x++;
  6213. }
  6214. if (nl) {
  6215. int i;
  6216. for (i = 0; i < sel_nl_sz; i++)
  6217. clip_addchar(&buf, sel_nl[i], 0, term->basic_erase_char.truecolour);
  6218. }
  6219. top.y++;
  6220. top.x = rect ? old_top_x : 0;
  6221. unlineptr(ldata);
  6222. }
  6223. #if SELECTION_NUL_TERMINATED
  6224. clip_addchar(&buf, 0, 0, term->basic_erase_char.truecolour);
  6225. #endif
  6226. /* Finally, transfer all that to the clipboard(s). */
  6227. {
  6228. int i;
  6229. bool clip_local = false;
  6230. for (i = 0; i < n_clipboards; i++) {
  6231. if (clipboards[i] == CLIP_LOCAL) {
  6232. clip_local = true;
  6233. } else if (clipboards[i] != CLIP_NULL) {
  6234. win_clip_write(
  6235. term->win, clipboards[i], buf.textbuf, buf.attrbuf,
  6236. buf.tcbuf, buf.bufpos, desel);
  6237. }
  6238. }
  6239. if (clip_local) {
  6240. sfree(term->last_selected_text);
  6241. sfree(term->last_selected_attr);
  6242. sfree(term->last_selected_tc);
  6243. term->last_selected_text = buf.textbuf;
  6244. term->last_selected_attr = buf.attrbuf;
  6245. term->last_selected_tc = buf.tcbuf;
  6246. term->last_selected_len = buf.bufpos;
  6247. } else {
  6248. sfree(buf.textbuf);
  6249. sfree(buf.attrbuf);
  6250. sfree(buf.tcbuf);
  6251. }
  6252. }
  6253. }
  6254. void term_copyall(Terminal *term, const int *clipboards, int n_clipboards)
  6255. {
  6256. pos top;
  6257. pos bottom;
  6258. tree234 *screen = term->screen;
  6259. top.y = -sblines(term);
  6260. top.x = 0;
  6261. bottom.y = find_last_nonempty_line(term, screen);
  6262. bottom.x = term->cols;
  6263. clipme(term, top, bottom, false, true, clipboards, n_clipboards);
  6264. }
  6265. static void paste_from_clip_local(void *vterm)
  6266. {
  6267. Terminal *term = (Terminal *)vterm;
  6268. term_do_paste(term, term->last_selected_text, term->last_selected_len);
  6269. }
  6270. void term_request_copy(Terminal *term, const int *clipboards, int n_clipboards)
  6271. {
  6272. int i;
  6273. for (i = 0; i < n_clipboards; i++) {
  6274. assert(clipboards[i] != CLIP_LOCAL);
  6275. if (clipboards[i] != CLIP_NULL) {
  6276. win_clip_write(term->win, clipboards[i],
  6277. term->last_selected_text, term->last_selected_attr,
  6278. term->last_selected_tc, term->last_selected_len,
  6279. false);
  6280. }
  6281. }
  6282. }
  6283. void term_request_paste(Terminal *term, int clipboard)
  6284. {
  6285. switch (clipboard) {
  6286. case CLIP_NULL:
  6287. /* Do nothing: CLIP_NULL never has data in it. */
  6288. break;
  6289. case CLIP_LOCAL:
  6290. queue_toplevel_callback(paste_from_clip_local, term);
  6291. break;
  6292. default:
  6293. win_clip_request_paste(term->win, clipboard);
  6294. break;
  6295. }
  6296. }
  6297. /*
  6298. * The wordness array is mainly for deciding the disposition of the
  6299. * US-ASCII characters.
  6300. */
  6301. static int wordtype(Terminal *term, int uc)
  6302. {
  6303. struct ucsword {
  6304. int start, end, ctype;
  6305. };
  6306. static const struct ucsword ucs_words[] = {
  6307. {128, 160, 0},
  6308. {161, 191, 1},
  6309. {215, 215, 1},
  6310. {247, 247, 1},
  6311. {0x037e, 0x037e, 1}, /* Greek question mark */
  6312. {0x0387, 0x0387, 1}, /* Greek ano teleia */
  6313. {0x055a, 0x055f, 1}, /* Armenian punctuation */
  6314. {0x0589, 0x0589, 1}, /* Armenian full stop */
  6315. {0x0700, 0x070d, 1}, /* Syriac punctuation */
  6316. {0x104a, 0x104f, 1}, /* Myanmar punctuation */
  6317. {0x10fb, 0x10fb, 1}, /* Georgian punctuation */
  6318. {0x1361, 0x1368, 1}, /* Ethiopic punctuation */
  6319. {0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
  6320. {0x17d4, 0x17dc, 1}, /* Khmer punctuation */
  6321. {0x1800, 0x180a, 1}, /* Mongolian punctuation */
  6322. {0x2000, 0x200a, 0}, /* Various spaces */
  6323. {0x2070, 0x207f, 2}, /* superscript */
  6324. {0x2080, 0x208f, 2}, /* subscript */
  6325. {0x200b, 0x27ff, 1}, /* punctuation and symbols */
  6326. {0x3000, 0x3000, 0}, /* ideographic space */
  6327. {0x3001, 0x3020, 1}, /* ideographic punctuation */
  6328. {0x303f, 0x309f, 3}, /* Hiragana */
  6329. {0x30a0, 0x30ff, 3}, /* Katakana */
  6330. {0x3300, 0x9fff, 3}, /* CJK Ideographs */
  6331. {0xac00, 0xd7a3, 3}, /* Hangul Syllables */
  6332. {0xf900, 0xfaff, 3}, /* CJK Ideographs */
  6333. {0xfe30, 0xfe6b, 1}, /* punctuation forms */
  6334. {0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
  6335. {0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
  6336. {0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
  6337. {0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
  6338. {0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
  6339. {0, 0, 0}
  6340. };
  6341. const struct ucsword *wptr;
  6342. switch (uc & CSET_MASK) {
  6343. case CSET_LINEDRW:
  6344. uc = term->ucsdata->unitab_xterm[uc & 0xFF];
  6345. break;
  6346. case CSET_ASCII:
  6347. uc = term->ucsdata->unitab_line[uc & 0xFF];
  6348. break;
  6349. case CSET_SCOACS:
  6350. uc = term->ucsdata->unitab_scoacs[uc&0xFF];
  6351. break;
  6352. }
  6353. switch (uc & CSET_MASK) {
  6354. case CSET_ACP:
  6355. uc = term->ucsdata->unitab_font[uc & 0xFF];
  6356. break;
  6357. case CSET_OEMCP:
  6358. uc = term->ucsdata->unitab_oemcp[uc & 0xFF];
  6359. break;
  6360. }
  6361. /* For DBCS fonts I can't do anything useful. Even this will sometimes
  6362. * fail as there's such a thing as a double width space. :-(
  6363. */
  6364. if (term->ucsdata->dbcs_screenfont &&
  6365. term->ucsdata->font_codepage == term->ucsdata->line_codepage)
  6366. return (uc != ' ');
  6367. if (uc < 0x80)
  6368. return term->wordness[uc];
  6369. for (wptr = ucs_words; wptr->start; wptr++) {
  6370. if (uc >= wptr->start && uc <= wptr->end)
  6371. return wptr->ctype;
  6372. }
  6373. return 2;
  6374. }
  6375. static int line_cols(Terminal *term, termline *ldata)
  6376. {
  6377. int cols = term->cols;
  6378. if (ldata->trusted) {
  6379. cols -= TRUST_SIGIL_WIDTH;
  6380. }
  6381. if (ldata->lattr & LATTR_WRAPPED2)
  6382. cols--;
  6383. if (cols < 0)
  6384. cols = 0;
  6385. return cols;
  6386. }
  6387. /*
  6388. * Spread the selection outwards according to the selection mode.
  6389. */
  6390. static pos sel_spread_half(Terminal *term, pos p, int dir)
  6391. {
  6392. termline *ldata;
  6393. short wvalue;
  6394. int topy = -sblines(term);
  6395. ldata = lineptr(p.y);
  6396. switch (term->selmode) {
  6397. case SM_CHAR:
  6398. /*
  6399. * In this mode, every character is a separate unit, except
  6400. * for runs of spaces at the end of a non-wrapping line.
  6401. */
  6402. if (!(ldata->lattr & LATTR_WRAPPED)) {
  6403. termchar *q = ldata->chars + line_cols(term, ldata);
  6404. while (q > ldata->chars &&
  6405. IS_SPACE_CHR(q[-1].chr) && !q[-1].cc_next)
  6406. q--;
  6407. if (q == ldata->chars + term->cols)
  6408. q--;
  6409. if (p.x >= q - ldata->chars)
  6410. p.x = (dir == -1 ? q - ldata->chars : term->cols - 1);
  6411. }
  6412. break;
  6413. case SM_WORD:
  6414. /*
  6415. * In this mode, the units are maximal runs of characters
  6416. * whose `wordness' has the same value.
  6417. */
  6418. wvalue = wordtype(term, UCSGET(ldata->chars, p.x));
  6419. if (dir == +1) {
  6420. while (1) {
  6421. int maxcols = line_cols(term, ldata);
  6422. if (p.x < maxcols-1) {
  6423. if (wordtype(term, UCSGET(ldata->chars, p.x+1)) == wvalue)
  6424. p.x++;
  6425. else
  6426. break;
  6427. } else {
  6428. if (p.y+1 < term->rows &&
  6429. (ldata->lattr & LATTR_WRAPPED)) {
  6430. termline *ldata2;
  6431. ldata2 = lineptr(p.y+1);
  6432. if (wordtype(term, UCSGET(ldata2->chars, 0))
  6433. == wvalue) {
  6434. p.x = 0;
  6435. p.y++;
  6436. unlineptr(ldata);
  6437. ldata = ldata2;
  6438. } else {
  6439. unlineptr(ldata2);
  6440. break;
  6441. }
  6442. } else
  6443. break;
  6444. }
  6445. }
  6446. } else {
  6447. while (1) {
  6448. if (p.x > 0) {
  6449. if (wordtype(term, UCSGET(ldata->chars, p.x-1)) == wvalue)
  6450. p.x--;
  6451. else
  6452. break;
  6453. } else {
  6454. termline *ldata2;
  6455. int maxcols;
  6456. if (p.y <= topy)
  6457. break;
  6458. ldata2 = lineptr(p.y-1);
  6459. maxcols = line_cols(term, ldata2);
  6460. if (ldata2->lattr & LATTR_WRAPPED) {
  6461. if (wordtype(term, UCSGET(ldata2->chars, maxcols-1))
  6462. == wvalue) {
  6463. p.x = maxcols-1;
  6464. p.y--;
  6465. unlineptr(ldata);
  6466. ldata = ldata2;
  6467. } else {
  6468. unlineptr(ldata2);
  6469. break;
  6470. }
  6471. } else
  6472. break;
  6473. }
  6474. }
  6475. }
  6476. break;
  6477. case SM_LINE:
  6478. /*
  6479. * In this mode, every line is a unit.
  6480. */
  6481. p.x = (dir == -1 ? 0 : term->cols - 1);
  6482. break;
  6483. }
  6484. unlineptr(ldata);
  6485. return p;
  6486. }
  6487. static void sel_spread(Terminal *term)
  6488. {
  6489. if (term->seltype == LEXICOGRAPHIC) {
  6490. term->selstart = sel_spread_half(term, term->selstart, -1);
  6491. decpos(term->selend);
  6492. term->selend = sel_spread_half(term, term->selend, +1);
  6493. incpos(term->selend);
  6494. }
  6495. }
  6496. static void term_paste_callback(void *vterm)
  6497. {
  6498. Terminal *term = (Terminal *)vterm;
  6499. if (term->paste_len == 0)
  6500. return;
  6501. while (term->paste_pos < term->paste_len) {
  6502. size_t n = 0;
  6503. while (n + term->paste_pos < term->paste_len) {
  6504. if (term->paste_buffer[term->paste_pos + n++] == '\015')
  6505. break;
  6506. }
  6507. if (term->ldisc) {
  6508. strbuf *buf = term_input_data_from_unicode(
  6509. term, term->paste_buffer + term->paste_pos, n);
  6510. term_keyinput_internal(term, buf->s, buf->len, false);
  6511. strbuf_free(buf);
  6512. }
  6513. term->paste_pos += n;
  6514. if (term->paste_pos < term->paste_len) {
  6515. queue_toplevel_callback(term_paste_callback, term);
  6516. return;
  6517. }
  6518. }
  6519. term_bracketed_paste_stop(term);
  6520. sfree(term->paste_buffer);
  6521. term->paste_buffer = NULL;
  6522. term->paste_len = 0;
  6523. }
  6524. /*
  6525. * Specialist string compare function. Returns true if the buffer of
  6526. * alen wide characters starting at a has as a prefix the buffer of
  6527. * blen characters starting at b.
  6528. */
  6529. static bool wstartswith(const wchar_t *a, size_t alen,
  6530. const wchar_t *b, size_t blen)
  6531. {
  6532. return alen >= blen && !wcsncmp(a, b, blen);
  6533. }
  6534. void term_do_paste(Terminal *term, const wchar_t *data, size_t len)
  6535. {
  6536. const wchar_t *p;
  6537. bool paste_controls = conf_get_bool(term->conf, CONF_paste_controls);
  6538. /*
  6539. * Pasting data into the terminal counts as a keyboard event (for
  6540. * purposes of the 'Reset scrollback on keypress' config option),
  6541. * unless the paste is zero-length.
  6542. */
  6543. if (len == 0)
  6544. return;
  6545. term_seen_key_event(term);
  6546. if (term->paste_buffer)
  6547. sfree(term->paste_buffer);
  6548. term->paste_pos = term->paste_len = 0;
  6549. term->paste_buffer = snewn(len + 12, wchar_t);
  6550. if (term->bracketed_paste && !term->no_bracketed_paste)
  6551. term_bracketed_paste_start(term);
  6552. p = data;
  6553. while (p < data + len) {
  6554. wchar_t wc = *p++;
  6555. if (wc == sel_nl[0] &&
  6556. wstartswith(p-1, data+len-(p-1), sel_nl, sel_nl_sz)) {
  6557. /*
  6558. * This is the (platform-dependent) sequence that the host
  6559. * OS uses to represent newlines in clipboard data.
  6560. * Normalise it to a press of CR.
  6561. */
  6562. p += sel_nl_sz - 1;
  6563. wc = '\015';
  6564. }
  6565. if ((wc & ~(wint_t)0x9F) == 0) {
  6566. /*
  6567. * This is a control code, either in the range 0x00-0x1F
  6568. * or 0x80-0x9F. We reject all of these in pastecontrols
  6569. * mode, except for a small set of permitted ones.
  6570. */
  6571. if (!paste_controls) {
  6572. /* In line with xterm 292, accepted control chars are:
  6573. * CR, LF, tab, backspace. (And DEL, i.e. 0x7F, but
  6574. * that's permitted by virtue of not matching the bit
  6575. * mask that got us into this if statement, so we
  6576. * don't have to permit it here. */
  6577. static const unsigned mask =
  6578. (1<<13) | (1<<10) | (1<<9) | (1<<8);
  6579. if (wc > 15 || !((mask >> wc) & 1))
  6580. continue;
  6581. }
  6582. if (wc == '\033' && term->bracketed_paste &&
  6583. wstartswith(p-1, data+len-(p-1), L"\033[201~", 6)) {
  6584. /*
  6585. * Also, in bracketed-paste mode, reject the ESC
  6586. * character that begins the end-of-paste sequence.
  6587. */
  6588. continue;
  6589. }
  6590. }
  6591. term->paste_buffer[term->paste_len++] = wc;
  6592. }
  6593. /* Assume a small paste will be OK in one go. */
  6594. if (term->paste_len < 256) {
  6595. if (term->ldisc) {
  6596. strbuf *buf = term_input_data_from_unicode(
  6597. term, term->paste_buffer, term->paste_len);
  6598. assert(buf->len <= INT_MAX); /* because paste_len was also small */
  6599. term_keyinput_internal(term, buf->s, buf->len, false);
  6600. strbuf_free(buf);
  6601. }
  6602. if (term->paste_buffer)
  6603. sfree(term->paste_buffer);
  6604. term_bracketed_paste_stop(term);
  6605. term->paste_buffer = NULL;
  6606. term->paste_pos = term->paste_len = 0;
  6607. }
  6608. queue_toplevel_callback(term_paste_callback, term);
  6609. }
  6610. void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked,
  6611. Mouse_Action a, int x, int y, bool shift, bool ctrl, bool alt)
  6612. {
  6613. pos selpoint;
  6614. termline *ldata;
  6615. bool raw_mouse = (term->xterm_mouse &&
  6616. !term->no_mouse_rep &&
  6617. !(term->mouse_override && shift));
  6618. int default_seltype;
  6619. // Don't do anything if mouse movement events weren't requested;
  6620. // Note: return early to avoid doing all of this code on every mouse move
  6621. // event only to throw it away.
  6622. if (a == MA_MOVE && (!raw_mouse || term->xterm_mouse < 3)) {
  6623. return;
  6624. }
  6625. if (y < 0) {
  6626. y = 0;
  6627. if (a == MA_DRAG && !raw_mouse)
  6628. term_scroll(term, 0, -1);
  6629. }
  6630. if (y >= term->rows) {
  6631. y = term->rows - 1;
  6632. if (a == MA_DRAG && !raw_mouse)
  6633. term_scroll(term, 0, +1);
  6634. }
  6635. if (x < 0) {
  6636. if (y > 0 && !raw_mouse && term->seltype != RECTANGULAR) {
  6637. /*
  6638. * When we're using the mouse for normal raster-based
  6639. * selection, dragging off the left edge of a terminal row
  6640. * is treated the same as the right-hand end of the
  6641. * previous row, in that it's considered to identify a
  6642. * point _before_ the first character on row y.
  6643. *
  6644. * But if the mouse action is going to be used for
  6645. * anything else - rectangular selection, or xterm mouse
  6646. * tracking - then we disable this special treatment.
  6647. */
  6648. x = term->cols - 1;
  6649. y--;
  6650. } else
  6651. x = 0;
  6652. }
  6653. if (x >= term->cols)
  6654. x = term->cols - 1;
  6655. selpoint.y = y + term->disptop;
  6656. ldata = lineptr(selpoint.y);
  6657. if ((ldata->lattr & LATTR_MODE) != LATTR_NORM)
  6658. x /= 2;
  6659. /*
  6660. * Transform x through the bidi algorithm to find the _logical_
  6661. * click point from the physical one.
  6662. */
  6663. if (term_bidi_line(term, ldata, y) != NULL) {
  6664. x = term->post_bidi_cache[y].backward[x];
  6665. }
  6666. selpoint.x = x;
  6667. unlineptr(ldata);
  6668. /*
  6669. * If we're in the middle of a selection operation, we ignore raw
  6670. * mouse mode until it's done (we must have been not in raw mouse
  6671. * mode when it started).
  6672. * This makes use of Shift for selection reliable, and avoids the
  6673. * host seeing mouse releases for which they never saw corresponding
  6674. * presses.
  6675. */
  6676. if (raw_mouse &&
  6677. (term->selstate != ABOUT_TO) && (term->selstate != DRAGGING)) {
  6678. int encstate = 0, r, c;
  6679. bool wheel;
  6680. char *response = NULL;
  6681. if (term->ldisc) {
  6682. switch (braw) {
  6683. case MBT_LEFT:
  6684. encstate = 0x00; /* left button down */
  6685. wheel = false;
  6686. break;
  6687. case MBT_MIDDLE:
  6688. encstate = 0x01;
  6689. wheel = false;
  6690. break;
  6691. case MBT_RIGHT:
  6692. encstate = 0x02;
  6693. wheel = false;
  6694. break;
  6695. case MBT_WHEEL_UP:
  6696. encstate = 0x40;
  6697. wheel = true;
  6698. break;
  6699. case MBT_WHEEL_DOWN:
  6700. encstate = 0x41;
  6701. wheel = true;
  6702. break;
  6703. case MBT_WHEEL_LEFT:
  6704. encstate = 0x42;
  6705. wheel = true;
  6706. break;
  6707. case MBT_WHEEL_RIGHT:
  6708. encstate = 0x43;
  6709. wheel = true;
  6710. break;
  6711. case MBT_NOTHING:
  6712. assert( a == MA_MOVE );
  6713. encstate = 0x03; // release; no buttons pressed
  6714. wheel = false;
  6715. break;
  6716. default:
  6717. return;
  6718. }
  6719. if (wheel) {
  6720. /* For mouse wheel buttons, we only ever expect to see
  6721. * MA_CLICK actions, and we don't try to keep track of
  6722. * the buttons being 'pressed' (since without matching
  6723. * click/release pairs that's pointless). */
  6724. if (a != MA_CLICK)
  6725. return;
  6726. } else switch (a) {
  6727. case MA_DRAG:
  6728. if (term->xterm_mouse == 1)
  6729. return;
  6730. encstate += 0x20; // motion indicator
  6731. break;
  6732. case MA_MOVE: // mouse move without buttons
  6733. assert( braw == MBT_NOTHING && bcooked == MBT_NOTHING );
  6734. if (term->xterm_mouse < 3)
  6735. return;
  6736. if (selpoint.x == term->raw_mouse_reported_x &&
  6737. selpoint.y == term->raw_mouse_reported_y)
  6738. return;
  6739. term->raw_mouse_reported_x = x;
  6740. term->raw_mouse_reported_y = y;
  6741. encstate += 0x20; // motion indicator
  6742. break;
  6743. case MA_RELEASE:
  6744. /* If multiple extensions are enabled, the xterm 1006 is used, so it's okay to check for only that */
  6745. if (!term->xterm_extended_mouse)
  6746. encstate = 0x03;
  6747. term->mouse_is_down = 0;
  6748. break;
  6749. case MA_CLICK:
  6750. if (term->mouse_is_down == braw)
  6751. return;
  6752. term->mouse_is_down = braw;
  6753. break;
  6754. default:
  6755. return;
  6756. }
  6757. if (shift)
  6758. encstate += 0x04;
  6759. if (ctrl)
  6760. encstate += 0x10;
  6761. r = y + 1;
  6762. c = x + 1;
  6763. /* Check the extensions in decreasing order of preference. Encoding the release event above assumes that 1006 comes first. */
  6764. if (term->xterm_extended_mouse) {
  6765. response = dupprintf("\033[<%d;%d;%d%c", encstate, c, r,
  6766. a == MA_RELEASE ? 'm' : 'M');
  6767. } else if (term->urxvt_extended_mouse) {
  6768. response = dupprintf("\033[%d;%d;%dM", encstate + 32, c, r);
  6769. } else if (c <= 223 && r <= 223) {
  6770. response = dupprintf("\033[M%c%c%c", encstate + 32,
  6771. c + 32, r + 32);
  6772. }
  6773. if (response) {
  6774. ldisc_send(term->ldisc, response, strlen(response), false);
  6775. sfree(response);
  6776. }
  6777. }
  6778. return;
  6779. }
  6780. /*
  6781. * Set the selection type (rectangular or normal) at the start
  6782. * of a selection attempt, from the state of Alt.
  6783. */
  6784. if (!alt ^ !term->rect_select)
  6785. default_seltype = RECTANGULAR;
  6786. else
  6787. default_seltype = LEXICOGRAPHIC;
  6788. if (term->selstate == NO_SELECTION) {
  6789. term->seltype = default_seltype;
  6790. }
  6791. if (bcooked == MBT_SELECT && a == MA_CLICK) {
  6792. deselect(term);
  6793. term->selstate = ABOUT_TO;
  6794. term->seltype = default_seltype;
  6795. term->selanchor = selpoint;
  6796. term->selmode = SM_CHAR;
  6797. } else if (bcooked == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
  6798. deselect(term);
  6799. term->selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
  6800. term->selstate = DRAGGING;
  6801. term->selstart = term->selanchor = selpoint;
  6802. term->selend = term->selstart;
  6803. incpos(term->selend);
  6804. sel_spread(term);
  6805. } else if ((bcooked == MBT_SELECT && a == MA_DRAG) ||
  6806. (bcooked == MBT_EXTEND && a != MA_RELEASE)) {
  6807. if (a == MA_DRAG &&
  6808. (term->selstate == NO_SELECTION || term->selstate == SELECTED)) {
  6809. /*
  6810. * This can happen if a front end has passed us a MA_DRAG
  6811. * without a prior MA_CLICK. OS X GTK does so, for
  6812. * example, if the initial button press was eaten by the
  6813. * WM when it activated the window in the first place. The
  6814. * nicest thing to do in this situation is to ignore
  6815. * further drags, and wait for the user to click in the
  6816. * window again properly if they want to select.
  6817. */
  6818. return;
  6819. }
  6820. if (term->selstate == ABOUT_TO && poseq(term->selanchor, selpoint))
  6821. return;
  6822. if (bcooked == MBT_EXTEND && a != MA_DRAG &&
  6823. term->selstate == SELECTED) {
  6824. if (term->seltype == LEXICOGRAPHIC) {
  6825. /*
  6826. * For normal selection, we extend by moving
  6827. * whichever end of the current selection is closer
  6828. * to the mouse.
  6829. */
  6830. if (posdiff(selpoint, term->selstart) <
  6831. posdiff(term->selend, term->selstart) / 2) {
  6832. term->selanchor = term->selend;
  6833. decpos(term->selanchor);
  6834. } else {
  6835. term->selanchor = term->selstart;
  6836. }
  6837. } else {
  6838. /*
  6839. * For rectangular selection, we have a choice of
  6840. * _four_ places to put selanchor and selpoint: the
  6841. * four corners of the selection.
  6842. */
  6843. if (2*selpoint.x < term->selstart.x + term->selend.x)
  6844. term->selanchor.x = term->selend.x-1;
  6845. else
  6846. term->selanchor.x = term->selstart.x;
  6847. if (2*selpoint.y < term->selstart.y + term->selend.y)
  6848. term->selanchor.y = term->selend.y;
  6849. else
  6850. term->selanchor.y = term->selstart.y;
  6851. }
  6852. term->selstate = DRAGGING;
  6853. }
  6854. if (term->selstate != ABOUT_TO && term->selstate != DRAGGING)
  6855. term->selanchor = selpoint;
  6856. term->selstate = DRAGGING;
  6857. if (term->seltype == LEXICOGRAPHIC) {
  6858. /*
  6859. * For normal selection, we set (selstart,selend) to
  6860. * (selpoint,selanchor) in some order.
  6861. */
  6862. if (poslt(selpoint, term->selanchor)) {
  6863. term->selstart = selpoint;
  6864. term->selend = term->selanchor;
  6865. incpos(term->selend);
  6866. } else {
  6867. term->selstart = term->selanchor;
  6868. term->selend = selpoint;
  6869. incpos(term->selend);
  6870. }
  6871. } else {
  6872. /*
  6873. * For rectangular selection, we may need to
  6874. * interchange x and y coordinates (if the user has
  6875. * dragged in the -x and +y directions, or vice versa).
  6876. */
  6877. term->selstart.x = min(term->selanchor.x, selpoint.x);
  6878. term->selend.x = 1+max(term->selanchor.x, selpoint.x);
  6879. term->selstart.y = min(term->selanchor.y, selpoint.y);
  6880. term->selend.y = max(term->selanchor.y, selpoint.y);
  6881. }
  6882. sel_spread(term);
  6883. } else if ((bcooked == MBT_SELECT || bcooked == MBT_EXTEND) &&
  6884. a == MA_RELEASE) {
  6885. if (term->selstate == DRAGGING) {
  6886. /*
  6887. * We've completed a selection. We now transfer the
  6888. * data to the clipboard.
  6889. */
  6890. clipme(term, term->selstart, term->selend,
  6891. (term->seltype == RECTANGULAR), false,
  6892. term->mouse_select_clipboards,
  6893. term->n_mouse_select_clipboards);
  6894. term->selstate = SELECTED;
  6895. } else
  6896. term->selstate = NO_SELECTION;
  6897. } else if (bcooked == MBT_PASTE
  6898. && (a == MA_CLICK
  6899. #if MULTICLICK_ONLY_EVENT
  6900. || a == MA_2CLK || a == MA_3CLK
  6901. #endif
  6902. )) {
  6903. term_request_paste(term, term->mouse_paste_clipboard);
  6904. }
  6905. /*
  6906. * Since terminal output is suppressed during drag-selects, we
  6907. * should make sure to write any pending output if one has just
  6908. * finished.
  6909. */
  6910. term_out(term, false);
  6911. term_schedule_update(term);
  6912. }
  6913. void term_cancel_selection_drag(Terminal *term)
  6914. {
  6915. /*
  6916. * In unusual circumstances, a mouse drag might be interrupted by
  6917. * something that steals the rest of the mouse gesture. An example
  6918. * is the GTK popup menu appearing. In that situation, we'll never
  6919. * receive the MA_RELEASE that finishes the DRAGGING state, which
  6920. * means terminal output could be suppressed indefinitely. Call
  6921. * this function from the front end in such situations to restore
  6922. * sensibleness.
  6923. */
  6924. if (term->selstate == DRAGGING)
  6925. term->selstate = NO_SELECTION;
  6926. term_out(term, false);
  6927. term_schedule_update(term);
  6928. }
  6929. static int shift_bitmap(bool shift, bool ctrl, bool alt, bool *consumed_alt)
  6930. {
  6931. int bitmap = (shift ? 1 : 0) + (alt ? 2 : 0) + (ctrl ? 4 : 0);
  6932. if (bitmap)
  6933. bitmap++;
  6934. if (alt && consumed_alt)
  6935. *consumed_alt = true;
  6936. return bitmap;
  6937. }
  6938. int format_arrow_key(char *buf, Terminal *term, int xkey,
  6939. bool shift, bool ctrl, bool alt, bool *consumed_alt)
  6940. {
  6941. char *p = buf;
  6942. if (term->vt52_mode)
  6943. p += sprintf(p, "\x1B%c", xkey);
  6944. else {
  6945. bool app_flg = (term->app_cursor_keys && !term->no_applic_c);
  6946. #if 0
  6947. /*
  6948. * RDB: VT100 & VT102 manuals both state the app cursor
  6949. * keys only work if the app keypad is on.
  6950. *
  6951. * SGT: That may well be true, but xterm disagrees and so
  6952. * does at least one application, so I've #if'ed this out
  6953. * and the behaviour is back to PuTTY's original: app
  6954. * cursor and app keypad are independently switchable
  6955. * modes. If anyone complains about _this_ I'll have to
  6956. * put in a configurable option.
  6957. */
  6958. if (!term->app_keypad_keys)
  6959. app_flg = 0;
  6960. #endif
  6961. int bitmap = 0;
  6962. /* Adjustment based on Shift, Ctrl and/or Alt */
  6963. switch (term->sharrow_type) {
  6964. case SHARROW_APPLICATION:
  6965. if (ctrl)
  6966. app_flg = !app_flg;
  6967. break;
  6968. case SHARROW_BITMAP:
  6969. bitmap = shift_bitmap(shift, ctrl, alt, consumed_alt);
  6970. break;
  6971. }
  6972. if (app_flg)
  6973. p += sprintf(p, "\x1BO%c", xkey);
  6974. else if (bitmap)
  6975. p += sprintf(p, "\x1B[1;%d%c", bitmap, xkey);
  6976. else
  6977. p += sprintf(p, "\x1B[%c", xkey);
  6978. }
  6979. return p - buf;
  6980. }
  6981. int format_function_key(char *buf, Terminal *term, int key_number,
  6982. bool shift, bool ctrl, bool alt, bool *consumed_alt)
  6983. {
  6984. char *p = buf;
  6985. static const int key_number_to_tilde_code[] = {
  6986. -1, /* no such key as F0 */
  6987. 11, 12, 13, 14, 15, /*gap*/ 17, 18, 19, 20, 21, /*gap*/
  6988. 23, 24, 25, 26, /*gap*/ 28, 29, /*gap*/ 31, 32, 33, 34,
  6989. };
  6990. assert(key_number > 0);
  6991. assert(key_number < lenof(key_number_to_tilde_code));
  6992. int index = key_number;
  6993. if (term->funky_type != FUNKY_XTERM_216 && term->funky_type != FUNKY_SCO) {
  6994. if (shift && index <= 10) {
  6995. shift = false;
  6996. index += 10;
  6997. }
  6998. }
  6999. int code = key_number_to_tilde_code[index];
  7000. if (term->funky_type == FUNKY_SCO) {
  7001. /* SCO function keys */
  7002. static const char sco_codes[] =
  7003. "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
  7004. index = (key_number >= 1 && key_number <= 12) ? key_number - 1 : 0;
  7005. if (shift) index += 12;
  7006. if (ctrl) index += 24;
  7007. p += sprintf(p, "\x1B[%c", sco_codes[index]);
  7008. } else if ((term->vt52_mode || term->funky_type == FUNKY_VT100P) &&
  7009. code >= 11 && code <= 24) {
  7010. int offt = 0;
  7011. if (code > 15)
  7012. offt++;
  7013. if (code > 21)
  7014. offt++;
  7015. if (term->vt52_mode)
  7016. p += sprintf(p, "\x1B%c", code + 'P' - 11 - offt);
  7017. else
  7018. p += sprintf(p, "\x1BO%c", code + 'P' - 11 - offt);
  7019. } else if (term->funky_type == FUNKY_LINUX && code >= 11 && code <= 15) {
  7020. p += sprintf(p, "\x1B[[%c", code + 'A' - 11);
  7021. } else if ((term->funky_type == FUNKY_XTERM ||
  7022. term->funky_type == FUNKY_XTERM_216) &&
  7023. code >= 11 && code <= 14) {
  7024. if (term->vt52_mode)
  7025. p += sprintf(p, "\x1B%c", code + 'P' - 11);
  7026. else {
  7027. int bitmap = 0;
  7028. if (term->funky_type == FUNKY_XTERM_216)
  7029. bitmap = shift_bitmap(shift, ctrl, alt, consumed_alt);
  7030. if (bitmap)
  7031. p += sprintf(p, "\x1B[1;%d%c", bitmap, code + 'P' - 11);
  7032. else
  7033. p += sprintf(p, "\x1BO%c", code + 'P' - 11);
  7034. }
  7035. } else {
  7036. int bitmap = 0;
  7037. if (term->funky_type == FUNKY_XTERM_216)
  7038. bitmap = shift_bitmap(shift, ctrl, alt, consumed_alt);
  7039. if (bitmap)
  7040. p += sprintf(p, "\x1B[%d;%d~", code, bitmap);
  7041. else
  7042. p += sprintf(p, "\x1B[%d~", code);
  7043. }
  7044. return p - buf;
  7045. }
  7046. int format_small_keypad_key(char *buf, Terminal *term, SmallKeypadKey key,
  7047. bool shift, bool ctrl, bool alt,
  7048. bool *consumed_alt)
  7049. {
  7050. char *p = buf;
  7051. int code;
  7052. switch (key) {
  7053. case SKK_HOME: code = 1; break;
  7054. case SKK_INSERT: code = 2; break;
  7055. case SKK_DELETE: code = 3; break;
  7056. case SKK_END: code = 4; break;
  7057. case SKK_PGUP: code = 5; break;
  7058. case SKK_PGDN: code = 6; break;
  7059. default: unreachable("bad small keypad key enum value");
  7060. }
  7061. /* Reorder edit keys to physical order */
  7062. if (term->funky_type == FUNKY_VT400 && code <= 6)
  7063. code = "\0\2\1\4\5\3\6"[code];
  7064. if (term->vt52_mode && code > 0 && code <= 6) {
  7065. p += sprintf(p, "\x1B%c", " HLMEIG"[code]);
  7066. } else if (term->funky_type == FUNKY_SCO) {
  7067. static const char codes[] = "HL.FIG";
  7068. if (code == 3) {
  7069. *p++ = '\x7F';
  7070. } else {
  7071. p += sprintf(p, "\x1B[%c", codes[code-1]);
  7072. }
  7073. } else if ((code == 1 || code == 4) && term->rxvt_homeend) {
  7074. p += sprintf(p, code == 1 ? "\x1B[H" : "\x1BOw");
  7075. } else {
  7076. if (term->vt52_mode) {
  7077. p += sprintf(p, "\x1B[%d~", code);
  7078. } else {
  7079. int bitmap = 0;
  7080. if (term->funky_type == FUNKY_XTERM_216)
  7081. bitmap = shift_bitmap(shift, ctrl, alt, consumed_alt);
  7082. if (bitmap)
  7083. p += sprintf(p, "\x1B[%d;%d~", code, bitmap);
  7084. else
  7085. p += sprintf(p, "\x1B[%d~", code);
  7086. }
  7087. }
  7088. return p - buf;
  7089. }
  7090. int format_numeric_keypad_key(char *buf, Terminal *term, char key,
  7091. bool shift, bool ctrl)
  7092. {
  7093. char *p = buf;
  7094. bool app_keypad = (term->app_keypad_keys && !term->no_applic_k);
  7095. if (term->nethack_keypad && (key >= '1' && key <= '9')) {
  7096. static const char nh_base[] = "bjnh.lyku";
  7097. char c = nh_base[key - '1'];
  7098. if (ctrl && c != '.')
  7099. c &= 0x1F;
  7100. else if (shift && c != '.')
  7101. c += 'A'-'a';
  7102. *p++ = c;
  7103. } else {
  7104. int xkey = 0;
  7105. if (term->funky_type == FUNKY_VT400 ||
  7106. (term->funky_type <= FUNKY_LINUX && app_keypad)) {
  7107. switch (key) {
  7108. case 'G': xkey = 'P'; break;
  7109. case '/': xkey = 'Q'; break;
  7110. case '*': xkey = 'R'; break;
  7111. case '-': xkey = 'S'; break;
  7112. }
  7113. }
  7114. if (app_keypad) {
  7115. switch (key) {
  7116. case '0': xkey = 'p'; break;
  7117. case '1': xkey = 'q'; break;
  7118. case '2': xkey = 'r'; break;
  7119. case '3': xkey = 's'; break;
  7120. case '4': xkey = 't'; break;
  7121. case '5': xkey = 'u'; break;
  7122. case '6': xkey = 'v'; break;
  7123. case '7': xkey = 'w'; break;
  7124. case '8': xkey = 'x'; break;
  7125. case '9': xkey = 'y'; break;
  7126. case '.': xkey = 'n'; break;
  7127. case '\r': xkey = 'M'; break;
  7128. case '+':
  7129. /*
  7130. * Keypad + is tricky. It covers a space that would
  7131. * be taken up on the VT100 by _two_ keys; so we
  7132. * let Shift select between the two. Worse still,
  7133. * in xterm function key mode we change which two...
  7134. */
  7135. if (term->funky_type == FUNKY_XTERM)
  7136. xkey = shift ? 'l' : 'k';
  7137. else
  7138. xkey = shift ? 'm' : 'l';
  7139. break;
  7140. case '/':
  7141. if (term->funky_type == FUNKY_XTERM)
  7142. xkey = 'o';
  7143. break;
  7144. case '*':
  7145. if (term->funky_type == FUNKY_XTERM)
  7146. xkey = 'j';
  7147. break;
  7148. case '-':
  7149. if (term->funky_type == FUNKY_XTERM)
  7150. xkey = 'm';
  7151. break;
  7152. }
  7153. }
  7154. if (xkey) {
  7155. if (term->vt52_mode) {
  7156. if (xkey >= 'P' && xkey <= 'S')
  7157. p += sprintf(p, "\x1B%c", xkey);
  7158. else
  7159. p += sprintf(p, "\x1B?%c", xkey);
  7160. } else
  7161. p += sprintf(p, "\x1BO%c", xkey);
  7162. }
  7163. }
  7164. return p - buf;
  7165. }
  7166. void term_keyinputw(Terminal *term, const wchar_t *widebuf, int len)
  7167. {
  7168. strbuf *buf = term_input_data_from_unicode(term, widebuf, len);
  7169. if (buf->len)
  7170. term_keyinput_internal(term, buf->s, buf->len, true);
  7171. strbuf_free(buf);
  7172. }
  7173. void term_keyinput(Terminal *term, int codepage, const void *str, int len)
  7174. {
  7175. if (codepage < 0 || codepage == term->ucsdata->line_codepage) {
  7176. /*
  7177. * This text needs no translation, either because it's already
  7178. * in the right character set, or because we got the special
  7179. * codepage value -1 from our caller which means 'this data
  7180. * should be charset-agnostic, just send it raw' (for really
  7181. * simple things like control characters).
  7182. */
  7183. term_keyinput_internal(term, str, len, true);
  7184. } else {
  7185. strbuf *buf = term_input_data_from_charset(term, codepage, str, len);
  7186. if (buf->len)
  7187. term_keyinput_internal(term, buf->s, buf->len, true);
  7188. strbuf_free(buf);
  7189. }
  7190. }
  7191. void term_nopaste(Terminal *term)
  7192. {
  7193. if (term->paste_len == 0)
  7194. return;
  7195. sfree(term->paste_buffer);
  7196. term_bracketed_paste_stop(term);
  7197. term->paste_buffer = NULL;
  7198. term->paste_len = 0;
  7199. }
  7200. static void deselect(Terminal *term)
  7201. {
  7202. term->selstate = NO_SELECTION;
  7203. term->selstart.x = term->selstart.y = term->selend.x = term->selend.y = 0;
  7204. }
  7205. void term_lost_clipboard_ownership(Terminal *term, int clipboard)
  7206. {
  7207. if (!(term->n_mouse_select_clipboards > 1 &&
  7208. clipboard == term->mouse_select_clipboards[1]))
  7209. return;
  7210. deselect(term);
  7211. term_update(term);
  7212. /*
  7213. * Since terminal output is suppressed during drag-selects, we
  7214. * should make sure to write any pending output if one has just
  7215. * finished.
  7216. */
  7217. term_out(term, false);
  7218. }
  7219. static void term_added_data(Terminal *term, bool called_from_term_data)
  7220. {
  7221. if (!term->in_term_out) {
  7222. term->in_term_out = true;
  7223. term_out(term, called_from_term_data);
  7224. term->in_term_out = false;
  7225. }
  7226. }
  7227. size_t term_data(Terminal *term, const void *data, size_t len)
  7228. {
  7229. bufchain_add(&term->inbuf, data, len);
  7230. term_added_data(term, true);
  7231. return bufchain_size(&term->inbuf);
  7232. }
  7233. void term_provide_logctx(Terminal *term, LogContext *logctx)
  7234. {
  7235. term->logctx = logctx;
  7236. }
  7237. void term_set_focus(Terminal *term, bool has_focus)
  7238. {
  7239. term->has_focus = has_focus;
  7240. term_schedule_cblink(term);
  7241. }
  7242. /*
  7243. * Provide "auto" settings for remote tty modes, suitable for an
  7244. * application with a terminal window.
  7245. */
  7246. char *term_get_ttymode(Terminal *term, const char *mode)
  7247. {
  7248. const char *val = NULL;
  7249. if (strcmp(mode, "ERASE") == 0) {
  7250. val = term->bksp_is_delete ? "^?" : "^H";
  7251. } else if (strcmp(mode, "IUTF8") == 0) {
  7252. val = (term->ucsdata->line_codepage == CP_UTF8) ? "yes" : "no";
  7253. }
  7254. /* FIXME: perhaps we should set ONLCR based on lfhascr as well? */
  7255. /* FIXME: or ECHO and friends based on local echo state? */
  7256. return dupstr(val);
  7257. }
  7258. struct term_userpass_state {
  7259. prompts_t *prompts;
  7260. size_t curr_prompt;
  7261. enum TermUserpassPromptState {
  7262. TUS_INITIAL, /* haven't even printed the prompt yet */
  7263. TUS_ACTIVE, /* prompt is currently receiving user input */
  7264. TUS_ABORTED, /* user pressed ^C or ^D to cancel prompt */
  7265. } prompt_state;
  7266. Terminal *term;
  7267. TermLineEditor *le;
  7268. TermLineEditorCallbackReceiver le_rcv;
  7269. };
  7270. static void term_userpass_next_prompt(struct term_userpass_state *s);
  7271. /*
  7272. * Signal that a prompts_t is done. This involves sending a
  7273. * notification to the caller, and also turning off our own callback
  7274. * that listens for more data arriving in the ldisc's input queue.
  7275. */
  7276. static inline SeatPromptResult signal_prompts_t(Terminal *term, prompts_t *p,
  7277. SeatPromptResult spr)
  7278. {
  7279. assert(p->callback && "Asynchronous userpass input requires a callback");
  7280. queue_toplevel_callback(p->callback, p->callback_ctx);
  7281. if (term->ldisc)
  7282. ldisc_provide_userpass_le(term->ldisc, NULL);
  7283. p->spr = spr;
  7284. if (p->data) {
  7285. term_userpass_state_free(p->data);
  7286. p->data = NULL;
  7287. }
  7288. return spr;
  7289. }
  7290. /* Tiny wrapper to make it easier to write lots of little strings */
  7291. static inline void term_write(Terminal *term, ptrlen data)
  7292. {
  7293. term_data(term, data.ptr, data.len);
  7294. }
  7295. static void term_lineedit_to_terminal(
  7296. TermLineEditorCallbackReceiver *rcv, ptrlen data)
  7297. {
  7298. struct term_userpass_state *s = container_of(
  7299. rcv, struct term_userpass_state, le_rcv);
  7300. prompt_t *pr = s->prompts->prompts[s->curr_prompt];
  7301. if (pr->echo)
  7302. term_write(s->term, data);
  7303. }
  7304. static void term_lineedit_to_backend(
  7305. TermLineEditorCallbackReceiver *rcv, ptrlen data)
  7306. {
  7307. struct term_userpass_state *s = container_of(
  7308. rcv, struct term_userpass_state, le_rcv);
  7309. prompt_t *pr = s->prompts->prompts[s->curr_prompt];
  7310. put_datapl(pr->result, data);
  7311. }
  7312. static void term_lineedit_newline(TermLineEditorCallbackReceiver *rcv)
  7313. {
  7314. struct term_userpass_state *s = container_of(
  7315. rcv, struct term_userpass_state, le_rcv);
  7316. prompt_t *pr = s->prompts->prompts[s->curr_prompt];
  7317. if (!pr->echo) {
  7318. /* If echo is disabled, we won't have printed the newline in
  7319. * term_lineedit_to_terminal, so print it now */
  7320. term_write(s->term, PTRLEN_LITERAL("\x0D\x0A"));
  7321. }
  7322. ldisc_provide_userpass_le(s->term->ldisc, NULL);
  7323. s->curr_prompt++;
  7324. s->prompt_state = TUS_INITIAL;
  7325. term_userpass_next_prompt(s);
  7326. }
  7327. static void term_lineedit_special(
  7328. TermLineEditorCallbackReceiver *rcv, SessionSpecialCode code, int arg)
  7329. {
  7330. struct term_userpass_state *s = container_of(
  7331. rcv, struct term_userpass_state, le_rcv);
  7332. switch (code) {
  7333. case SS_IP:
  7334. case SS_EOF:
  7335. ldisc_provide_userpass_le(s->term->ldisc, NULL);
  7336. s->prompt_state = TUS_ABORTED;
  7337. signal_prompts_t(s->term, s->prompts, SPR_USER_ABORT);
  7338. default:
  7339. break;
  7340. }
  7341. }
  7342. static const TermLineEditorCallbackReceiverVtable
  7343. term_userpass_lineedit_receiver_vt = {
  7344. .to_terminal = term_lineedit_to_terminal,
  7345. .to_backend = term_lineedit_to_backend,
  7346. .special = term_lineedit_special,
  7347. .newline = term_lineedit_newline,
  7348. };
  7349. static struct term_userpass_state *term_userpass_state_new(
  7350. Terminal *term, prompts_t *prompts)
  7351. {
  7352. struct term_userpass_state *s = snew(struct term_userpass_state);
  7353. s->prompts = prompts;
  7354. s->curr_prompt = 0;
  7355. s->prompt_state = TUS_INITIAL;
  7356. s->term = term;
  7357. s->le_rcv.vt = &term_userpass_lineedit_receiver_vt;
  7358. s->le = lineedit_new(term, LE_INTERRUPT | LE_EOF_ALWAYS | LE_ESC_ERASES,
  7359. &s->le_rcv);
  7360. assert(!term->userpass_state);
  7361. term->userpass_state = s;
  7362. return s;
  7363. }
  7364. static void term_userpass_state_free(struct term_userpass_state *s)
  7365. {
  7366. assert(s->term->userpass_state == s);
  7367. s->term->userpass_state = NULL;
  7368. lineedit_free(s->le);
  7369. sfree(s);
  7370. }
  7371. static void term_userpass_next_prompt(struct term_userpass_state *s)
  7372. {
  7373. if (s->prompt_state != TUS_INITIAL)
  7374. return;
  7375. if (s->curr_prompt < s->prompts->n_prompts) {
  7376. prompt_t *pr = s->prompts->prompts[s->curr_prompt];
  7377. term_write(s->term, ptrlen_from_asciz(pr->prompt));
  7378. s->prompt_state = TUS_ACTIVE;
  7379. ldisc_provide_userpass_le(s->term->ldisc, s->le);
  7380. } else {
  7381. /* This triggers the callback provided by the userpass client,
  7382. * which will call term_userpass_state to fetch the result
  7383. * we're storing here */
  7384. signal_prompts_t(s->term, s->prompts, SPR_OK);
  7385. }
  7386. }
  7387. static bool terminal_use_utf8 = true;
  7388. bool set_legacy_charset_handling(bool newvalue)
  7389. {
  7390. terminal_use_utf8 = !newvalue;
  7391. return true;
  7392. }
  7393. /*
  7394. * Process some terminal data in the course of username/password
  7395. * input.
  7396. */
  7397. SeatPromptResult term_get_userpass_input(Terminal *term, prompts_t *p)
  7398. {
  7399. if (!term->ldisc) {
  7400. /* Can't handle interactive prompts without an ldisc */
  7401. return signal_prompts_t(term, p, SPR_SW_ABORT(
  7402. "Terminal not prepared for interactive prompts"));
  7403. }
  7404. if (p->spr.kind != SPRK_INCOMPLETE) {
  7405. /* We've already finished these prompts, so return the same
  7406. * result again */
  7407. return p->spr;
  7408. }
  7409. struct term_userpass_state *s = (struct term_userpass_state *)p->data;
  7410. if (!s) {
  7411. /*
  7412. * First call. Set some stuff up.
  7413. */
  7414. p->data = s = term_userpass_state_new(term, p);
  7415. p->spr = SPR_INCOMPLETE;
  7416. term->userpass_utf8_override = p->utf8 && terminal_use_utf8;
  7417. /* We only print the `name' caption if we have to... */
  7418. if (p->name_reqd && p->name) {
  7419. ptrlen plname = ptrlen_from_asciz(p->name);
  7420. term_write(term, plname);
  7421. if (!ptrlen_endswith(plname, PTRLEN_LITERAL("\n"), NULL))
  7422. term_write(term, PTRLEN_LITERAL("\r\n"));
  7423. }
  7424. /* ...but we always print any `instruction'. */
  7425. if (p->instruction) {
  7426. ptrlen plinst = ptrlen_from_asciz(p->instruction);
  7427. term_write(term, plinst);
  7428. if (!ptrlen_endswith(plinst, PTRLEN_LITERAL("\n"), NULL))
  7429. term_write(term, PTRLEN_LITERAL("\r\n"));
  7430. }
  7431. /*
  7432. * Zero all the results, in case we abort half-way through.
  7433. */
  7434. {
  7435. int i;
  7436. for (i = 0; i < (int)p->n_prompts; i++)
  7437. prompt_set_result(p->prompts[i], "");
  7438. }
  7439. /* And print the first prompt. */
  7440. term_userpass_next_prompt(s);
  7441. }
  7442. return SPR_INCOMPLETE;
  7443. }
  7444. void term_notify_minimised(Terminal *term, bool minimised)
  7445. {
  7446. term->minimised = minimised;
  7447. }
  7448. void term_notify_palette_changed(Terminal *term)
  7449. {
  7450. palette_reset(term, true);
  7451. }
  7452. void term_notify_window_pos(Terminal *term, int x, int y)
  7453. {
  7454. term->winpos_x = x;
  7455. term->winpos_y = y;
  7456. }
  7457. void term_notify_window_size_pixels(Terminal *term, int x, int y)
  7458. {
  7459. term->winpixsize_x = x;
  7460. term->winpixsize_y = y;
  7461. }