terminal.c 296 KB

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