terminal.c 294 KB

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